200 lines
8.8 KiB
Rust
200 lines
8.8 KiB
Rust
// Process entry point for NyRT.
|
||
|
||
// ---- Process entry (driver) ----
|
||
#[cfg(not(test))]
|
||
#[no_mangle]
|
||
pub extern "C" fn main() -> i32 {
|
||
// Phase 88: AOT 実行器でも Ring0Context は必須(PluginHost/ログなどが依存する)。
|
||
// EXE 直起動では host 側の init が存在しないため、ここで先に初期化する。
|
||
if nyash_rust::runtime::ring0::GLOBAL_RING0.get().is_none() {
|
||
nyash_rust::runtime::ring0::init_global_ring0(nyash_rust::runtime::ring0::default_ring0());
|
||
}
|
||
|
||
// Initialize plugin host: prefer nyash.toml next to the executable; fallback to CWD
|
||
let exe_dir = std::env::current_exe()
|
||
.ok()
|
||
.and_then(|p| p.parent().map(|d| d.to_path_buf()));
|
||
|
||
// Windows: assist DLL/plugin discovery by extending PATH and normalizing PYTHONHOME
|
||
#[cfg(target_os = "windows")]
|
||
if let Some(dir) = &exe_dir {
|
||
use std::path::PathBuf;
|
||
// Extend PATH with exe_dir and exe_dir\plugins if not already present
|
||
let mut path_val = std::env::var("PATH").unwrap_or_default();
|
||
let add_path = |pv: &mut String, p: &PathBuf| {
|
||
let ps = p.display().to_string();
|
||
if !pv.split(';').any(|seg| seg.eq_ignore_ascii_case(&ps)) {
|
||
if !pv.is_empty() {
|
||
pv.push(';');
|
||
}
|
||
pv.push_str(&ps);
|
||
}
|
||
};
|
||
add_path(&mut path_val, dir);
|
||
let plug = dir.join("plugins");
|
||
if plug.is_dir() {
|
||
add_path(&mut path_val, &plug);
|
||
}
|
||
std::env::set_var("PATH", &path_val);
|
||
|
||
// Normalize PYTHONHOME: if unset, point to exe_dir\python when present.
|
||
match std::env::var("PYTHONHOME") {
|
||
Ok(v) => {
|
||
// If relative, make absolute under exe_dir
|
||
let pb = PathBuf::from(&v);
|
||
if pb.is_relative() {
|
||
let abs = dir.join(pb);
|
||
std::env::set_var("PYTHONHOME", abs.display().to_string());
|
||
}
|
||
}
|
||
Err(_) => {
|
||
let cand = dir.join("python");
|
||
if cand.is_dir() {
|
||
std::env::set_var("PYTHONHOME", cand.display().to_string());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// 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");
|
||
if candidate.exists() {
|
||
let _ =
|
||
nyash_rust::runtime::init_global_plugin_host(candidate.to_string_lossy().as_ref());
|
||
inited = true;
|
||
}
|
||
}
|
||
if !inited {
|
||
let _ = nyash_rust::runtime::init_global_plugin_host("nyash.toml");
|
||
}
|
||
// Optional verbosity
|
||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||
println!(
|
||
"🔌 nyrt: plugin host init attempted (exe_dir={}, cwd={})",
|
||
exe_dir
|
||
.as_ref()
|
||
.map(|p| p.display().to_string())
|
||
.unwrap_or_else(|| "?".into()),
|
||
std::env::current_dir()
|
||
.map(|p| p.display().to_string())
|
||
.unwrap_or_else(|_| "?".into())
|
||
);
|
||
}
|
||
// Call exported Nyash entry if linked: `ny_main` (i64 -> return code normalized)
|
||
unsafe {
|
||
extern "C" {
|
||
fn ny_main() -> i64;
|
||
}
|
||
// SAFETY: if not linked, calling will be an unresolved symbol at link-time; we rely on link step to include ny_main.
|
||
let v = ny_main();
|
||
let exit_code: i64 = {
|
||
use nyash_rust::{box_trait::IntegerBox, runtime::host_handles as handles};
|
||
if v > 0 {
|
||
if let Some(obj) = handles::get(v as u64) {
|
||
if let Some(ib) = obj.as_any().downcast_ref::<IntegerBox>() {
|
||
ib.value as i64
|
||
} else {
|
||
// Avoid “raw integer vs handle id” collision:
|
||
// if the handle exists but isn't an IntegerBox, treat `v` as a raw i64.
|
||
v
|
||
}
|
||
} else {
|
||
v
|
||
}
|
||
} else {
|
||
v
|
||
}
|
||
};
|
||
// Print standardized result line for golden comparisons (can be silenced for tests)
|
||
let silent = std::env::var("NYASH_NYRT_SILENT_RESULT").ok().as_deref() == Some("1");
|
||
if !silent {
|
||
println!("Result: {}", exit_code);
|
||
}
|
||
// 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));
|
||
// ✂️ REMOVED: Legacy JIT handles::len() - part of 42% deletable functions
|
||
let handles = 0u64; // Placeholder: handles tracking removed with JIT archival
|
||
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
|
||
);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ✂️ REMOVED: Legacy JIT leak diagnostics - part of 42% deletable functions
|
||
// Leak diagnostics functionality removed with JIT archival
|
||
// handles::type_tally() no longer available in Plugin-First architecture
|
||
exit_code as i32
|
||
}
|
||
}
|