feat(parser): Phase 285A1.4 & A1.5 - Weak field sugar + Parser hang fix
A1.4: Add sugar syntax `public weak parent` ≡ `public { weak parent }`
A1.5: Fix parser hang on unsupported `param: Type` syntax
Key changes:
- A1.4: Extend visibility parser to handle weak modifier (fields.rs)
- A1.5: Shared helper `parse_param_name_list()` with progress-zero detection
- A1.5: Fix 6 vulnerable parameter parsing loops (methods, constructors, functions)
- Tests: Sugar syntax (OK/NG), parser hang (timeout-based)
- Docs: lifecycle.md, EBNF.md, phase-285a1-boxification.md
Additional changes:
- weak() builtin implementation (handlers/weak.rs)
- Leak tracking improvements (leak_tracker.rs)
- Documentation updates (lifecycle, types, memory-finalization, etc.)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -511,6 +511,14 @@ pub extern "C" fn nyrt_host_call_slot(
|
||||
crate::backend::vm::VMValue::Void => {
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
// Phase 285A0: WeakBox upgrade or void
|
||||
crate::backend::vm::VMValue::WeakBox(w) => {
|
||||
if let Some(arc) = w.upgrade() {
|
||||
arc.share_box()
|
||||
} else {
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
}
|
||||
};
|
||||
let out = map.has(key_box);
|
||||
let vmv = crate::backend::vm::VMValue::from_nyash_box(out);
|
||||
@ -539,6 +547,14 @@ pub extern "C" fn nyrt_host_call_slot(
|
||||
crate::backend::vm::VMValue::Void => {
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
// Phase 285A0: WeakBox upgrade or void
|
||||
crate::backend::vm::VMValue::WeakBox(w) => {
|
||||
if let Some(arc) = w.upgrade() {
|
||||
arc.share_box()
|
||||
} else {
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
}
|
||||
};
|
||||
let out = map.get(key_box);
|
||||
let vmv = crate::backend::vm::VMValue::from_nyash_box(out);
|
||||
@ -567,6 +583,14 @@ pub extern "C" fn nyrt_host_call_slot(
|
||||
crate::backend::vm::VMValue::Void => {
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
// Phase 285A0: WeakBox upgrade or void
|
||||
crate::backend::vm::VMValue::WeakBox(w) => {
|
||||
if let Some(arc) = w.upgrade() {
|
||||
arc.share_box()
|
||||
} else {
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
}
|
||||
};
|
||||
let val_box: Box<dyn NyashBox> = match argv[1].clone() {
|
||||
crate::backend::vm::VMValue::Integer(i) => {
|
||||
@ -586,6 +610,14 @@ pub extern "C" fn nyrt_host_call_slot(
|
||||
crate::backend::vm::VMValue::Void => {
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
// Phase 285A0: WeakBox upgrade or void
|
||||
crate::backend::vm::VMValue::WeakBox(w) => {
|
||||
if let Some(arc) = w.upgrade() {
|
||||
arc.share_box()
|
||||
} else {
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
}
|
||||
};
|
||||
let out = map.set(key_box, val_box);
|
||||
let vmv = crate::backend::vm::VMValue::from_nyash_box(out);
|
||||
|
||||
@ -1,10 +1,41 @@
|
||||
//! Leak Tracker - Exit-time diagnostics for strong references still held
|
||||
//!
|
||||
//! Phase 285: Extended to report all global roots (modules, host_handles, plugin boxes).
|
||||
//!
|
||||
//! ## Environment Variable
|
||||
//!
|
||||
//! - `NYASH_LEAK_LOG=1` - Summary counts only
|
||||
//! - `NYASH_LEAK_LOG=2` - Verbose (include names/entries, truncated to first 10)
|
||||
//!
|
||||
//! ## Output Format
|
||||
//!
|
||||
//! ```text
|
||||
//! [lifecycle/leak] Roots still held at exit:
|
||||
//! [lifecycle/leak] modules: 3
|
||||
//! [lifecycle/leak] host_handles: 5
|
||||
//! [lifecycle/leak] plugin_boxes: 2
|
||||
//! ```
|
||||
|
||||
use crate::runtime::get_global_ring0;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
|
||||
static ENABLED: Lazy<bool> =
|
||||
Lazy::new(|| std::env::var("NYASH_LEAK_LOG").unwrap_or_default() == "1");
|
||||
/// Leak log level: 0 = off, 1 = summary, 2 = verbose
|
||||
static LEVEL: Lazy<u8> = Lazy::new(|| {
|
||||
match std::env::var("NYASH_LEAK_LOG")
|
||||
.unwrap_or_default()
|
||||
.as_str()
|
||||
{
|
||||
"1" => 1,
|
||||
"2" => 2,
|
||||
_ => 0,
|
||||
}
|
||||
});
|
||||
|
||||
/// Backward compatibility: enabled if level >= 1
|
||||
static ENABLED: Lazy<bool> = Lazy::new(|| *LEVEL >= 1);
|
||||
|
||||
static LEAKS: Lazy<Mutex<HashMap<(String, u32), &'static str>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
@ -38,21 +69,97 @@ impl Drop for Reporter {
|
||||
if !*ENABLED {
|
||||
return;
|
||||
}
|
||||
let m = LEAKS.lock().unwrap();
|
||||
if m.is_empty() {
|
||||
return;
|
||||
}
|
||||
get_global_ring0().log.warn(&format!(
|
||||
"[leak] Detected {} non-finalized plugin boxes:",
|
||||
m.len()
|
||||
));
|
||||
for ((ty, id), _) in m.iter() {
|
||||
get_global_ring0().log.warn(&format!(
|
||||
" - {}(id={}) not finalized (missing fini or scope)",
|
||||
ty, id
|
||||
));
|
||||
}
|
||||
emit_leak_report();
|
||||
}
|
||||
}
|
||||
|
||||
static REPORTER: Lazy<Reporter> = Lazy::new(|| Reporter);
|
||||
|
||||
/// Emit exit-time leak report (Phase 285)
|
||||
///
|
||||
/// Called automatically on program exit via Reporter::drop.
|
||||
/// Can also be called manually for testing.
|
||||
pub fn emit_leak_report() {
|
||||
let level = *LEVEL;
|
||||
if level == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let ring0 = get_global_ring0();
|
||||
|
||||
// Collect root counts
|
||||
let modules = crate::runtime::modules_registry::snapshot_names_and_strings();
|
||||
let host_handles = crate::runtime::host_handles::snapshot();
|
||||
let plugin_boxes = LEAKS.lock().map(|m| m.len()).unwrap_or(0);
|
||||
|
||||
let modules_count = modules.len();
|
||||
let host_handles_count = host_handles.len();
|
||||
|
||||
// Only print if there's something to report
|
||||
if modules_count == 0 && host_handles_count == 0 && plugin_boxes == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// Summary header
|
||||
ring0
|
||||
.log
|
||||
.warn("[lifecycle/leak] Roots still held at exit:");
|
||||
|
||||
// Summary counts
|
||||
if modules_count > 0 {
|
||||
ring0
|
||||
.log
|
||||
.warn(&format!("[lifecycle/leak] modules: {}", modules_count));
|
||||
}
|
||||
if host_handles_count > 0 {
|
||||
ring0.log.warn(&format!(
|
||||
"[lifecycle/leak] host_handles: {}",
|
||||
host_handles_count
|
||||
));
|
||||
}
|
||||
if plugin_boxes > 0 {
|
||||
ring0
|
||||
.log
|
||||
.warn(&format!("[lifecycle/leak] plugin_boxes: {}", plugin_boxes));
|
||||
}
|
||||
|
||||
// Verbose details (level 2)
|
||||
if level >= 2 {
|
||||
const MAX_ENTRIES: usize = 10;
|
||||
|
||||
// Module names
|
||||
if !modules.is_empty() {
|
||||
ring0.log.warn("[lifecycle/leak] module names:");
|
||||
for (i, (name, _value)) in modules.iter().take(MAX_ENTRIES).enumerate() {
|
||||
ring0
|
||||
.log
|
||||
.warn(&format!("[lifecycle/leak] [{}] {}", i, name));
|
||||
}
|
||||
if modules.len() > MAX_ENTRIES {
|
||||
ring0.log.warn(&format!(
|
||||
"[lifecycle/leak] ... and {} more",
|
||||
modules.len() - MAX_ENTRIES
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Plugin box details
|
||||
if plugin_boxes > 0 {
|
||||
ring0.log.warn("[lifecycle/leak] plugin box details:");
|
||||
if let Ok(m) = LEAKS.lock() {
|
||||
for (i, ((ty, id), _)) in m.iter().take(MAX_ENTRIES).enumerate() {
|
||||
ring0.log.warn(&format!(
|
||||
"[lifecycle/leak] [{}] {}(id={}) not finalized",
|
||||
i, ty, id
|
||||
));
|
||||
}
|
||||
if m.len() > MAX_ENTRIES {
|
||||
ring0.log.warn(&format!(
|
||||
"[lifecycle/leak] ... and {} more",
|
||||
m.len() - MAX_ENTRIES
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user