docs/ci: selfhost bootstrap/exe-first workflows; add ny-llvmc scaffolding + JSON v0 schema validation; plan: unify to Nyash ABI v2 (no backwards compat)
This commit is contained in:
11
crates/nyash-llvm-compiler/Cargo.toml
Normal file
11
crates/nyash-llvm-compiler/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "nyash-llvm-compiler"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Nyash LLVM compiler CLI (harness wrapper). Compiles MIR(JSON) -> object (.o) or dummy."
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
122
crates/nyash-llvm-compiler/src/main.rs
Normal file
122
crates/nyash-llvm-compiler/src/main.rs
Normal file
@ -0,0 +1,122 @@
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use clap::{ArgAction, Parser};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "ny-llvmc", about = "Nyash LLVM compiler (llvmlite harness wrapper)")]
|
||||
struct Args {
|
||||
/// MIR JSON input file path (use '-' to read from stdin). When omitted with --dummy, a dummy ny_main is emitted.
|
||||
#[arg(long = "in", value_name = "FILE", default_value = "-")]
|
||||
infile: String,
|
||||
|
||||
/// Output object file (.o)
|
||||
#[arg(long, value_name = "FILE")]
|
||||
out: PathBuf,
|
||||
|
||||
/// Generate a dummy object (ny_main -> i32 0). Ignores --in when set.
|
||||
#[arg(long, action = ArgAction::SetTrue)]
|
||||
dummy: bool,
|
||||
|
||||
/// Path to Python harness script (defaults to tools/llvmlite_harness.py in CWD)
|
||||
#[arg(long, value_name = "FILE")]
|
||||
harness: Option<PathBuf>,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
// Ensure parent dir exists
|
||||
if let Some(parent) = args.out.parent() {
|
||||
std::fs::create_dir_all(parent).ok();
|
||||
}
|
||||
|
||||
// Resolve harness path
|
||||
let harness_path = if let Some(p) = args.harness.clone() {
|
||||
p
|
||||
} else {
|
||||
PathBuf::from("tools/llvmlite_harness.py")
|
||||
};
|
||||
|
||||
if args.dummy {
|
||||
run_harness_dummy(&harness_path, &args.out)
|
||||
.with_context(|| "failed to run harness in dummy mode")?;
|
||||
println!("[ny-llvmc] dummy object written: {}", args.out.display());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Prepare input JSON path: either from file or stdin -> temp file
|
||||
let mut temp_path: Option<PathBuf> = None;
|
||||
let input_path = if args.infile == "-" {
|
||||
let mut buf = String::new();
|
||||
std::io::stdin()
|
||||
.read_to_string(&mut buf)
|
||||
.context("reading MIR JSON from stdin")?;
|
||||
// Basic sanity check that it's JSON
|
||||
let _: serde_json::Value = serde_json::from_str(&buf)
|
||||
.context("stdin does not contain valid JSON")?;
|
||||
let tmp = std::env::temp_dir().join("ny_llvmc_stdin.json");
|
||||
let mut f = File::create(&tmp).context("create temp json file")?;
|
||||
f.write_all(buf.as_bytes()).context("write temp json")?;
|
||||
temp_path = Some(tmp.clone());
|
||||
tmp
|
||||
} else {
|
||||
PathBuf::from(&args.infile)
|
||||
};
|
||||
|
||||
if !input_path.exists() {
|
||||
bail!("input JSON not found: {}", input_path.display());
|
||||
}
|
||||
|
||||
run_harness_in(&harness_path, &input_path, &args.out)
|
||||
.with_context(|| format!("failed to compile MIR JSON via harness: {}", input_path.display()))?;
|
||||
println!("[ny-llvmc] object written: {}", args.out.display());
|
||||
|
||||
// Cleanup temp file if used
|
||||
if let Some(p) = temp_path {
|
||||
let _ = std::fs::remove_file(p);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_harness_dummy(harness: &Path, out: &Path) -> Result<()> {
|
||||
ensure_python()?;
|
||||
let status = Command::new("python3")
|
||||
.arg(harness)
|
||||
.arg("--out")
|
||||
.arg(out)
|
||||
.status()
|
||||
.context("failed to execute python harness (dummy)")?;
|
||||
if !status.success() {
|
||||
bail!("harness exited with status: {:?}", status.code());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_harness_in(harness: &Path, input: &Path, out: &Path) -> Result<()> {
|
||||
ensure_python()?;
|
||||
let status = Command::new("python3")
|
||||
.arg(harness)
|
||||
.arg("--in")
|
||||
.arg(input)
|
||||
.arg("--out")
|
||||
.arg(out)
|
||||
.status()
|
||||
.context("failed to execute python harness")?;
|
||||
if !status.success() {
|
||||
bail!("harness exited with status: {:?}", status.code());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_python() -> Result<()> {
|
||||
match Command::new("python3").arg("--version").output() {
|
||||
Ok(out) if out.status.success() => Ok(()),
|
||||
_ => bail!("python3 not found in PATH (required for llvmlite harness)"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,6 +78,7 @@ pub extern "C" fn nyash_string_concat_hh_export(a_h: i64, b_h: i64) -> i64 {
|
||||
String::new()
|
||||
};
|
||||
let s = format!("{}{}", to_s(a_h), to_s(b_h));
|
||||
nyash_rust::runtime::global_hooks::gc_alloc(s.len() as u64);
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s));
|
||||
let h = handles::to_handle(arc) as i64;
|
||||
eprintln!("[TRACE] concat_hh -> {}", h);
|
||||
@ -134,6 +135,7 @@ pub extern "C" fn nyash_string_substring_hii_export(h: i64, start: i64, end: i64
|
||||
let (st_u, en_u) = (st as usize, en as usize);
|
||||
let sub = s.get(st_u.min(s.len())..en_u.min(s.len())).unwrap_or("");
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(sub.to_string()));
|
||||
nyash_rust::runtime::global_hooks::gc_alloc(sub.len() as u64);
|
||||
let nh = handles::to_handle(arc) as i64;
|
||||
eprintln!("[TRACE] substring_hii -> {}", nh);
|
||||
nh
|
||||
@ -196,7 +198,8 @@ pub extern "C" fn nyash_box_from_i8_string(ptr: *const i8) -> i64 {
|
||||
Ok(v) => v.to_string(),
|
||||
Err(_) => return 0,
|
||||
};
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s));
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s.clone()));
|
||||
nyash_rust::runtime::global_hooks::gc_alloc(s.len() as u64);
|
||||
let h = handles::to_handle(arc) as i64;
|
||||
eprintln!("[TRACE] from_i8_string -> {}", h);
|
||||
h
|
||||
@ -208,6 +211,7 @@ pub extern "C" fn nyash_box_from_i8_string(ptr: *const i8) -> i64 {
|
||||
pub extern "C" fn nyash_box_from_f64(val: f64) -> i64 {
|
||||
use nyash_rust::{box_trait::NyashBox, boxes::FloatBox, jit::rt::handles};
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(FloatBox::new(val));
|
||||
nyash_rust::runtime::global_hooks::gc_alloc(8);
|
||||
handles::to_handle(arc) as i64
|
||||
}
|
||||
|
||||
@ -220,6 +224,7 @@ pub extern "C" fn nyash_box_from_i64(val: i64) -> i64 {
|
||||
jit::rt::handles,
|
||||
};
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(IntegerBox::new(val));
|
||||
nyash_rust::runtime::global_hooks::gc_alloc(8);
|
||||
handles::to_handle(arc) as i64
|
||||
}
|
||||
|
||||
@ -487,7 +492,8 @@ pub extern "C" fn nyash_string_from_u64x2_export(lo: i64, hi: i64, len: i64) ->
|
||||
bytes.push(((hi_u >> (8 * i)) & 0xff) as u8);
|
||||
}
|
||||
let s = String::from_utf8_lossy(&bytes).to_string();
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s));
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s.clone()));
|
||||
nyash_rust::runtime::global_hooks::gc_alloc(s.len() as u64);
|
||||
handles::to_handle(arc) as i64
|
||||
}
|
||||
|
||||
@ -542,9 +548,25 @@ pub extern "C" fn nyash_gc_barrier_write_export(handle_or_ptr: i64) -> i64 {
|
||||
if std::env::var("NYASH_GC_BARRIER_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[nyrt] nyash.gc.barrier_write h=0x{:x}", handle_or_ptr);
|
||||
}
|
||||
// Forward to runtime GC hooks when available (Write barrier)
|
||||
nyash_rust::runtime::global_hooks::gc_barrier(nyash_rust::runtime::BarrierKind::Write);
|
||||
0
|
||||
}
|
||||
|
||||
// LLVM safepoint exports (llvmlite harness)
|
||||
// export: ny_safepoint(live_count: i64, live_values: i64*) -> void
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ny_safepoint(_live_count: i64, _live_values: *const i64) {
|
||||
// For now we ignore live-values; runtime uses cooperative safepoint + poll
|
||||
nyash_rust::runtime::global_hooks::safepoint_and_poll();
|
||||
}
|
||||
|
||||
// export: ny_check_safepoint() -> void
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ny_check_safepoint() {
|
||||
nyash_rust::runtime::global_hooks::safepoint_and_poll();
|
||||
}
|
||||
|
||||
#[export_name = "nyash.string.birth_h"]
|
||||
pub extern "C" fn nyash_string_birth_h_export() -> i64 {
|
||||
// Create a new StringBox via unified plugin host; return runtime handle as i64
|
||||
@ -552,6 +574,7 @@ pub extern "C" fn nyash_string_birth_h_export() -> i64 {
|
||||
if let Ok(b) = host_g.create_box("StringBox", &[]) {
|
||||
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::from(b);
|
||||
let h = nyash_rust::jit::rt::handles::to_handle(arc);
|
||||
nyash_rust::runtime::global_hooks::gc_alloc(0);
|
||||
return h as i64;
|
||||
}
|
||||
}
|
||||
@ -564,6 +587,7 @@ pub extern "C" fn nyash_integer_birth_h_export() -> i64 {
|
||||
if let Ok(b) = host_g.create_box("IntegerBox", &[]) {
|
||||
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::from(b);
|
||||
let h = nyash_rust::jit::rt::handles::to_handle(arc);
|
||||
nyash_rust::runtime::global_hooks::gc_alloc(0);
|
||||
return h as i64;
|
||||
}
|
||||
}
|
||||
@ -576,6 +600,7 @@ pub extern "C" fn nyash_console_birth_h_export() -> i64 {
|
||||
if let Ok(b) = host_g.create_box("ConsoleBox", &[]) {
|
||||
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::from(b);
|
||||
let h = nyash_rust::jit::rt::handles::to_handle(arc);
|
||||
nyash_rust::runtime::global_hooks::gc_alloc(0);
|
||||
return h as i64;
|
||||
}
|
||||
}
|
||||
@ -587,6 +612,7 @@ pub extern "C" fn nyash_console_birth_h_export() -> i64 {
|
||||
pub extern "C" fn nyash_array_birth_h_export() -> i64 {
|
||||
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> =
|
||||
std::sync::Arc::new(nyash_rust::boxes::array::ArrayBox::new());
|
||||
nyash_rust::runtime::global_hooks::gc_alloc(0);
|
||||
nyash_rust::jit::rt::handles::to_handle(arc) as i64
|
||||
}
|
||||
|
||||
@ -595,6 +621,7 @@ pub extern "C" fn nyash_array_birth_h_export() -> i64 {
|
||||
pub extern "C" fn nyash_map_birth_h_export() -> i64 {
|
||||
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> =
|
||||
std::sync::Arc::new(nyash_rust::boxes::map_box::MapBox::new());
|
||||
nyash_rust::runtime::global_hooks::gc_alloc(0);
|
||||
nyash_rust::jit::rt::handles::to_handle(arc) as i64
|
||||
}
|
||||
// ---- Process entry (driver) ----
|
||||
@ -646,6 +673,15 @@ pub extern "C" fn main() -> i32 {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Initialize a minimal runtime to back global hooks (GC/scheduler) for safepoints
|
||||
// Choose GC hooks based on env (default dev: Counting for observability unless explicitly off)
|
||||
let mut rt_builder = nyash_rust::runtime::NyashRuntimeBuilder::new();
|
||||
let gc_mode = nyash_rust::runtime::gc_mode::GcMode::from_env();
|
||||
let controller = std::sync::Arc::new(nyash_rust::runtime::gc_controller::GcController::new(gc_mode));
|
||||
rt_builder = rt_builder.with_gc_hooks(controller);
|
||||
let rt_hooks = rt_builder.build();
|
||||
nyash_rust::runtime::global_hooks::set_from_runtime(&rt_hooks);
|
||||
|
||||
let mut inited = false;
|
||||
if let Some(dir) = &exe_dir {
|
||||
let candidate = dir.join("nyash.toml");
|
||||
@ -680,6 +716,70 @@ pub extern "C" fn main() -> i32 {
|
||||
let v = ny_main();
|
||||
// Print standardized result line for golden comparisons
|
||||
println!("Result: {}", v);
|
||||
// Optional GC metrics after program completes
|
||||
let want_json = std::env::var("NYASH_GC_METRICS_JSON").ok().as_deref() == Some("1");
|
||||
let want_text = std::env::var("NYASH_GC_METRICS").ok().as_deref() == Some("1");
|
||||
if want_json || want_text {
|
||||
let (sp, br, bw) = rt_hooks
|
||||
.gc
|
||||
.snapshot_counters()
|
||||
.unwrap_or((0, 0, 0));
|
||||
let handles = nyash_rust::jit::rt::handles::len();
|
||||
let gc_mode_s = gc_mode.as_str();
|
||||
// Include allocation totals if controller is used
|
||||
let any_gc: &dyn std::any::Any = &*rt_hooks.gc;
|
||||
let (alloc_count, alloc_bytes, trial_nodes, trial_edges, collect_total, collect_sp, collect_alloc, last_ms, last_reason) = if let Some(ctrl) = any_gc
|
||||
.downcast_ref::<nyash_rust::runtime::gc_controller::GcController>()
|
||||
{
|
||||
let (ac, ab) = ctrl.alloc_totals();
|
||||
let (tn, te) = ctrl.trial_reachability_last();
|
||||
let (ct, csp, calloc) = ctrl.collection_totals();
|
||||
let lms = ctrl.trial_duration_last_ms();
|
||||
let lrf = ctrl.trial_reason_last_bits();
|
||||
(ac, ab, tn, te, ct, csp, calloc, lms, lrf)
|
||||
} else {
|
||||
(0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
};
|
||||
// Settings snapshot (env)
|
||||
let sp_interval = std::env::var("NYASH_GC_COLLECT_SP").ok().and_then(|s| s.parse::<u64>().ok()).unwrap_or(0);
|
||||
let alloc_thresh = std::env::var("NYASH_GC_COLLECT_ALLOC").ok().and_then(|s| s.parse::<u64>().ok()).unwrap_or(0);
|
||||
let auto_sp = std::env::var("NYASH_LLVM_AUTO_SAFEPOINT").ok().map(|v| v == "1").unwrap_or(true);
|
||||
if want_json {
|
||||
// Minimal JSON assembly to avoid extra deps in nyrt
|
||||
println!(
|
||||
"{{\"kind\":\"gc_metrics\",\"safepoints\":{},\"barrier_reads\":{},\"barrier_writes\":{},\"jit_handles\":{},\"alloc_count\":{},\"alloc_bytes\":{},\"trial_nodes\":{},\"trial_edges\":{},\"collections\":{},\"collect_by_sp\":{},\"collect_by_alloc\":{},\"last_collect_ms\":{},\"last_reason_bits\":{},\"sp_interval\":{},\"alloc_threshold\":{},\"auto_safepoint\":{},\"gc_mode\":\"{}\"}}",
|
||||
sp, br, bw, handles, alloc_count, alloc_bytes, trial_nodes, trial_edges, collect_total, collect_sp, collect_alloc, last_ms, last_reason, sp_interval, alloc_thresh, if auto_sp {1} else {0}, gc_mode_s
|
||||
);
|
||||
} else if want_text {
|
||||
eprintln!(
|
||||
"[GC] metrics: safepoints={} read_barriers={} write_barriers={} jit_handles={} allocs={} bytes={} collections={} (sp={} alloc={}) last_ms={} mode={}",
|
||||
sp, br, bw, handles, alloc_count, alloc_bytes, collect_total, collect_sp, collect_alloc, last_ms, gc_mode_s
|
||||
);
|
||||
}
|
||||
// Threshold warning
|
||||
if let Ok(s) = std::env::var("NYASH_GC_ALLOC_THRESHOLD") {
|
||||
if let Ok(th) = s.parse::<u64>() {
|
||||
if alloc_bytes > th {
|
||||
eprintln!(
|
||||
"[GC][warn] allocation bytes {} exceeded threshold {}",
|
||||
alloc_bytes, th
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Leak diagnostics: report remaining JIT handles by type (Top-10)
|
||||
if std::env::var("NYASH_GC_LEAK_DIAG").ok().as_deref() == Some("1") {
|
||||
let tally = nyash_rust::jit::rt::handles::type_tally();
|
||||
let total = tally.iter().map(|(_, n)| *n as u64).sum::<u64>();
|
||||
if total > 0 {
|
||||
eprintln!("[leak] Remaining handles by type (top 10):");
|
||||
for (i, (ty, n)) in tally.into_iter().take(10).enumerate() {
|
||||
eprintln!(" {}. {} x{}", i + 1, ty, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
v as i32
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +131,7 @@ pub extern "C" fn nyash_console_readline_export() -> *mut i8 {
|
||||
// Use read_to_end if stdin is not a TTY? Simpler: read_line through BufRead
|
||||
// For simplicity, read from stdin into buffer until newline or EOF
|
||||
let mut buf = String::new();
|
||||
let mut handle = io::stdin();
|
||||
// Note: use std::io::stdin() directly without an unused handle binding
|
||||
// On failure or EOF, return empty string
|
||||
match io::stdin().read_line(&mut buf) {
|
||||
Ok(_n) => {
|
||||
|
||||
@ -114,7 +114,7 @@ pub extern "C" fn nyash_future_spawn_method_h(
|
||||
let handle =
|
||||
nyash_rust::jit::rt::handles::to_handle(fut_box.clone() as std::sync::Arc<dyn NyashBox>);
|
||||
// Copy data for async task
|
||||
let mut cap: usize = 512;
|
||||
let cap: usize = 512;
|
||||
let tlv = buf.clone();
|
||||
let inv = invoke.unwrap();
|
||||
nyash_rust::runtime::global_hooks::spawn_task(
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::encode::{nyrt_encode_arg_or_legacy, nyrt_encode_from_legacy_at};
|
||||
use crate::plugin::invoke_core;
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_invoke3_i64(
|
||||
type_id: i64,
|
||||
@ -8,39 +9,14 @@ pub extern "C" fn nyash_plugin_invoke3_i64(
|
||||
a1: i64,
|
||||
a2: i64,
|
||||
) -> i64 {
|
||||
use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
|
||||
// Resolve receiver instance from handle first; fallback to legacy VM args (param index)
|
||||
let mut instance_id: u32 = 0;
|
||||
let mut real_type_id: u32 = 0;
|
||||
let mut invoke: Option<
|
||||
unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
||||
> = None;
|
||||
if a0 > 0 {
|
||||
if let Some(obj) = nyash_rust::jit::rt::handles::get(a0 as u64) {
|
||||
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
instance_id = p.instance_id();
|
||||
real_type_id = p.inner.type_id;
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
if invoke.is_none()
|
||||
&& a0 >= 0
|
||||
&& std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1")
|
||||
{
|
||||
nyash_rust::jit::rt::with_legacy_vm_args(|args| {
|
||||
let idx = a0 as usize;
|
||||
if let Some(nyash_rust::backend::vm::VMValue::BoxRef(b)) = args.get(idx) {
|
||||
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
instance_id = p.instance_id();
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if invoke.is_none() {
|
||||
return 0;
|
||||
}
|
||||
// Resolve receiver via shared core helper
|
||||
let recv = match invoke_core::resolve_receiver_for_a0(a0) {
|
||||
Some(r) => r,
|
||||
None => return 0,
|
||||
};
|
||||
let instance_id: u32 = recv.instance_id;
|
||||
let _real_type_id: u32 = recv.real_type_id;
|
||||
let invoke = recv.invoke;
|
||||
// Build TLV args from a1/a2 if present. Prefer handles/StringBox/IntegerBox via runtime host.
|
||||
use nyash_rust::{backend::vm::VMValue, jit::rt::handles};
|
||||
// argc from LLVM lowering is explicit arg count (excludes receiver)
|
||||
@ -48,164 +24,11 @@ pub extern "C" fn nyash_plugin_invoke3_i64(
|
||||
let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
|
||||
// Encode legacy VM arg at position into provided buffer (avoid capturing &mut buf)
|
||||
let mut encode_from_legacy_into = |dst: &mut Vec<u8>, arg_pos: usize| {
|
||||
nyash_rust::jit::rt::with_legacy_vm_args(|args| {
|
||||
if let Some(v) = args.get(arg_pos) {
|
||||
match v {
|
||||
VMValue::String(s) => {
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::string(dst, s)
|
||||
}
|
||||
VMValue::Integer(i) => {
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::i64(dst, *i)
|
||||
}
|
||||
VMValue::Float(f) => {
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::f64(dst, *f)
|
||||
}
|
||||
VMValue::Bool(b) => {
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::bool(dst, *b)
|
||||
}
|
||||
VMValue::BoxRef(b) => {
|
||||
// BufferBox → TLV bytes
|
||||
if let Some(bufbox) = b
|
||||
.as_any()
|
||||
.downcast_ref::<nyash_rust::boxes::buffer::BufferBox>()
|
||||
{
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::bytes(
|
||||
dst,
|
||||
&bufbox.to_vec(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
// Prefer StringBox/IntegerBox primitives when possible
|
||||
let host = nyash_rust::runtime::get_global_plugin_host();
|
||||
if let Ok(hg) = host.read() {
|
||||
if p.box_type == "StringBox" {
|
||||
if let Ok(Some(sb)) = hg.invoke_instance_method(
|
||||
"StringBox",
|
||||
"toUtf8",
|
||||
p.instance_id(),
|
||||
&[],
|
||||
) {
|
||||
if let Some(s) = sb
|
||||
.as_any()
|
||||
.downcast_ref::<nyash_rust::box_trait::StringBox>()
|
||||
{
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::string(
|
||||
dst, &s.value,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if p.box_type == "IntegerBox" {
|
||||
if let Ok(Some(ibx)) = hg.invoke_instance_method(
|
||||
"IntegerBox",
|
||||
"get",
|
||||
p.instance_id(),
|
||||
&[],
|
||||
) {
|
||||
if let Some(i) =
|
||||
ibx.as_any()
|
||||
.downcast_ref::<nyash_rust::box_trait::IntegerBox>()
|
||||
{
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::i64(
|
||||
dst, i.value,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback: pass handle as plugin-handle TLV
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle(
|
||||
dst,
|
||||
p.inner.type_id,
|
||||
p.instance_id(),
|
||||
);
|
||||
} else {
|
||||
// Stringify unknown boxes
|
||||
let s = b.to_string_box().value;
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::string(dst, &s)
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
nyrt_encode_from_legacy_at(dst, arg_pos)
|
||||
};
|
||||
// Encode argument value or fallback to legacy slot (avoid capturing &mut buf)
|
||||
let mut encode_arg_into = |dst: &mut Vec<u8>, val: i64, pos: usize| {
|
||||
let mut appended = false;
|
||||
// Try handle first
|
||||
if val > 0 {
|
||||
if let Some(obj) = handles::get(val as u64) {
|
||||
// BufferBox handle → TLV bytes
|
||||
if let Some(bufbox) = obj
|
||||
.as_any()
|
||||
.downcast_ref::<nyash_rust::boxes::buffer::BufferBox>()
|
||||
{
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::bytes(dst, &bufbox.to_vec());
|
||||
appended = true;
|
||||
return;
|
||||
}
|
||||
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
let host = nyash_rust::runtime::get_global_plugin_host();
|
||||
if let Ok(hg) = host.read() {
|
||||
if p.box_type == "StringBox" {
|
||||
if let Ok(Some(sb)) = hg.invoke_instance_method(
|
||||
"StringBox",
|
||||
"toUtf8",
|
||||
p.instance_id(),
|
||||
&[],
|
||||
) {
|
||||
if let Some(s) = sb
|
||||
.as_any()
|
||||
.downcast_ref::<nyash_rust::box_trait::StringBox>()
|
||||
{
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::string(
|
||||
dst, &s.value,
|
||||
);
|
||||
appended = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if p.box_type == "IntegerBox" {
|
||||
if let Ok(Some(ibx)) =
|
||||
hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[])
|
||||
{
|
||||
if let Some(i) = ibx
|
||||
.as_any()
|
||||
.downcast_ref::<nyash_rust::box_trait::IntegerBox>()
|
||||
{
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::i64(
|
||||
dst, i.value,
|
||||
);
|
||||
appended = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Otherwise, pass as handle TLV
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle(
|
||||
dst,
|
||||
p.inner.type_id,
|
||||
p.instance_id(),
|
||||
);
|
||||
appended = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Legacy VM args by positional index (1-based for a1)
|
||||
let before = dst.len();
|
||||
encode_from_legacy_into(dst, pos);
|
||||
if dst.len() != before {
|
||||
appended = true;
|
||||
}
|
||||
// If still nothing appended (no-op), fallback to raw i64
|
||||
if !appended {
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::i64(dst, val);
|
||||
}
|
||||
nyrt_encode_arg_or_legacy(dst, val, pos)
|
||||
};
|
||||
if nargs >= 1 {
|
||||
encode_arg_into(&mut buf, a1, 1);
|
||||
@ -219,123 +42,20 @@ pub extern "C" fn nyash_plugin_invoke3_i64(
|
||||
encode_from_legacy_into(&mut buf, pos);
|
||||
}
|
||||
}
|
||||
// Prepare output buffer (dynamic growth on short buffer)
|
||||
let mut cap: usize = 256;
|
||||
let (mut tag_ret, mut sz_ret, mut payload_ret): (u8, usize, Vec<u8>) = (0, 0, Vec::new());
|
||||
loop {
|
||||
let mut out = vec![0u8; cap];
|
||||
let mut out_len: usize = out.len();
|
||||
let rc = unsafe {
|
||||
invoke.unwrap()(
|
||||
type_id as u32,
|
||||
method_id as u32,
|
||||
instance_id,
|
||||
buf.as_ptr(),
|
||||
buf.len(),
|
||||
out.as_mut_ptr(),
|
||||
&mut out_len,
|
||||
)
|
||||
};
|
||||
if rc != 0 {
|
||||
// Retry on short buffer hint (-1) or when plugin wrote beyond capacity (len > cap)
|
||||
if rc == -1 || out_len > cap {
|
||||
cap = cap.saturating_mul(2).max(out_len + 16);
|
||||
if cap > 1 << 20 {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
let slice = &out[..out_len];
|
||||
if let Some((t, s, p)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(slice) {
|
||||
tag_ret = t;
|
||||
sz_ret = s;
|
||||
payload_ret = p.to_vec();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if payload_ret.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
// Call invoke with dynamic buffer logic centralized
|
||||
let (tag_ret, sz_ret, payload_ret): (u8, usize, Vec<u8>) = match invoke_core::plugin_invoke_call(
|
||||
invoke,
|
||||
type_id as u32,
|
||||
method_id as u32,
|
||||
instance_id,
|
||||
&buf,
|
||||
) {
|
||||
Some((t, s, p)) => (t, s, p),
|
||||
None => return 0,
|
||||
};
|
||||
if let Some((tag, sz, payload)) = Some((tag_ret, sz_ret, payload_ret.as_slice())) {
|
||||
match tag {
|
||||
2 => {
|
||||
// I32
|
||||
if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) {
|
||||
return v as i64;
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
// I64
|
||||
if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) {
|
||||
return v as i64;
|
||||
}
|
||||
if payload.len() == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
return i64::from_le_bytes(b);
|
||||
}
|
||||
}
|
||||
6 | 7 => {
|
||||
// String/Bytes -> register StringBox handle
|
||||
use nyash_rust::box_trait::{NyashBox, StringBox};
|
||||
let s = nyash_rust::runtime::plugin_ffi_common::decode::string(payload);
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s));
|
||||
let h = nyash_rust::jit::rt::handles::to_handle(arc);
|
||||
return h as i64;
|
||||
}
|
||||
8 => {
|
||||
// Handle(tag=8) -> register and return handle id (i64)
|
||||
if sz == 8 {
|
||||
let mut t = [0u8; 4];
|
||||
t.copy_from_slice(&payload[0..4]);
|
||||
let mut i = [0u8; 4];
|
||||
i.copy_from_slice(&payload[4..8]);
|
||||
let r_type = u32::from_le_bytes(t);
|
||||
let r_inst = u32::from_le_bytes(i);
|
||||
// Build PluginBoxV2 and register into handle-registry
|
||||
let meta_opt =
|
||||
nyash_rust::runtime::plugin_loader_v2::metadata_for_type_id(r_type);
|
||||
let (box_type_name, invoke_ptr) = if let Some(meta) = meta_opt {
|
||||
(meta.box_type.clone(), meta.invoke_fn)
|
||||
} else {
|
||||
("PluginBox".to_string(), invoke.unwrap())
|
||||
};
|
||||
let pb = nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2(
|
||||
box_type_name.clone(),
|
||||
r_type,
|
||||
r_inst,
|
||||
invoke_ptr,
|
||||
);
|
||||
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> =
|
||||
std::sync::Arc::new(pb);
|
||||
let h = nyash_rust::jit::rt::handles::to_handle(arc);
|
||||
return h as i64;
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
// Bool
|
||||
return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
}
|
||||
5 => {
|
||||
// F64 → optional conversion to i64
|
||||
if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") {
|
||||
if sz == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
let f = f64::from_le_bytes(b);
|
||||
return f as i64;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
if let Some(v) = invoke_core::decode_entry_to_i64(tag, sz, payload, invoke) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
0
|
||||
@ -484,77 +204,7 @@ pub extern "C" fn nyash_plugin_invoke3_f64(
|
||||
});
|
||||
};
|
||||
let mut encode_arg = |val: i64, pos: usize| {
|
||||
let mut appended = false;
|
||||
if val > 0 {
|
||||
if let Some(obj) = handles::get(val as u64) {
|
||||
if let Some(bufbox) = obj
|
||||
.as_any()
|
||||
.downcast_ref::<nyash_rust::boxes::buffer::BufferBox>()
|
||||
{
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::bytes(
|
||||
&mut buf,
|
||||
&bufbox.to_vec(),
|
||||
);
|
||||
appended = true;
|
||||
return;
|
||||
}
|
||||
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
let host = nyash_rust::runtime::get_global_plugin_host();
|
||||
if let Ok(hg) = host.read() {
|
||||
if p.box_type == "StringBox" {
|
||||
if let Ok(Some(sb)) = hg.invoke_instance_method(
|
||||
"StringBox",
|
||||
"toUtf8",
|
||||
p.instance_id(),
|
||||
&[],
|
||||
) {
|
||||
if let Some(s) = sb
|
||||
.as_any()
|
||||
.downcast_ref::<nyash_rust::box_trait::StringBox>()
|
||||
{
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::string(
|
||||
&mut buf, &s.value,
|
||||
);
|
||||
appended = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if p.box_type == "IntegerBox" {
|
||||
if let Ok(Some(ibx)) =
|
||||
hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[])
|
||||
{
|
||||
if let Some(i) = ibx
|
||||
.as_any()
|
||||
.downcast_ref::<nyash_rust::box_trait::IntegerBox>()
|
||||
{
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::i64(
|
||||
&mut buf, i.value,
|
||||
);
|
||||
appended = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle(
|
||||
&mut buf,
|
||||
p.inner.type_id,
|
||||
p.instance_id(),
|
||||
);
|
||||
appended = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
let before = buf.len();
|
||||
// Use global helper to avoid nested mutable borrows on buf
|
||||
nyrt_encode_from_legacy_at(&mut buf, pos);
|
||||
if buf.len() != before {
|
||||
appended = true;
|
||||
}
|
||||
if !appended {
|
||||
nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, val);
|
||||
}
|
||||
crate::encode::nyrt_encode_arg_or_legacy(&mut buf, val, pos)
|
||||
};
|
||||
if nargs >= 1 {
|
||||
encode_arg(a1, 1);
|
||||
@ -567,77 +217,20 @@ pub extern "C" fn nyash_plugin_invoke3_f64(
|
||||
nyrt_encode_from_legacy_at(&mut buf, pos);
|
||||
}
|
||||
}
|
||||
// Prepare output buffer (dynamic growth on short buffer)
|
||||
let mut cap: usize = 256;
|
||||
let (mut tag_ret, mut sz_ret, mut payload_ret): (u8, usize, Vec<u8>) = (0, 0, Vec::new());
|
||||
loop {
|
||||
let mut out = vec![0u8; cap];
|
||||
let mut out_len: usize = out.len();
|
||||
let rc = unsafe {
|
||||
invoke.unwrap()(
|
||||
type_id as u32,
|
||||
method_id as u32,
|
||||
instance_id,
|
||||
buf.as_ptr(),
|
||||
buf.len(),
|
||||
out.as_mut_ptr(),
|
||||
&mut out_len,
|
||||
)
|
||||
};
|
||||
if rc != 0 {
|
||||
// Retry on short buffer (-1) or when plugin wrote beyond capacity
|
||||
if rc == -1 || out_len > cap {
|
||||
cap = cap.saturating_mul(2).max(out_len + 16);
|
||||
if cap > 1 << 20 {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
let slice = &out[..out_len];
|
||||
if let Some((t, s, p)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(slice) {
|
||||
tag_ret = t;
|
||||
sz_ret = s;
|
||||
payload_ret = p.to_vec();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if payload_ret.is_empty() {
|
||||
return 0.0;
|
||||
}
|
||||
// Invoke via shared helper
|
||||
let (mut tag_ret, mut sz_ret, mut payload_ret): (u8, usize, Vec<u8>) = match invoke_core::plugin_invoke_call(
|
||||
invoke.unwrap(),
|
||||
type_id as u32,
|
||||
method_id as u32,
|
||||
instance_id,
|
||||
&buf,
|
||||
) {
|
||||
Some((t, s, p)) => (t, s, p),
|
||||
None => return 0.0,
|
||||
};
|
||||
if let Some((tag, sz, payload)) = Some((tag_ret, sz_ret, payload_ret.as_slice())) {
|
||||
match tag {
|
||||
5 => {
|
||||
// F64
|
||||
if sz == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
return f64::from_le_bytes(b);
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
// I64 -> f64
|
||||
if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) {
|
||||
return v as f64;
|
||||
}
|
||||
if payload.len() == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
return (i64::from_le_bytes(b)) as f64;
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
// Bool -> f64
|
||||
return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
if let Some(f) = invoke_core::decode_entry_to_f64(tag, sz, payload) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
0.0
|
||||
@ -832,42 +425,10 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64
|
||||
&mut out_len,
|
||||
)
|
||||
};
|
||||
if rc != 0 {
|
||||
return 0;
|
||||
}
|
||||
if rc != 0 { return 0; }
|
||||
let out_slice = &out[..out_len];
|
||||
if let Some((tag, _sz, payload)) =
|
||||
nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(out_slice)
|
||||
{
|
||||
match tag {
|
||||
3 => {
|
||||
if payload.len() == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
return i64::from_le_bytes(b);
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
}
|
||||
5 => {
|
||||
if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") {
|
||||
if payload.len() == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
let f = f64::from_le_bytes(b);
|
||||
return f as i64;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Some((tag, sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(out_slice) {
|
||||
if let Some(v) = super::invoke_core::decode_entry_to_i64(tag, sz, payload, invoke.unwrap()) { return v; }
|
||||
}
|
||||
0
|
||||
}
|
||||
@ -1106,73 +667,8 @@ pub extern "C" fn nyash_plugin_invoke3_tagged_i64(
|
||||
if rc != 0 {
|
||||
return 0;
|
||||
}
|
||||
if let Some((tag, _sz, payload)) =
|
||||
nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len])
|
||||
{
|
||||
match tag {
|
||||
2 => {
|
||||
if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) {
|
||||
return v as i64;
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
if payload.len() == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
return i64::from_le_bytes(b);
|
||||
}
|
||||
if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) {
|
||||
return v as i64;
|
||||
}
|
||||
}
|
||||
6 | 7 => {
|
||||
use nyash_rust::box_trait::{NyashBox, StringBox};
|
||||
let s = nyash_rust::runtime::plugin_ffi_common::decode::string(payload);
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s));
|
||||
let h = nyash_rust::jit::rt::handles::to_handle(arc);
|
||||
return h as i64;
|
||||
}
|
||||
1 => {
|
||||
return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
}
|
||||
8 => {
|
||||
if payload.len() == 8 {
|
||||
let mut t = [0u8; 4];
|
||||
t.copy_from_slice(&payload[0..4]);
|
||||
let mut i = [0u8; 4];
|
||||
i.copy_from_slice(&payload[4..8]);
|
||||
let r_type = u32::from_le_bytes(t);
|
||||
let r_inst = u32::from_le_bytes(i);
|
||||
let pb = nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2(
|
||||
"PluginBox".into(),
|
||||
r_type,
|
||||
r_inst,
|
||||
invoke.unwrap(),
|
||||
);
|
||||
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> =
|
||||
std::sync::Arc::new(pb);
|
||||
let h = nyash_rust::jit::rt::handles::to_handle(arc);
|
||||
return h as i64;
|
||||
}
|
||||
}
|
||||
5 => {
|
||||
if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") {
|
||||
if payload.len() == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
let f = f64::from_le_bytes(b);
|
||||
return f as i64;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Some((tag, sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
|
||||
if let Some(v) = invoke_core::decode_entry_to_i64(tag, sz, payload, invoke.unwrap()) { return v; }
|
||||
}
|
||||
0
|
||||
}
|
||||
@ -1263,73 +759,8 @@ pub extern "C" fn nyash_plugin_invoke_tagged_v_i64(
|
||||
if rc != 0 {
|
||||
return 0;
|
||||
}
|
||||
if let Some((tag, _sz, payload)) =
|
||||
nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len])
|
||||
{
|
||||
match tag {
|
||||
2 => {
|
||||
if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) {
|
||||
return v as i64;
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
if payload.len() == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
return i64::from_le_bytes(b);
|
||||
}
|
||||
if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) {
|
||||
return v as i64;
|
||||
}
|
||||
}
|
||||
6 | 7 => {
|
||||
use nyash_rust::box_trait::{NyashBox, StringBox};
|
||||
let s = nyash_rust::runtime::plugin_ffi_common::decode::string(payload);
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s));
|
||||
let h = nyash_rust::jit::rt::handles::to_handle(arc);
|
||||
return h as i64;
|
||||
}
|
||||
1 => {
|
||||
return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
}
|
||||
8 => {
|
||||
if payload.len() == 8 {
|
||||
let mut t = [0u8; 4];
|
||||
t.copy_from_slice(&payload[0..4]);
|
||||
let mut i = [0u8; 4];
|
||||
i.copy_from_slice(&payload[4..8]);
|
||||
let r_type = u32::from_le_bytes(t);
|
||||
let r_inst = u32::from_le_bytes(i);
|
||||
let pb = nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2(
|
||||
"PluginBox".into(),
|
||||
r_type,
|
||||
r_inst,
|
||||
invoke.unwrap(),
|
||||
);
|
||||
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> =
|
||||
std::sync::Arc::new(pb);
|
||||
let h = nyash_rust::jit::rt::handles::to_handle(arc);
|
||||
return h as i64;
|
||||
}
|
||||
}
|
||||
5 => {
|
||||
if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") {
|
||||
if payload.len() == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
let f = f64::from_le_bytes(b);
|
||||
return f as i64;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Some((tag, sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
|
||||
if let Some(v) = invoke_core::decode_entry_to_i64(tag, sz, payload, invoke.unwrap()) { return v; }
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
199
crates/nyrt/src/plugin/invoke_core.rs
Normal file
199
crates/nyrt/src/plugin/invoke_core.rs
Normal file
@ -0,0 +1,199 @@
|
||||
use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
|
||||
|
||||
/// Thin shared helpers for plugin invoke shims (i64/f64)
|
||||
///
|
||||
/// Goal: centralize receiver resolution and the dynamic buffer call loop,
|
||||
/// keeping extern functions in invoke.rs small and consistent.
|
||||
|
||||
pub struct Receiver {
|
||||
pub instance_id: u32,
|
||||
pub real_type_id: u32,
|
||||
pub invoke:
|
||||
unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
||||
}
|
||||
|
||||
/// Resolve receiver from a0: prefer handle registry; fallback to legacy VM args when allowed.
|
||||
pub fn resolve_receiver_for_a0(a0: i64) -> Option<Receiver> {
|
||||
// 1) Handle registry (preferred)
|
||||
if a0 > 0 {
|
||||
if let Some(obj) = nyash_rust::jit::rt::handles::get(a0 as u64) {
|
||||
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
return Some(Receiver {
|
||||
instance_id: p.instance_id(),
|
||||
real_type_id: p.inner.type_id,
|
||||
invoke: p.inner.invoke_fn,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2) Legacy VM args (index by a0) unless handle-only is enforced
|
||||
if a0 >= 0
|
||||
&& std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1")
|
||||
{
|
||||
nyash_rust::jit::rt::with_legacy_vm_args(|args| {
|
||||
let idx = a0 as usize;
|
||||
if let Some(nyash_rust::backend::vm::VMValue::BoxRef(b)) = args.get(idx) {
|
||||
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
return Some(Receiver {
|
||||
instance_id: p.instance_id(),
|
||||
real_type_id: p.inner.type_id,
|
||||
invoke: p.inner.invoke_fn,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Call plugin invoke with dynamic buffer growth, returning first TLV entry on success.
|
||||
pub fn plugin_invoke_call(
|
||||
invoke: unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
||||
type_id: u32,
|
||||
method_id: u32,
|
||||
instance_id: u32,
|
||||
tlv_args: &[u8],
|
||||
) -> Option<(u8, usize, Vec<u8>)> {
|
||||
let mut cap: usize = 256;
|
||||
let mut tag_ret: u8 = 0;
|
||||
let mut sz_ret: usize = 0;
|
||||
let mut payload_ret: Vec<u8> = Vec::new();
|
||||
loop {
|
||||
let mut out = vec![0u8; cap];
|
||||
let mut out_len: usize = out.len();
|
||||
let rc = unsafe {
|
||||
invoke(
|
||||
type_id,
|
||||
method_id,
|
||||
instance_id,
|
||||
tlv_args.as_ptr(),
|
||||
tlv_args.len(),
|
||||
out.as_mut_ptr(),
|
||||
&mut out_len,
|
||||
)
|
||||
};
|
||||
if rc != 0 {
|
||||
// Retry on short buffer hint (-1) or when plugin wrote beyond capacity (len > cap)
|
||||
if rc == -1 || out_len > cap {
|
||||
cap = cap.saturating_mul(2).max(out_len + 16);
|
||||
if cap > 1 << 20 {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
let slice = &out[..out_len];
|
||||
if let Some((t, s, p)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(slice) {
|
||||
tag_ret = t;
|
||||
sz_ret = s;
|
||||
payload_ret = p.to_vec();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if payload_ret.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some((tag_ret, sz_ret, payload_ret))
|
||||
}
|
||||
|
||||
/// Decode a single TLV entry to i64 with side-effects (handle registration) when applicable.
|
||||
pub fn decode_entry_to_i64(
|
||||
tag: u8,
|
||||
sz: usize,
|
||||
payload: &[u8],
|
||||
fallback_invoke: unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
||||
) -> Option<i64> {
|
||||
match tag {
|
||||
2 => nyash_rust::runtime::plugin_ffi_common::decode::i32(payload).map(|v| v as i64),
|
||||
3 => {
|
||||
if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) {
|
||||
return Some(v as i64);
|
||||
}
|
||||
if payload.len() == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
return Some(i64::from_le_bytes(b));
|
||||
}
|
||||
None
|
||||
}
|
||||
6 | 7 => {
|
||||
use nyash_rust::box_trait::{NyashBox, StringBox};
|
||||
let s = nyash_rust::runtime::plugin_ffi_common::decode::string(payload);
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s));
|
||||
let h = nyash_rust::jit::rt::handles::to_handle(arc);
|
||||
Some(h as i64)
|
||||
}
|
||||
8 => {
|
||||
if sz == 8 {
|
||||
let mut t = [0u8; 4];
|
||||
t.copy_from_slice(&payload[0..4]);
|
||||
let mut i = [0u8; 4];
|
||||
i.copy_from_slice(&payload[4..8]);
|
||||
let r_type = u32::from_le_bytes(t);
|
||||
let r_inst = u32::from_le_bytes(i);
|
||||
// Use metadata if available to set box_type/invoke_fn
|
||||
let meta_opt = nyash_rust::runtime::plugin_loader_v2::metadata_for_type_id(r_type);
|
||||
let (box_type_name, invoke_ptr) = if let Some(meta) = meta_opt {
|
||||
(meta.box_type.clone(), meta.invoke_fn)
|
||||
} else {
|
||||
("PluginBox".to_string(), fallback_invoke)
|
||||
};
|
||||
let pb = nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2(
|
||||
box_type_name,
|
||||
r_type,
|
||||
r_inst,
|
||||
invoke_ptr,
|
||||
);
|
||||
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> =
|
||||
std::sync::Arc::new(pb);
|
||||
let h = nyash_rust::jit::rt::handles::to_handle(arc);
|
||||
return Some(h as i64);
|
||||
}
|
||||
None
|
||||
}
|
||||
1 => nyash_rust::runtime::plugin_ffi_common::decode::bool(payload)
|
||||
.map(|b| if b { 1 } else { 0 }),
|
||||
5 => {
|
||||
if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") && sz == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
let f = f64::from_le_bytes(b);
|
||||
return Some(f as i64);
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Decode a single TLV entry to f64 when possible.
|
||||
pub fn decode_entry_to_f64(tag: u8, sz: usize, payload: &[u8]) -> Option<f64> {
|
||||
match tag {
|
||||
5 => {
|
||||
if sz == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
Some(f64::from_le_bytes(b))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) {
|
||||
return Some(v as f64);
|
||||
}
|
||||
if payload.len() == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
return Some((i64::from_le_bytes(b)) as f64);
|
||||
}
|
||||
None
|
||||
}
|
||||
1 => nyash_rust::runtime::plugin_ffi_common::decode::bool(payload)
|
||||
.map(|b| if b { 1.0 } else { 0.0 }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ pub mod console;
|
||||
pub mod future;
|
||||
pub mod instance;
|
||||
pub mod invoke;
|
||||
pub mod invoke_core;
|
||||
pub mod map;
|
||||
pub mod semantics;
|
||||
pub mod string;
|
||||
@ -14,6 +15,7 @@ pub use console::*;
|
||||
pub use future::*;
|
||||
pub use instance::*;
|
||||
pub use invoke::*;
|
||||
pub use invoke_core::*;
|
||||
pub use map::*;
|
||||
pub use semantics::*;
|
||||
pub use string::*;
|
||||
|
||||
Reference in New Issue
Block a user