Phase 11-12: LLVM backend initial, semantics layer, plugin unification
Major changes: - LLVM backend initial implementation (compiler.rs, llvm mode) - Semantics layer integration in interpreter (operators.rs) - Phase 12 plugin architecture revision (3-layer system) - Builtin box removal preparation - MIR instruction set documentation (26→Core-15 migration) - Cross-backend testing infrastructure - Await/nowait syntax support New features: - LLVM AOT compilation support (--backend llvm) - Semantics layer for interpreter→VM flow - Tri-backend smoke tests - Plugin-only registry mode Bug fixes: - Interpreter plugin box arithmetic operations - Branch test returns incorrect values Documentation: - Phase 12 README.md updated with new plugin architecture - Removed obsolete NYIR proposals - Added LLVM test programs documentation Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
37
src/runtime/global_hooks.rs
Normal file
37
src/runtime/global_hooks.rs
Normal file
@ -0,0 +1,37 @@
|
||||
//! Lightweight global hooks for JIT/extern to reach GC/scheduler without owning NyashRuntime.
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use super::{gc::GcHooks, scheduler::Scheduler};
|
||||
|
||||
static GLOBAL_GC: OnceCell<RwLock<Option<Arc<dyn GcHooks>>>> = OnceCell::new();
|
||||
static GLOBAL_SCHED: OnceCell<RwLock<Option<Arc<dyn Scheduler>>>> = OnceCell::new();
|
||||
|
||||
fn gc_cell() -> &'static RwLock<Option<Arc<dyn GcHooks>>> { GLOBAL_GC.get_or_init(|| RwLock::new(None)) }
|
||||
fn sched_cell() -> &'static RwLock<Option<Arc<dyn Scheduler>>> { GLOBAL_SCHED.get_or_init(|| RwLock::new(None)) }
|
||||
|
||||
pub fn set_from_runtime(rt: &crate::runtime::nyash_runtime::NyashRuntime) {
|
||||
if let Ok(mut g) = gc_cell().write() { *g = Some(rt.gc.clone()); }
|
||||
if let Ok(mut s) = sched_cell().write() { *s = rt.scheduler.as_ref().cloned(); }
|
||||
}
|
||||
|
||||
pub fn set_gc(gc: Arc<dyn GcHooks>) { if let Ok(mut g) = gc_cell().write() { *g = Some(gc); } }
|
||||
pub fn set_scheduler(s: Arc<dyn Scheduler>) { if let Ok(mut w) = sched_cell().write() { *w = Some(s); } }
|
||||
|
||||
/// Perform a runtime safepoint and poll the scheduler if available.
|
||||
pub fn safepoint_and_poll() {
|
||||
if let Ok(g) = gc_cell().read() {
|
||||
if let Some(gc) = g.as_ref() { gc.safepoint(); }
|
||||
}
|
||||
if let Ok(s) = sched_cell().read() {
|
||||
if let Some(sched) = s.as_ref() { sched.poll(); }
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to schedule a task on the global scheduler. Returns true if scheduled.
|
||||
pub fn spawn_task(_name: &str, f: Box<dyn FnOnce() + 'static>) -> bool {
|
||||
// Minimal inline execution to avoid Send bounds; upgrade to true scheduling later
|
||||
f();
|
||||
true
|
||||
}
|
||||
@ -12,6 +12,8 @@ pub mod unified_registry;
|
||||
pub mod nyash_runtime;
|
||||
pub mod gc;
|
||||
pub mod scheduler;
|
||||
pub mod global_hooks;
|
||||
pub mod semantics;
|
||||
// pub mod plugin_box; // legacy - 古いPluginBox
|
||||
// pub mod plugin_loader; // legacy - Host VTable使用
|
||||
pub mod type_meta;
|
||||
|
||||
@ -476,14 +476,15 @@ impl PluginLoaderV2 {
|
||||
Ok(None)
|
||||
}
|
||||
("env.runtime", "checkpoint") => {
|
||||
// Minimal safepoint checkpoint stub (no-op)
|
||||
// Safepoint + scheduler poll via global hooks
|
||||
if std::env::var("NYASH_RUNTIME_CHECKPOINT_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[runtime.checkpoint] reached");
|
||||
}
|
||||
crate::runtime::global_hooks::safepoint_and_poll();
|
||||
Ok(None)
|
||||
}
|
||||
// Future/Await bridge (scaffold): maps MIR Future* to Box operations
|
||||
("env.future", "new") => {
|
||||
("env.future", "new") | ("env.future", "birth") => {
|
||||
// new(value) -> FutureBox(set to value)
|
||||
let fut = crate::boxes::future::FutureBox::new();
|
||||
if let Some(v) = args.get(0) { fut.set_result(v.clone_box()); }
|
||||
@ -512,6 +513,51 @@ impl PluginLoaderV2 {
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
("env.future", "spawn_instance") => {
|
||||
// spawn_instance(recv, method_name, args...) -> FutureBox
|
||||
// If a scheduler is available, schedule the call; else invoke synchronously.
|
||||
use crate::box_trait::{NyashBox, VoidBox, ErrorBox};
|
||||
let fut = crate::boxes::future::FutureBox::new();
|
||||
if args.len() >= 2 {
|
||||
if let Some(pb) = args[0].as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
let method_name = if let Some(sb) = args[1].as_any().downcast_ref::<crate::box_trait::StringBox>() { sb.value.clone() } else { args[1].to_string_box().value };
|
||||
// Clone boxes for scheduled call (same-thread scheduler; no Send bound)
|
||||
let tail: Vec<Box<dyn NyashBox>> = args.iter().skip(2).map(|a| a.clone_box()).collect();
|
||||
let recv_type = pb.box_type.clone();
|
||||
let recv_id = pb.instance_id();
|
||||
// Clones for fallback inline path
|
||||
let recv_type_inline = recv_type.clone();
|
||||
let method_name_inline = method_name.clone();
|
||||
let tail_inline: Vec<Box<dyn NyashBox>> = tail.iter().map(|a| a.clone_box()).collect();
|
||||
let fut_setter = fut.clone();
|
||||
let scheduled = crate::runtime::global_hooks::spawn_task("spawn_instance", Box::new(move || {
|
||||
let host = crate::runtime::get_global_plugin_host();
|
||||
let read_res = host.read();
|
||||
if let Ok(ro) = read_res {
|
||||
match ro.invoke_instance_method(&recv_type, &method_name, recv_id, &tail) {
|
||||
Ok(ret) => { if let Some(v) = ret { fut_setter.set_result(v); } else { fut_setter.set_result(Box::new(VoidBox::new())); } }
|
||||
Err(e) => { fut_setter.set_result(Box::new(ErrorBox::new("InvokeError", &format!("{}.{}: {:?}", recv_type, method_name, e)))); }
|
||||
}
|
||||
}
|
||||
}));
|
||||
if !scheduled {
|
||||
// Fallback: run inline synchronously
|
||||
let host = crate::runtime::get_global_plugin_host();
|
||||
let read_res = host.read();
|
||||
if let Ok(ro) = read_res {
|
||||
match ro.invoke_instance_method(&recv_type_inline, &method_name_inline, recv_id, &tail_inline) {
|
||||
Ok(ret) => { if let Some(v) = ret { fut.set_result(v); } else { fut.set_result(Box::new(VoidBox::new())); } }
|
||||
Err(e) => { fut.set_result(Box::new(ErrorBox::new("InvokeError", &format!("{}.{}: {:?}", recv_type_inline, method_name_inline, e)))); }
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(Some(Box::new(fut)));
|
||||
}
|
||||
}
|
||||
// Fallback: resolved future of first arg
|
||||
if let Some(v) = args.get(0) { fut.set_result(v.clone_box()); }
|
||||
Ok(Some(Box::new(fut)))
|
||||
}
|
||||
("env.canvas", _) => {
|
||||
eprintln!("[env.canvas] {} invoked (stub)", method_name);
|
||||
Ok(None)
|
||||
|
||||
97
src/runtime/semantics.rs
Normal file
97
src/runtime/semantics.rs
Normal file
@ -0,0 +1,97 @@
|
||||
/*!
|
||||
* Shared semantics for coercions and basic ops
|
||||
*
|
||||
* Goal: Unify Script→MIR→VM→AOT semantics by centralizing
|
||||
* string/number coercions and common operation ordering.
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, IntegerBox};
|
||||
|
||||
/// Try to unwrap InstanceBox and return inner if present
|
||||
fn maybe_unwrap_instance(b: &dyn NyashBox) -> &dyn NyashBox {
|
||||
if let Some(inst) = b.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
|
||||
if let Some(ref inner) = inst.inner_content { return inner.as_ref(); }
|
||||
}
|
||||
b
|
||||
}
|
||||
|
||||
/// Result.Ok(inner) → recurse helper
|
||||
fn maybe_unwrap_result_ok(b: &dyn NyashBox) -> &dyn NyashBox {
|
||||
if let Some(res) = b.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() {
|
||||
if let crate::boxes::result::NyashResultBox::Ok(inner) = res { return inner.as_ref(); }
|
||||
}
|
||||
b
|
||||
}
|
||||
|
||||
/// Best-effort string coercion used by all backends.
|
||||
pub fn coerce_to_string(b: &dyn NyashBox) -> Option<String> {
|
||||
let b = maybe_unwrap_instance(b);
|
||||
// Internal StringBox
|
||||
if let Some(s) = b.as_any().downcast_ref::<StringBox>() { return Some(s.value.clone()); }
|
||||
// Result.Ok recursion
|
||||
let b2 = maybe_unwrap_result_ok(b);
|
||||
if !std::ptr::eq(b2 as *const _, b as *const _) {
|
||||
if let Some(s) = coerce_to_string(b2) { return Some(s); }
|
||||
}
|
||||
|
||||
// Plugin StringBox: prefer toUtf8; fallback to toString
|
||||
if let Some(pb) = b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
// StringBox.toUtf8
|
||||
if pb.box_type == "StringBox" {
|
||||
let host = crate::runtime::get_global_plugin_host();
|
||||
let read_res = host.read();
|
||||
if let Ok(ro) = read_res {
|
||||
if let Ok(ret) = ro.invoke_instance_method("StringBox", "toUtf8", pb.instance_id(), &[]) {
|
||||
if let Some(vb) = ret {
|
||||
if let Some(sb2) = vb.as_any().downcast_ref::<StringBox>() { return Some(sb2.value.clone()); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// AnyBox.toString
|
||||
let host = crate::runtime::get_global_plugin_host();
|
||||
let read_res = host.read();
|
||||
if let Ok(ro) = read_res {
|
||||
if let Ok(ret) = ro.invoke_instance_method(&pb.box_type, "toString", pb.instance_id(), &[]) {
|
||||
if let Some(vb) = ret { if let Some(s) = coerce_to_string(vb.as_ref()) { return Some(s); } }
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Best-effort integer coercion used by all backends.
|
||||
pub fn coerce_to_i64(b: &dyn NyashBox) -> Option<i64> {
|
||||
let b = maybe_unwrap_instance(b);
|
||||
if let Some(i) = b.as_any().downcast_ref::<IntegerBox>() { return Some(i.value); }
|
||||
|
||||
// Plugin numeric getters
|
||||
if let Some(pb) = b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
// IntegerBox.get -> IntegerBox
|
||||
if pb.box_type == "IntegerBox" {
|
||||
let host = crate::runtime::get_global_plugin_host();
|
||||
let read_res = host.read();
|
||||
if let Ok(ro) = read_res {
|
||||
if let Ok(ret) = ro.invoke_instance_method("IntegerBox", "get", pb.instance_id(), &[]) {
|
||||
if let Some(vb) = ret { if let Some(ii) = vb.as_any().downcast_ref::<IntegerBox>() { return Some(ii.value); } }
|
||||
}
|
||||
}
|
||||
}
|
||||
// FloatBox.toDouble -> FloatBox
|
||||
if pb.box_type == "FloatBox" {
|
||||
let host = crate::runtime::get_global_plugin_host();
|
||||
let read_res = host.read();
|
||||
if let Ok(ro) = read_res {
|
||||
if let Ok(ret) = ro.invoke_instance_method("FloatBox", "toDouble", pb.instance_id(), &[]) {
|
||||
if let Some(vb) = ret { if let Some(fb) = vb.as_any().downcast_ref::<crate::boxes::FloatBox>() { return Some(fb.value as i64); } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback via string coercion -> parse
|
||||
if let Some(s) = coerce_to_string(b) {
|
||||
return s.trim().parse::<i64>().ok();
|
||||
}
|
||||
None
|
||||
}
|
||||
Reference in New Issue
Block a user