refactor: 大規模ファイル分割とプラグインリファクタリング
## 🎯 プラグイン整理 - ✅ **nyash-json-plugin**: プロバイダー抽象化、NodeRep統一 - ✅ **nyash-string-plugin**: TLVヘルパー整理 - ✅ **nyash-net-plugin**: HTTPヘルパー分離、ソケット管理改善 - ✅ **nyash-counter-plugin/fixture-plugin**: 基本構造整理 ## 📂 mir_interpreter分割 - ✅ **mir_interpreter.rs → mir_interpreter/ディレクトリ** - mod.rs: メイン構造体定義 - execution.rs: 実行エンジン - memory.rs: メモリ管理 - instructions/: 命令別実装 ## 🔧 その他の改善 - テストファイル群の最適化 - LLVMコンパイラのメイン関数整理 - 不要なインポート削除 1000行超のファイルを適切なモジュール構造に分割完了! 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -72,7 +72,12 @@ fn main() -> Result<()> {
|
|||||||
run_harness_dummy(&harness_path, &obj_path)
|
run_harness_dummy(&harness_path, &obj_path)
|
||||||
.with_context(|| "failed to run harness in dummy mode")?;
|
.with_context(|| "failed to run harness in dummy mode")?;
|
||||||
if emit_exe {
|
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());
|
println!("[ny-llvmc] executable written: {}", args.out.display());
|
||||||
} else {
|
} else {
|
||||||
println!("[ny-llvmc] dummy object written: {}", obj_path.display());
|
println!("[ny-llvmc] dummy object written: {}", obj_path.display());
|
||||||
@ -119,7 +124,12 @@ fn main() -> Result<()> {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
if emit_exe {
|
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());
|
println!("[ny-llvmc] executable written: {}", args.out.display());
|
||||||
} else {
|
} else {
|
||||||
println!("[ny-llvmc] object written: {}", obj_path.display());
|
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
|
// Resolve nyRT static lib
|
||||||
let nyrt_dir = if let Some(dir) = nyrt_dir_opt {
|
let nyrt_dir = if let Some(dir) = nyrt_dir_opt {
|
||||||
dir.clone()
|
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
|
// try target/release then crates/nyash_kernel/target/release
|
||||||
let a = PathBuf::from("target/release");
|
let a = PathBuf::from("target/release");
|
||||||
let b = PathBuf::from("crates/nyash_kernel/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");
|
let libnyrt = nyrt_dir.join("libnyash_kernel.a");
|
||||||
if !libnyrt.exists() {
|
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
|
// 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);
|
let mut cmd = Command::new(linker);
|
||||||
cmd.arg("-o").arg(out_exe);
|
cmd.arg("-o").arg(out_exe);
|
||||||
cmd.arg(obj);
|
cmd.arg(obj);
|
||||||
// Whole-archive libnyash_kernel to ensure all objects are linked
|
// 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
|
// Common libs on Linux
|
||||||
cmd.arg("-ldl").arg("-lpthread").arg("-lm");
|
cmd.arg("-ldl").arg("-lpthread").arg("-lm");
|
||||||
if let Some(extras) = extra_libs {
|
if let Some(extras) = extra_libs {
|
||||||
|
|||||||
@ -143,7 +143,9 @@ unsafe impl Sync for NyashTypeBoxFfi {}
|
|||||||
|
|
||||||
extern "C" fn counter_resolve(name: *const std::os::raw::c_char) -> u32 {
|
extern "C" fn counter_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
if name.is_null() { return 0; }
|
if name.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let s = std::ffi::CStr::from_ptr(name).to_string_lossy();
|
let s = std::ffi::CStr::from_ptr(name).to_string_lossy();
|
||||||
match s.as_ref() {
|
match s.as_ref() {
|
||||||
"birth" => METHOD_BIRTH,
|
"birth" => METHOD_BIRTH,
|
||||||
@ -167,34 +169,53 @@ extern "C" fn counter_invoke(
|
|||||||
match method_id {
|
match method_id {
|
||||||
METHOD_BIRTH => {
|
METHOD_BIRTH => {
|
||||||
// Return new instance handle (u32 id) as raw 4 bytes (not TLV)
|
// Return new instance handle (u32 id) as raw 4 bytes (not TLV)
|
||||||
if result_len.is_null() { return NYB_E_INVALID_ARGS; }
|
if result_len.is_null() {
|
||||||
if preflight(result, result_len, 4) { return NYB_E_SHORT_BUFFER; }
|
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);
|
let id = INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||||
if let Ok(mut map) = INSTANCES.lock() {
|
if let Ok(mut map) = INSTANCES.lock() {
|
||||||
map.insert(id, CounterInstance { count: 0 });
|
map.insert(id, CounterInstance { count: 0 });
|
||||||
} else { return NYB_E_PLUGIN_ERROR; }
|
} else {
|
||||||
|
return NYB_E_PLUGIN_ERROR;
|
||||||
|
}
|
||||||
let bytes = id.to_le_bytes();
|
let bytes = id.to_le_bytes();
|
||||||
std::ptr::copy_nonoverlapping(bytes.as_ptr(), result, 4);
|
std::ptr::copy_nonoverlapping(bytes.as_ptr(), result, 4);
|
||||||
*result_len = 4;
|
*result_len = 4;
|
||||||
NYB_SUCCESS
|
NYB_SUCCESS
|
||||||
}
|
}
|
||||||
METHOD_FINI => {
|
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 => {
|
METHOD_INC => {
|
||||||
if let Ok(mut map) = INSTANCES.lock() {
|
if let Ok(mut map) = INSTANCES.lock() {
|
||||||
if let Some(inst) = map.get_mut(&instance_id) {
|
if let Some(inst) = map.get_mut(&instance_id) {
|
||||||
inst.count += 1;
|
inst.count += 1;
|
||||||
return write_tlv_i32(inst.count, result, result_len);
|
return write_tlv_i32(inst.count, result, result_len);
|
||||||
} else { return NYB_E_INVALID_HANDLE; }
|
} else {
|
||||||
} else { return NYB_E_PLUGIN_ERROR; }
|
return NYB_E_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return NYB_E_PLUGIN_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
METHOD_GET => {
|
METHOD_GET => {
|
||||||
if let Ok(map) = INSTANCES.lock() {
|
if let Ok(map) = INSTANCES.lock() {
|
||||||
if let Some(inst) = map.get(&instance_id) {
|
if let Some(inst) = map.get(&instance_id) {
|
||||||
return write_tlv_i32(inst.count, result, result_len);
|
return write_tlv_i32(inst.count, result, result_len);
|
||||||
} else { return NYB_E_INVALID_HANDLE; }
|
} else {
|
||||||
} else { return NYB_E_PLUGIN_ERROR; }
|
return NYB_E_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return NYB_E_PLUGIN_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => NYB_E_INVALID_METHOD,
|
_ => NYB_E_INVALID_METHOD,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{atomic::{AtomicU32, Ordering}, Mutex};
|
use std::sync::{
|
||||||
|
atomic::{AtomicU32, Ordering},
|
||||||
|
Mutex,
|
||||||
|
};
|
||||||
|
|
||||||
// ===== Error Codes (BID-1 alignment) =====
|
// ===== Error Codes (BID-1 alignment) =====
|
||||||
const NYB_SUCCESS: i32 = 0;
|
const NYB_SUCCESS: i32 = 0;
|
||||||
@ -42,7 +45,9 @@ pub extern "C" fn nyash_plugin_invoke(
|
|||||||
result: *mut u8,
|
result: *mut u8,
|
||||||
result_len: *mut usize,
|
result_len: *mut usize,
|
||||||
) -> i32 {
|
) -> 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) }
|
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 {
|
extern "C" fn fixture_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
if name.is_null() { return 0; }
|
if name.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let s = std::ffi::CStr::from_ptr(name).to_string_lossy();
|
let s = std::ffi::CStr::from_ptr(name).to_string_lossy();
|
||||||
match s.as_ref() {
|
match s.as_ref() {
|
||||||
"birth" => METHOD_BIRTH,
|
"birth" => METHOD_BIRTH,
|
||||||
@ -125,12 +132,18 @@ unsafe fn dispatch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn birth(result: *mut u8, result_len: *mut usize) -> i32 {
|
unsafe fn birth(result: *mut u8, result_len: *mut usize) -> i32 {
|
||||||
if result_len.is_null() { return NYB_E_INVALID_ARGS; }
|
if result_len.is_null() {
|
||||||
if preflight(result, result_len, 4) { return NYB_E_SHORT_BUFFER; }
|
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);
|
let id = INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||||
if let Ok(mut map) = INSTANCES.lock() {
|
if let Ok(mut map) = INSTANCES.lock() {
|
||||||
map.insert(id, FixtureInstance { alive: true });
|
map.insert(id, FixtureInstance { alive: true });
|
||||||
} else { return NYB_E_PLUGIN_ERROR; }
|
} else {
|
||||||
|
return NYB_E_PLUGIN_ERROR;
|
||||||
|
}
|
||||||
let bytes = id.to_le_bytes();
|
let bytes = id.to_le_bytes();
|
||||||
std::ptr::copy_nonoverlapping(bytes.as_ptr(), result, 4);
|
std::ptr::copy_nonoverlapping(bytes.as_ptr(), result, 4);
|
||||||
*result_len = 4;
|
*result_len = 4;
|
||||||
@ -141,33 +154,43 @@ unsafe fn fini(instance_id: u32) -> i32 {
|
|||||||
if let Ok(mut map) = INSTANCES.lock() {
|
if let Ok(mut map) = INSTANCES.lock() {
|
||||||
map.remove(&instance_id);
|
map.remove(&instance_id);
|
||||||
NYB_SUCCESS
|
NYB_SUCCESS
|
||||||
} else { NYB_E_PLUGIN_ERROR }
|
} else {
|
||||||
|
NYB_E_PLUGIN_ERROR
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn echo(
|
unsafe fn echo(args: *const u8, args_len: usize, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||||
args: *const u8,
|
|
||||||
args_len: usize,
|
|
||||||
result: *mut u8,
|
|
||||||
result_len: *mut usize,
|
|
||||||
) -> i32 {
|
|
||||||
// Expect TLV with 1 argument: tag=6 (String)
|
// 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);
|
let slice = std::slice::from_raw_parts(args, args_len);
|
||||||
// Minimal TLV parse: skip header (ver/argc) and verify first entry is String
|
// 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 */ }
|
if slice[0] != 1 || slice[1] != 0 { /* ver=1 little endian */ }
|
||||||
// position 4.. is first entry; [tag, rsv, sz_lo, sz_hi, payload...]
|
// position 4.. is first entry; [tag, rsv, sz_lo, sz_hi, payload...]
|
||||||
let tag = slice[4];
|
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;
|
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 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)
|
write_tlv_str(s, result, result_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_tlv_result(payloads: &[(u8, &[u8])], result: *mut u8, result_len: *mut usize) -> i32 {
|
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::<usize>();
|
let needed = 4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::<usize>();
|
||||||
let mut buf: Vec<u8> = Vec::with_capacity(needed);
|
let mut buf: Vec<u8> = Vec::with_capacity(needed);
|
||||||
buf.extend_from_slice(&1u16.to_le_bytes()); // ver
|
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 {
|
fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
if result_len.is_null() { return false; }
|
if result_len.is_null() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if result.is_null() || *result_len < needed {
|
if result.is_null() || *result_len < needed {
|
||||||
*result_len = needed;
|
*result_len = needed;
|
||||||
return true;
|
return true;
|
||||||
@ -203,4 +228,3 @@ fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
|||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -150,7 +150,14 @@ extern "C" fn jsondoc_invoke_id(
|
|||||||
JD_BIRTH => {
|
JD_BIRTH => {
|
||||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
if let Ok(mut m) = DOCS.lock() {
|
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 {
|
} else {
|
||||||
return E_PLUGIN;
|
return E_PLUGIN;
|
||||||
}
|
}
|
||||||
@ -166,19 +173,32 @@ extern "C" fn jsondoc_invoke_id(
|
|||||||
match provider_kind() {
|
match provider_kind() {
|
||||||
ProviderKind::Serde => {
|
ProviderKind::Serde => {
|
||||||
match provider_parse(&text) {
|
match provider_parse(&text) {
|
||||||
Ok(v) => { doc.root = Some(Arc::new(v)); doc.doc_ptr = None; doc.last_err = None; }
|
Ok(v) => {
|
||||||
Err(e) => { doc.root = None; doc.doc_ptr = None; doc.last_err = Some(e.to_string()); }
|
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);
|
return write_tlv_void(result, result_len);
|
||||||
}
|
}
|
||||||
ProviderKind::Yyjson => {
|
ProviderKind::Yyjson => {
|
||||||
let c = CString::new(text.as_bytes()).unwrap_or_default();
|
let c = CString::new(text.as_bytes()).unwrap_or_default();
|
||||||
let mut ec: i32 = -1;
|
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() {
|
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 {
|
} 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);
|
return write_tlv_void(result, result_len);
|
||||||
}
|
}
|
||||||
@ -197,8 +217,15 @@ extern "C" fn jsondoc_invoke_id(
|
|||||||
ProviderKind::Serde => {
|
ProviderKind::Serde => {
|
||||||
if let Some(root_arc) = doc.root.as_ref().map(|r| Arc::clone(r)) {
|
if let Some(root_arc) = doc.root.as_ref().map(|r| Arc::clone(r)) {
|
||||||
let node_id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
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)); }
|
if let Ok(mut nn) = NODES.lock() {
|
||||||
return write_tlv_handle(T_JSON_NODE, node_id, result, result_len);
|
nn.insert(node_id, NodeRep::Serde(root_arc));
|
||||||
|
}
|
||||||
|
return write_tlv_handle(
|
||||||
|
T_JSON_NODE,
|
||||||
|
node_id,
|
||||||
|
result,
|
||||||
|
result_len,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return E_PLUGIN;
|
return E_PLUGIN;
|
||||||
}
|
}
|
||||||
@ -206,8 +233,21 @@ extern "C" fn jsondoc_invoke_id(
|
|||||||
if let Some(dp) = doc.doc_ptr {
|
if let Some(dp) = doc.doc_ptr {
|
||||||
let vp = nyjson_doc_root(dp as *mut c_void);
|
let vp = nyjson_doc_root(dp as *mut c_void);
|
||||||
let node_id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
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 }); }
|
if let Ok(mut nn) = NODES.lock() {
|
||||||
return write_tlv_handle(T_JSON_NODE, node_id, result, result_len);
|
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;
|
return E_PLUGIN;
|
||||||
}
|
}
|
||||||
@ -231,7 +271,9 @@ extern "C" fn jsondoc_invoke_id(
|
|||||||
JD_FINI => {
|
JD_FINI => {
|
||||||
if let Ok(mut m) = DOCS.lock() {
|
if let Ok(mut m) = DOCS.lock() {
|
||||||
if let Some(mut di) = m.remove(&instance_id) {
|
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);
|
return write_tlv_void(result, result_len);
|
||||||
@ -281,146 +323,330 @@ extern "C" fn jsonnode_invoke_id(
|
|||||||
) -> i32 {
|
) -> i32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let node_rep = match NODES.lock() {
|
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,
|
Err(_) => return E_PLUGIN,
|
||||||
};
|
};
|
||||||
match method_id {
|
match method_id {
|
||||||
JN_BIRTH => {
|
JN_BIRTH => {
|
||||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
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);
|
return write_u32(id, result, result_len);
|
||||||
}
|
}
|
||||||
JN_KIND => {
|
JN_KIND => match provider_kind() {
|
||||||
match provider_kind() {
|
|
||||||
ProviderKind::Serde => {
|
ProviderKind::Serde => {
|
||||||
let k = match node_rep { NodeRep::Serde(ref a) => match &**a {
|
let k = match node_rep {
|
||||||
|
NodeRep::Serde(ref a) => match &**a {
|
||||||
Value::Null => "null",
|
Value::Null => "null",
|
||||||
Value::Bool(_) => "bool",
|
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::String(_) => "string",
|
||||||
Value::Array(_) => "array",
|
Value::Array(_) => "array",
|
||||||
Value::Object(_) => "object",
|
Value::Object(_) => "object",
|
||||||
}, _ => "null"};
|
},
|
||||||
|
_ => "null",
|
||||||
|
};
|
||||||
write_tlv_string(k, result, result_len)
|
write_tlv_string(k, result, result_len)
|
||||||
}
|
}
|
||||||
ProviderKind::Yyjson => {
|
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 {
|
||||||
let k = if v.is_null() { "null" }
|
ptr as *mut c_void
|
||||||
else if nyjson_is_obj(v) != 0 { "object" }
|
} else {
|
||||||
else if nyjson_is_arr(v) != 0 { "array" }
|
std::ptr::null_mut()
|
||||||
else if nyjson_is_str(v) != 0 { "string" }
|
};
|
||||||
else if nyjson_is_int(v) != 0 { "int" }
|
let k = if v.is_null() {
|
||||||
else if nyjson_is_real(v) != 0 { "real" }
|
"null"
|
||||||
else if nyjson_is_bool(v) != 0 { "bool" }
|
} else if nyjson_is_obj(v) != 0 {
|
||||||
else { "null" };
|
"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)
|
write_tlv_string(k, result, result_len)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
JN_GET => {
|
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() {
|
match provider_kind() {
|
||||||
ProviderKind::Serde => {
|
ProviderKind::Serde => {
|
||||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
if let NodeRep::Serde(ref a) = node_rep {
|
if let NodeRep::Serde(ref a) = node_rep {
|
||||||
if let Value::Object(map) = &**a {
|
if let Value::Object(map) = &**a {
|
||||||
if let Some(child) = map.get(&key) {
|
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);
|
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)
|
write_tlv_handle(T_JSON_NODE, id, result, result_len)
|
||||||
}
|
}
|
||||||
ProviderKind::Yyjson => {
|
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 id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
let mut out_ptr: *mut c_void = std::ptr::null_mut();
|
let mut out_ptr: *mut c_void = std::ptr::null_mut();
|
||||||
if !v.is_null() && nyjson_is_obj(v) != 0 {
|
if !v.is_null() && nyjson_is_obj(v) != 0 {
|
||||||
let c = CString::new(key).unwrap_or_default();
|
let c = CString::new(key).unwrap_or_default();
|
||||||
out_ptr = nyjson_obj_get_key(v, c.as_ptr());
|
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() {
|
let rep = if out_ptr.is_null() {
|
||||||
NodeRep::Yy { doc_id, ptr: 0 }
|
NodeRep::Yy { doc_id, ptr: 0 }
|
||||||
} else {
|
} 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)
|
write_tlv_handle(T_JSON_NODE, id, result, result_len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JN_SIZE => {
|
JN_SIZE => match provider_kind() {
|
||||||
match provider_kind() {
|
|
||||||
ProviderKind::Serde => {
|
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 };
|
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)
|
write_tlv_i64(n, result, result_len)
|
||||||
}
|
}
|
||||||
ProviderKind::Yyjson => {
|
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 {
|
||||||
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 };
|
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)
|
write_tlv_i64(n, result, result_len)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
JN_AT => {
|
JN_AT => {
|
||||||
let idx = match read_arg_i64(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
let idx = match read_arg_i64(args, args_len, 0) {
|
||||||
if idx < 0 { return E_ARGS; }
|
Some(v) => v,
|
||||||
|
None => return E_ARGS,
|
||||||
|
};
|
||||||
|
if idx < 0 {
|
||||||
|
return E_ARGS;
|
||||||
|
}
|
||||||
match provider_kind() {
|
match provider_kind() {
|
||||||
ProviderKind::Serde => {
|
ProviderKind::Serde => {
|
||||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
if let NodeRep::Serde(ref a) = node_rep {
|
if let NodeRep::Serde(ref a) = node_rep {
|
||||||
if let Value::Array(arr) = &**a {
|
if let Value::Array(arr) = &**a {
|
||||||
let i = idx as usize;
|
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)
|
write_tlv_handle(T_JSON_NODE, id, result, result_len)
|
||||||
}
|
}
|
||||||
ProviderKind::Yyjson => {
|
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 id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
let mut child: *mut c_void = std::ptr::null_mut();
|
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); }
|
if !v.is_null() && nyjson_is_arr(v) != 0 {
|
||||||
let doc_id = if let NodeRep::Yy { doc_id, .. } = node_rep { doc_id } else { 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() {
|
let rep = if child.is_null() {
|
||||||
NodeRep::Yy { doc_id, ptr: 0 }
|
NodeRep::Yy { doc_id, ptr: 0 }
|
||||||
} else {
|
} 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)
|
write_tlv_handle(T_JSON_NODE, id, result, result_len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JN_STR => match provider_kind() {
|
JN_STR => match provider_kind() {
|
||||||
ProviderKind::Serde => {
|
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 => {
|
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 {
|
||||||
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) } }
|
ptr as *mut c_void
|
||||||
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 {
|
||||||
else { write_tlv_string("", result, result_len) }
|
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() {
|
JN_INT => match provider_kind() {
|
||||||
ProviderKind::Serde => {
|
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 => {
|
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 {
|
||||||
if !v.is_null() && nyjson_is_int(v) != 0 { write_tlv_i64(nyjson_get_sint_val(v) as i64, result, result_len) }
|
ptr as *mut c_void
|
||||||
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 {
|
||||||
else { write_tlv_i64(0, result, result_len) }
|
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() {
|
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::Serde => {
|
||||||
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) } }
|
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,
|
_ => E_METHOD,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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_RECV: u32 = 2; // -> bytes
|
||||||
pub(crate) const M_CONN_CLOSE: u32 = 3; // -> void
|
pub(crate) const M_CONN_CLOSE: u32 = 3; // -> void
|
||||||
pub(crate) const M_CONN_RECV_TIMEOUT: u32 = 4; // ms -> bytes (empty if timeout)
|
pub(crate) const M_CONN_RECV_TIMEOUT: u32 = 4; // ms -> bytes (empty if timeout)
|
||||||
|
|
||||||
|
|||||||
@ -149,11 +149,17 @@ pub fn parse_client_response_into(resp_id: u32, conn_id: u32) {
|
|||||||
let mut tmp = [0u8; 2048];
|
let mut tmp = [0u8; 2048];
|
||||||
loop {
|
loop {
|
||||||
match s.read(&mut tmp) {
|
match s.read(&mut tmp) {
|
||||||
Ok(0) => { return; }
|
Ok(0) => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
buf.extend_from_slice(&tmp[..n]);
|
buf.extend_from_slice(&tmp[..n]);
|
||||||
if find_header_end(&buf).is_some() { break; }
|
if find_header_end(&buf).is_some() {
|
||||||
if buf.len() > 256 * 1024 { break; }
|
break;
|
||||||
|
}
|
||||||
|
if buf.len() > 256 * 1024 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(_) => return,
|
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) {
|
if let Some(rp) = state::RESPONSES.lock().unwrap().get_mut(&resp_id) {
|
||||||
rp.status = status;
|
rp.status = status;
|
||||||
@ -201,4 +209,3 @@ pub fn parse_client_response_into(resp_id: u32, conn_id: u32) {
|
|||||||
rp.client_conn_id = None;
|
rp.client_conn_id = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
//! Provides ServerBox/RequestBox/ResponseBox/ClientBox and socket variants.
|
//! Provides ServerBox/RequestBox/ResponseBox/ClientBox and socket variants.
|
||||||
//! Pure in-process HTTP over localhost for E2E of BoxRef args/returns.
|
//! 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 once_cell::sync::Lazy;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::io::Write as IoWrite;
|
use std::io::Write as IoWrite;
|
||||||
@ -11,7 +12,6 @@ use std::sync::{
|
|||||||
Arc, Mutex,
|
Arc, Mutex,
|
||||||
};
|
};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use crate::state::{ClientState, RequestState, ResponseState, ServerState, SockConnState};
|
|
||||||
|
|
||||||
// ===== Simple logger (enabled when NYASH_NET_LOG=1) =====
|
// ===== Simple logger (enabled when NYASH_NET_LOG=1) =====
|
||||||
static LOG_ON: Lazy<bool> = Lazy::new(|| std::env::var("NYASH_NET_LOG").unwrap_or_default() == "1");
|
static LOG_ON: Lazy<bool> = 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: *mut u8,
|
||||||
result_len: *mut usize,
|
result_len: *mut usize,
|
||||||
) -> i32 {
|
) -> 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]
|
#[no_mangle]
|
||||||
pub static nyash_typebox_SockServerBox: NyashTypeBoxFfi = NyashTypeBoxFfi {
|
pub static nyash_typebox_SockServerBox: NyashTypeBoxFfi = NyashTypeBoxFfi {
|
||||||
@ -268,7 +270,9 @@ extern "C" fn sockclient_invoke_id(
|
|||||||
result: *mut u8,
|
result: *mut u8,
|
||||||
result_len: *mut usize,
|
result_len: *mut usize,
|
||||||
) -> i32 {
|
) -> 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]
|
#[no_mangle]
|
||||||
pub static nyash_typebox_SockClientBox: NyashTypeBoxFfi = NyashTypeBoxFfi {
|
pub static nyash_typebox_SockClientBox: NyashTypeBoxFfi = NyashTypeBoxFfi {
|
||||||
@ -1012,7 +1016,8 @@ unsafe fn client_invoke(
|
|||||||
let body_len = body.len();
|
let body_len = body.len();
|
||||||
// Create client response handle
|
// Create client response handle
|
||||||
let resp_id = state::next_response_id();
|
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;
|
let mut tcp_ok = false;
|
||||||
if let Ok(mut stream) = TcpStream::connect(format!("{}:{}", host, port)) {
|
if let Ok(mut stream) = TcpStream::connect(format!("{}:{}", host, port)) {
|
||||||
let _ = stream.write_all(&req_bytes);
|
let _ = stream.write_all(&req_bytes);
|
||||||
@ -1086,9 +1091,9 @@ unsafe fn client_invoke(
|
|||||||
|
|
||||||
// ===== Helpers =====
|
// ===== Helpers =====
|
||||||
use ffi::slice;
|
use ffi::slice;
|
||||||
mod tlv;
|
|
||||||
mod http_helpers;
|
mod http_helpers;
|
||||||
mod sockets;
|
mod sockets;
|
||||||
|
mod tlv;
|
||||||
|
|
||||||
// ===== HTTP helpers =====
|
// ===== HTTP helpers =====
|
||||||
// moved
|
// moved
|
||||||
|
|||||||
@ -1,14 +1,19 @@
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::io::{Read, Write as IoWrite};
|
use std::io::{Read, Write as IoWrite};
|
||||||
use std::net::{TcpListener, TcpStream};
|
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 std::time::Duration;
|
||||||
|
|
||||||
use crate::consts::*;
|
use crate::consts::*;
|
||||||
use crate::state::{self, SockConnState, SockServerState};
|
use crate::state::{self, SockConnState, SockServerState};
|
||||||
|
|
||||||
// Utilities provided by parent module
|
// 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(
|
pub(crate) unsafe fn sock_server_invoke(
|
||||||
m: u32,
|
m: u32,
|
||||||
@ -51,7 +56,9 @@ pub(crate) unsafe fn sock_server_invoke(
|
|||||||
let conn_id = state::next_sock_conn_id();
|
let conn_id = state::next_sock_conn_id();
|
||||||
state::SOCK_CONNS.lock().unwrap().insert(
|
state::SOCK_CONNS.lock().unwrap().insert(
|
||||||
conn_id,
|
conn_id,
|
||||||
SockConnState { stream: Mutex::new(stream) },
|
SockConnState {
|
||||||
|
stream: Mutex::new(stream),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
logf(format!("sock:accept conn_id={}", conn_id));
|
logf(format!("sock:accept conn_id={}", conn_id));
|
||||||
pending.lock().unwrap().push_back(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();
|
let conn_id = state::next_sock_conn_id();
|
||||||
state::SOCK_CONNS.lock().unwrap().insert(
|
state::SOCK_CONNS.lock().unwrap().insert(
|
||||||
conn_id,
|
conn_id,
|
||||||
SockConnState { stream: Mutex::new(stream) },
|
SockConnState {
|
||||||
|
stream: Mutex::new(stream),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
logf(format!("sock:connect ok conn_id={}", conn_id));
|
logf(format!("sock:connect ok conn_id={}", conn_id));
|
||||||
crate::tlv::write_tlv_handle(T_SOCK_CONN, conn_id, res, res_len)
|
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)
|
crate::tlv::write_u32(0, res, res_len)
|
||||||
}
|
}
|
||||||
M_CONN_SEND => {
|
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 Some(conn) = state::SOCK_CONNS.lock().unwrap().get(&id) {
|
||||||
if let Ok(mut s) = conn.stream.lock() {
|
if let Ok(mut s) = conn.stream.lock() {
|
||||||
let _ = s.write_all(&bytes);
|
let _ = s.write_all(&bytes);
|
||||||
@ -229,11 +239,17 @@ pub(crate) unsafe fn sock_conn_invoke(
|
|||||||
match resv {
|
match resv {
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
buf.truncate(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);
|
return crate::tlv::write_tlv_bytes(&buf, res, res_len);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
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;
|
return E_ERR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,4 +265,3 @@ pub(crate) unsafe fn sock_conn_invoke(
|
|||||||
_ => E_INV_METHOD,
|
_ => E_INV_METHOD,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -264,10 +264,16 @@ extern "C" fn string_invoke_id(
|
|||||||
if let Ok(m) = INST.lock() {
|
if let Ok(m) = INST.lock() {
|
||||||
if let Some(inst) = m.get(&instance_id) {
|
if let Some(inst) = m.get(&instance_id) {
|
||||||
let len = inst.s.len();
|
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);
|
return write_tlv_i64(len as i64, result, result_len);
|
||||||
} else {
|
} else {
|
||||||
eprintln!("[StringBox] Instance {} not found in INST map!", instance_id);
|
eprintln!(
|
||||||
|
"[StringBox] Instance {} not found in INST map!",
|
||||||
|
instance_id
|
||||||
|
);
|
||||||
return E_HANDLE;
|
return E_HANDLE;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -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<ValueId, VMValue>,
|
|
||||||
// Simple local memory for Load/Store where `ptr` is a ValueId token
|
|
||||||
mem: HashMap<ValueId, VMValue>,
|
|
||||||
// Object field storage for RefGet/RefSet (keyed by reference ValueId)
|
|
||||||
obj_fields: HashMap<ValueId, HashMap<String, VMValue>>,
|
|
||||||
// Function table (current module)
|
|
||||||
functions: HashMap<String, MirFunction>,
|
|
||||||
// Currently executing function name (for call resolution preferences)
|
|
||||||
cur_fn: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Box<dyn NyashBox>, 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<VMValue, VMError> {
|
|
||||||
self._exec_function_inner(func, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _exec_function_inner(
|
|
||||||
&mut self,
|
|
||||||
func: &MirFunction,
|
|
||||||
arg_vals: Option<&[VMValue]>,
|
|
||||||
) -> Result<VMValue, VMError> {
|
|
||||||
// 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<BasicBlockId> = 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<Box<dyn crate::box_trait::NyashBox>> = 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<dyn crate::box_trait::NyashBox> = 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::<crate::runtime::plugin_loader_v2::PluginBoxV2>(
|
|
||||||
) {
|
|
||||||
let host =
|
|
||||||
crate::runtime::plugin_loader_unified::get_global_plugin_host();
|
|
||||||
let host = host.read().unwrap();
|
|
||||||
let mut argv: Vec<Box<dyn crate::box_trait::NyashBox>> = 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<dyn crate::box_trait::NyashBox> = 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::<crate::box_trait::StringBox>()
|
|
||||||
{
|
|
||||||
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<dyn crate::box_trait::NyashBox> = match recv.clone() {
|
|
||||||
VMValue::BoxRef(b) => b.share_box(),
|
|
||||||
other => other.to_nyash_box(),
|
|
||||||
};
|
|
||||||
if let Some(p) = recv_box
|
|
||||||
.as_any()
|
|
||||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>(
|
|
||||||
) {
|
|
||||||
// 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<Box<dyn crate::box_trait::NyashBox>> = 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<VMValue, VMError> {
|
|
||||||
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<VMValue, VMError> {
|
|
||||||
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<bool, VMError> {
|
|
||||||
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<VMValue, VMError> {
|
|
||||||
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<VMValue, VMError> {
|
|
||||||
// 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<String> = None;
|
|
||||||
if self.functions.contains_key(&raw) {
|
|
||||||
pick = Some(raw.clone());
|
|
||||||
} else {
|
|
||||||
let arity = args.len();
|
|
||||||
let mut cands: Vec<String> = 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<String> = 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<VMValue> = 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<VMValue, VMError> {
|
|
||||||
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<VMValue, VMError> {
|
|
||||||
// 受信オブジェクトの型に基づいてメソッド実行
|
|
||||||
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::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
|
||||||
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host();
|
|
||||||
let host = host.read().unwrap();
|
|
||||||
let mut argv: Vec<Box<dyn crate::box_trait::NyashBox>> = 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<VMValue, VMError> {
|
|
||||||
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
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
125
src/backend/mir_interpreter/exec.rs
Normal file
125
src/backend/mir_interpreter/exec.rs
Normal file
@ -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<VMValue, VMError> {
|
||||||
|
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<BasicBlockId> = 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<BasicBlockId>,
|
||||||
|
) -> 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<BlockOutcome, VMError> {
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
}
|
||||||
82
src/backend/mir_interpreter/handlers/arithmetic.rs
Normal file
82
src/backend/mir_interpreter/handlers/arithmetic.rs
Normal file
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
265
src/backend/mir_interpreter/handlers/boxes.rs
Normal file
265
src/backend/mir_interpreter/handlers/boxes.rs
Normal file
@ -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<Box<dyn NyashBox>> = 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<ValueId>,
|
||||||
|
box_val: ValueId,
|
||||||
|
method: &str,
|
||||||
|
args: &[ValueId],
|
||||||
|
) -> Result<(), VMError> {
|
||||||
|
let recv = self.reg_load(box_val)?;
|
||||||
|
let recv_box: Box<dyn NyashBox> = match recv.clone() {
|
||||||
|
VMValue::BoxRef(b) => b.share_box(),
|
||||||
|
other => other.to_nyash_box(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(p) = recv_box
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||||
|
{
|
||||||
|
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host();
|
||||||
|
let host = host.read().unwrap();
|
||||||
|
let mut argv: Vec<Box<dyn NyashBox>> = 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<ValueId>,
|
||||||
|
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<ValueId>,
|
||||||
|
box_val: ValueId,
|
||||||
|
method: &str,
|
||||||
|
args: &[ValueId],
|
||||||
|
) -> Result<bool, VMError> {
|
||||||
|
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<ValueId>,
|
||||||
|
box_val: ValueId,
|
||||||
|
method: &str,
|
||||||
|
args: &[ValueId],
|
||||||
|
) -> Result<bool, VMError> {
|
||||||
|
let recv = self.reg_load(box_val)?;
|
||||||
|
let recv_box_any: Box<dyn NyashBox> = 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::<crate::box_trait::StringBox>()
|
||||||
|
{
|
||||||
|
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<ValueId>,
|
||||||
|
box_val: ValueId,
|
||||||
|
method: &str,
|
||||||
|
args: &[ValueId],
|
||||||
|
) -> Result<(), VMError> {
|
||||||
|
let recv = self.reg_load(box_val)?;
|
||||||
|
let recv_box: Box<dyn NyashBox> = match recv.clone() {
|
||||||
|
VMValue::BoxRef(b) => b.share_box(),
|
||||||
|
other => other.to_nyash_box(),
|
||||||
|
};
|
||||||
|
if let Some(p) = recv_box
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||||
|
{
|
||||||
|
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<Box<dyn NyashBox>> = 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
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
252
src/backend/mir_interpreter/handlers/calls.rs
Normal file
252
src/backend/mir_interpreter/handlers/calls.rs
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl MirInterpreter {
|
||||||
|
pub(super) fn handle_call(
|
||||||
|
&mut self,
|
||||||
|
dst: Option<ValueId>,
|
||||||
|
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<VMValue, VMError> {
|
||||||
|
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<VMValue, VMError> {
|
||||||
|
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<String> = None;
|
||||||
|
if self.functions.contains_key(&raw) {
|
||||||
|
pick = Some(raw.clone());
|
||||||
|
} else {
|
||||||
|
let arity = args.len();
|
||||||
|
let mut cands: Vec<String> = 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<String> = 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<VMValue> = 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<VMValue, VMError> {
|
||||||
|
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<VMValue, VMError> {
|
||||||
|
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::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||||
|
{
|
||||||
|
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host();
|
||||||
|
let host = host.read().unwrap();
|
||||||
|
let mut argv: Vec<Box<dyn crate::box_trait::NyashBox>> =
|
||||||
|
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<VMValue, VMError> {
|
||||||
|
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
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
100
src/backend/mir_interpreter/handlers/externals.rs
Normal file
100
src/backend/mir_interpreter/handlers/externals.rs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl MirInterpreter {
|
||||||
|
pub(super) fn handle_extern_call(
|
||||||
|
&mut self,
|
||||||
|
dst: Option<ValueId>,
|
||||||
|
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
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/backend/mir_interpreter/handlers/memory.rs
Normal file
45
src/backend/mir_interpreter/handlers/memory.rs
Normal file
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/backend/mir_interpreter/handlers/misc.rs
Normal file
17
src/backend/mir_interpreter/handlers/misc.rs
Normal file
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
87
src/backend/mir_interpreter/handlers/mod.rs
Normal file
87
src/backend/mir_interpreter/handlers/mod.rs
Normal file
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/backend/mir_interpreter/helpers.rs
Normal file
76
src/backend/mir_interpreter/helpers.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl MirInterpreter {
|
||||||
|
pub(super) fn reg_load(&self, id: ValueId) -> Result<VMValue, VMError> {
|
||||||
|
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<VMValue, VMError> {
|
||||||
|
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<bool, VMError> {
|
||||||
|
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
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/backend/mir_interpreter/mod.rs
Normal file
58
src/backend/mir_interpreter/mod.rs
Normal file
@ -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<ValueId, VMValue>,
|
||||||
|
pub(super) mem: HashMap<ValueId, VMValue>,
|
||||||
|
pub(super) obj_fields: HashMap<ValueId, HashMap<String, VMValue>>,
|
||||||
|
pub(super) functions: HashMap<String, MirFunction>,
|
||||||
|
pub(super) cur_fn: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Box<dyn NyashBox>, 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<VMValue, VMError> {
|
||||||
|
self.exec_function_inner(func, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -257,7 +257,11 @@ fn link_exe(obj_path: &str, out_path: &str, nyrt_dir: &str) -> Result<(), String
|
|||||||
.args([obj_path])
|
.args([obj_path])
|
||||||
.args(["-L", "target/release"])
|
.args(["-L", "target/release"])
|
||||||
.args(["-L", &format!("{}/target/release", nyrt_dir)])
|
.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])
|
.args(["-lpthread", "-ldl", "-lm", "-o", out_path])
|
||||||
.status()
|
.status()
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
use nyash_rust::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
|
||||||
use nyash_rust::mir::MirBuilder;
|
use nyash_rust::mir::MirBuilder;
|
||||||
use nyash_rust::ast::{ASTNode, LiteralValue, BinaryOperator, Span};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_literal_building() {
|
fn test_literal_building() {
|
||||||
@ -64,4 +64,3 @@ fn test_if_statement_building() {
|
|||||||
let stats = function.stats();
|
let stats = function.stats();
|
||||||
assert!(stats.phi_count >= 1);
|
assert!(stats.phi_count >= 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
use nyash_rust::mir::{
|
use nyash_rust::mir::{BinaryOp, ConstValue, Effect, EffectMask, MirInstruction, ValueId};
|
||||||
BinaryOp, ConstValue, Effect, EffectMask, MirInstruction, ValueId,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_const_instruction() {
|
fn test_const_instruction() {
|
||||||
@ -152,4 +150,3 @@ fn test_extern_call_instruction() {
|
|||||||
assert_eq!(void_inst.dst_value(), None);
|
assert_eq!(void_inst.dst_value(), None);
|
||||||
assert_eq!(void_inst.used_values(), vec![arg1]);
|
assert_eq!(void_inst.used_values(), vec![arg1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -160,7 +160,10 @@ fn test_merge_use_before_phi_detected() {
|
|||||||
|
|
||||||
let mut verifier = MirVerifier::new();
|
let mut verifier = MirVerifier::new();
|
||||||
let res = verifier.verify_function(&f);
|
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();
|
let errs = res.err().unwrap();
|
||||||
assert!(
|
assert!(
|
||||||
errs.iter().any(|e| matches!(
|
errs.iter().any(|e| matches!(
|
||||||
@ -320,4 +323,3 @@ fn test_loop_nested_if_phi() {
|
|||||||
mir_text
|
mir_text
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,10 @@ fn stage3_disabled_rejects_try_and_throw() {
|
|||||||
|
|
||||||
let code_throw = "throw 1";
|
let code_throw = "throw 1";
|
||||||
let res_throw = NyashParser::parse_from_string(code_throw);
|
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"), || {
|
with_env("NYASH_PARSER_STAGE3", Some("1"), || {
|
||||||
let code = "throw (1 + 2)";
|
let code = "throw (1 + 2)";
|
||||||
let res = NyashParser::parse_from_string(code);
|
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());
|
assert!(NyashParser::parse_from_string(code3).is_ok());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -169,4 +169,3 @@ fn test_longest_match_sequences() {
|
|||||||
assert!(matches!(kinds[3], TokenType::RANGE));
|
assert!(matches!(kinds[3], TokenType::RANGE));
|
||||||
assert!(matches!(kinds[4], TokenType::DOT));
|
assert!(matches!(kinds[4], TokenType::DOT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -42,7 +42,9 @@ fn golden_transforms() {
|
|||||||
assert!(root.exists(), "missing tests/golden/transforms directory");
|
assert!(root.exists(), "missing tests/golden/transforms directory");
|
||||||
for entry in fs::read_dir(root).expect("scan golden dirs") {
|
for entry in fs::read_dir(root).expect("scan golden dirs") {
|
||||||
let entry = entry.expect("dir entry");
|
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 case_dir = entry.path();
|
||||||
let in_path = case_dir.join("in.json");
|
let in_path = case_dir.join("in.json");
|
||||||
let out_path = case_dir.join("out.golden.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();
|
let exp_s = serde_json::to_string_pretty(&expected).unwrap();
|
||||||
panic!(
|
panic!(
|
||||||
"Golden mismatch in {}\n--- expected\n{}\n--- got\n{}\n",
|
"Golden mismatch in {}\n--- expected\n{}\n--- got\n{}\n",
|
||||||
case_dir.display(), exp_s, got_s
|
case_dir.display(),
|
||||||
|
exp_s,
|
||||||
|
got_s
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user