chore: Phase 25.1 完了 - LoopForm v2/Stage1 CLI/環境変数削減 + Phase 26-D からの変更
Phase 25.1 完了成果: - ✅ LoopForm v2 テスト・ドキュメント・コメント完備 - 4ケース(A/B/C/D)完全テストカバレッジ - 最小再現ケース作成(SSAバグ調査用) - SSOT文書作成(loopform_ssot.md) - 全ソースに [LoopForm] コメントタグ追加 - ✅ Stage-1 CLI デバッグ環境構築 - stage1_cli.hako 実装 - stage1_bridge.rs ブリッジ実装 - デバッグツール作成(stage1_debug.sh/stage1_minimal.sh) - アーキテクチャ改善提案文書 - ✅ 環境変数削減計画策定 - 25変数の完全調査・分類 - 6段階削減ロードマップ(25→5、80%削減) - 即時削除可能変数特定(NYASH_CONFIG/NYASH_DEBUG) Phase 26-D からの累積変更: - PHI実装改善(ExitPhiBuilder/HeaderPhiBuilder等) - MIRビルダーリファクタリング - 型伝播・最適化パス改善 - その他約300ファイルの累積変更 🎯 技術的成果: - SSAバグ根本原因特定(条件分岐内loop変数変更) - Region+next_iパターン適用完了(UsingCollectorBox等) - LoopFormパターン文書化・テスト化完了 - セルフホスティング基盤強化 Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: ChatGPT <noreply@openai.com> Co-Authored-By: Task Assistant <task@anthropic.com>
This commit is contained in:
@ -17,4 +17,3 @@ pub fn warn_nyash_toml_used_once() {
|
||||
"[deprecate] using nyash.toml; please rename to hako.toml",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -35,9 +35,13 @@ pub struct CountingGc {
|
||||
}
|
||||
|
||||
impl CountingGc {
|
||||
pub fn new() -> Self { Self::new_with_mode(crate::runtime::gc_mode::GcMode::RcCycle) }
|
||||
pub fn new() -> Self {
|
||||
Self::new_with_mode(crate::runtime::gc_mode::GcMode::RcCycle)
|
||||
}
|
||||
pub fn new_with_mode(mode: crate::runtime::gc_mode::GcMode) -> Self {
|
||||
Self { inner: crate::runtime::gc_controller::GcController::new(mode) }
|
||||
Self {
|
||||
inner: crate::runtime::gc_controller::GcController::new(mode),
|
||||
}
|
||||
}
|
||||
pub fn snapshot(&self) -> (u64, u64, u64) {
|
||||
self.inner.snapshot()
|
||||
|
||||
@ -84,8 +84,14 @@ impl GcHooks for GcController {
|
||||
if sp_hit || bytes_hit {
|
||||
// Record reason flags for diagnostics
|
||||
let mut flags: u64 = 0;
|
||||
if sp_hit { flags |= 1; self.collect_by_sp.fetch_add(1, Ordering::Relaxed); }
|
||||
if bytes_hit { flags |= 2; self.collect_by_alloc.fetch_add(1, Ordering::Relaxed); }
|
||||
if sp_hit {
|
||||
flags |= 1;
|
||||
self.collect_by_sp.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
if bytes_hit {
|
||||
flags |= 2;
|
||||
self.collect_by_alloc.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
self.trial_reason_last.store(flags, Ordering::Relaxed);
|
||||
self.run_trial_collection();
|
||||
}
|
||||
@ -114,8 +120,7 @@ impl GcHooks for GcController {
|
||||
}
|
||||
self.alloc_count.fetch_add(1, Ordering::Relaxed);
|
||||
self.alloc_bytes.fetch_add(bytes, Ordering::Relaxed);
|
||||
self.bytes_since_last
|
||||
.fetch_add(bytes, Ordering::Relaxed);
|
||||
self.bytes_since_last.fetch_add(bytes, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,5 +207,7 @@ impl GcController {
|
||||
pub fn trial_duration_last_ms(&self) -> u64 {
|
||||
self.trial_duration_last_ms.load(Ordering::Relaxed)
|
||||
}
|
||||
pub fn trial_reason_last_bits(&self) -> u64 { self.trial_reason_last.load(Ordering::Relaxed) }
|
||||
pub fn trial_reason_last_bits(&self) -> u64 {
|
||||
self.trial_reason_last.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,4 +31,3 @@ impl GcMode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -32,4 +32,3 @@ pub fn trace_children(obj: &dyn NyashBox, visit: &mut dyn FnMut(Arc<dyn NyashBox
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -152,7 +152,9 @@ pub fn pop_task_scope() {
|
||||
if st.scope_depth > 0 {
|
||||
st.scope_depth -= 1;
|
||||
}
|
||||
if st.scope_depth == 0 { do_join = true; }
|
||||
if st.scope_depth == 0 {
|
||||
do_join = true;
|
||||
}
|
||||
// Pop explicit group for this scope
|
||||
popped = st.group_stack.pop();
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
//! プラグインシステムとBox管理の中核
|
||||
|
||||
pub mod box_registry;
|
||||
pub mod deprecations;
|
||||
pub mod gc;
|
||||
pub mod gc_controller;
|
||||
pub mod gc_mode;
|
||||
@ -10,19 +11,18 @@ pub mod gc_trace;
|
||||
pub mod global_hooks;
|
||||
pub mod leak_tracker;
|
||||
pub mod nyash_runtime;
|
||||
pub mod observe; // Lightweight observability flags (OOB etc.)
|
||||
pub mod plugin_config;
|
||||
pub mod plugin_ffi_common;
|
||||
pub mod plugin_loader_unified;
|
||||
pub mod plugin_loader_v2;
|
||||
pub mod scheduler;
|
||||
pub mod semantics;
|
||||
pub mod unified_registry;
|
||||
pub mod provider_lock;
|
||||
pub mod provider_verify;
|
||||
pub mod observe; // Lightweight observability flags (OOB etc.)
|
||||
pub mod deprecations; // Deprecation warnings with warn-once guards
|
||||
// pub mod plugin_box; // legacy - 古いPluginBox
|
||||
// pub mod plugin_loader; // legacy - Host VTable使用
|
||||
pub mod scheduler;
|
||||
pub mod semantics;
|
||||
pub mod unified_registry; // Deprecation warnings with warn-once guards
|
||||
// pub mod plugin_box; // legacy - 古いPluginBox
|
||||
// pub mod plugin_loader; // legacy - Host VTable使用
|
||||
pub mod extern_registry; // ExternCall (env.*) 登録・診断用レジストリ
|
||||
pub mod host_api; // C ABI: plugins -> host 逆呼び出しAPI(TLSでVMに橋渡し)
|
||||
pub mod host_handles; // C ABI(TLV) 向け HostHandle レジストリ(ユーザー/内蔵Box受け渡し)
|
||||
|
||||
@ -19,4 +19,3 @@ pub fn mark_oob() {
|
||||
pub fn oob_seen() -> bool {
|
||||
OOB_SEEN.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
|
||||
@ -83,7 +83,12 @@ impl PluginHost {
|
||||
|
||||
/// Load a single library directly from path for `using kind="dylib"` autoload.
|
||||
/// Boxes list is best-effort (may be empty). When empty, TypeBox FFI is used to resolve metadata.
|
||||
pub fn load_library_direct(&self, lib_name: &str, path: &str, boxes: &[String]) -> BidResult<()> {
|
||||
pub fn load_library_direct(
|
||||
&self,
|
||||
lib_name: &str,
|
||||
path: &str,
|
||||
boxes: &[String],
|
||||
) -> BidResult<()> {
|
||||
// If caller didn't provide box names, try to infer from nyash_box.toml
|
||||
let inferred_boxes: Vec<String> = if boxes.is_empty() {
|
||||
let nyb = std::path::Path::new(path)
|
||||
@ -94,7 +99,11 @@ impl PluginHost {
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let effective_boxes: Vec<String> = if boxes.is_empty() { inferred_boxes } else { boxes.to_vec() };
|
||||
let effective_boxes: Vec<String> = if boxes.is_empty() {
|
||||
inferred_boxes
|
||||
} else {
|
||||
boxes.to_vec()
|
||||
};
|
||||
let def = crate::config::nyash_toml_v2::LibraryDefinition {
|
||||
boxes: effective_boxes.clone(),
|
||||
path: path.to_string(),
|
||||
@ -105,15 +114,29 @@ impl PluginHost {
|
||||
if l.config.is_none() {
|
||||
let mut cfg = NyashConfigV2 {
|
||||
libraries: std::collections::HashMap::new(),
|
||||
plugin_paths: crate::config::nyash_toml_v2::PluginPaths { search_paths: vec![] },
|
||||
plugin_paths: crate::config::nyash_toml_v2::PluginPaths {
|
||||
search_paths: vec![],
|
||||
},
|
||||
plugins: std::collections::HashMap::new(),
|
||||
box_types: std::collections::HashMap::new(),
|
||||
};
|
||||
cfg.libraries.insert(lib_name.to_string(), crate::config::nyash_toml_v2::LibraryDefinition { boxes: def.boxes.clone(), path: def.path.clone() });
|
||||
cfg.libraries.insert(
|
||||
lib_name.to_string(),
|
||||
crate::config::nyash_toml_v2::LibraryDefinition {
|
||||
boxes: def.boxes.clone(),
|
||||
path: def.path.clone(),
|
||||
},
|
||||
);
|
||||
l.config = Some(cfg);
|
||||
// No dedicated config file; keep config_path None and rely on box_specs fallback
|
||||
} else if let Some(cfg) = l.config.as_mut() {
|
||||
cfg.libraries.insert(lib_name.to_string(), crate::config::nyash_toml_v2::LibraryDefinition { boxes: def.boxes.clone(), path: def.path.clone() });
|
||||
cfg.libraries.insert(
|
||||
lib_name.to_string(),
|
||||
crate::config::nyash_toml_v2::LibraryDefinition {
|
||||
boxes: def.boxes.clone(),
|
||||
path: def.path.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
// Load the library now
|
||||
l.load_plugin_direct(lib_name, &def)?;
|
||||
@ -146,7 +169,8 @@ impl PluginHost {
|
||||
if let Some((lib_name, _lib_def)) = cfg.find_library_for_box(box_type) {
|
||||
if let Some(box_conf) = cfg.get_box_config(lib_name, box_type, &toml_value) {
|
||||
// Prefer config mapping; fallback to loader's TypeBox resolve(name)
|
||||
let (method_id, returns_result) = if let Some(m) = box_conf.methods.get(method_name) {
|
||||
let (method_id, returns_result) = if let Some(m) = box_conf.methods.get(method_name)
|
||||
{
|
||||
(m.method_id, m.returns_result)
|
||||
} else {
|
||||
let l = self.loader.read().unwrap();
|
||||
@ -177,7 +201,13 @@ impl PluginHost {
|
||||
if let Some(entry) = bm.get(method_name) {
|
||||
// Support both { method_id = N } and bare integer in the future
|
||||
let (method_id, returns_result) = if let Some(mid) = entry.get("method_id") {
|
||||
(mid.as_integer().unwrap_or(0) as u32, entry.get("returns_result").and_then(|b| b.as_bool()).unwrap_or(false))
|
||||
(
|
||||
mid.as_integer().unwrap_or(0) as u32,
|
||||
entry
|
||||
.get("returns_result")
|
||||
.and_then(|b| b.as_bool())
|
||||
.unwrap_or(false),
|
||||
)
|
||||
} else if let Some(mid) = entry.as_integer() {
|
||||
(mid as u32, false)
|
||||
} else {
|
||||
@ -266,7 +296,9 @@ impl PluginHost {
|
||||
}
|
||||
// Library-backed path
|
||||
if let Some((lib_name, _)) = cfg.find_library_for_box(box_type) {
|
||||
if let Some(box_conf) = cfg.get_box_config(lib_name, box_type, &toml_value) {
|
||||
if let Some(box_conf) =
|
||||
cfg.get_box_config(lib_name, box_type, &toml_value)
|
||||
{
|
||||
if let Some(m) = box_conf.methods.get(method_name) {
|
||||
return m.returns_result;
|
||||
}
|
||||
@ -331,28 +363,50 @@ impl PluginHost {
|
||||
/// 2) Top-level tables that look like box sections (have `type_id` or `methods`/`lifecycle`)
|
||||
fn infer_box_names_from_nyash_box(nyb_path: &std::path::Path) -> Vec<String> {
|
||||
let mut out: Vec<String> = Vec::new();
|
||||
if !nyb_path.exists() { return out; }
|
||||
let Ok(text) = std::fs::read_to_string(nyb_path) else { return out; };
|
||||
let Ok(doc) = toml::from_str::<toml::Value>(&text) else { return out; };
|
||||
if !nyb_path.exists() {
|
||||
return out;
|
||||
}
|
||||
let Ok(text) = std::fs::read_to_string(nyb_path) else {
|
||||
return out;
|
||||
};
|
||||
let Ok(doc) = toml::from_str::<toml::Value>(&text) else {
|
||||
return out;
|
||||
};
|
||||
// 1) explicit provides
|
||||
if let Some(arr) = doc.get("provides").and_then(|v| v.get("boxes")).and_then(|v| v.as_array()) {
|
||||
for v in arr { if let Some(s) = v.as_str() { out.push(s.to_string()); } }
|
||||
out.sort(); out.dedup();
|
||||
if !out.is_empty() { return out; }
|
||||
if let Some(arr) = doc
|
||||
.get("provides")
|
||||
.and_then(|v| v.get("boxes"))
|
||||
.and_then(|v| v.as_array())
|
||||
{
|
||||
for v in arr {
|
||||
if let Some(s) = v.as_str() {
|
||||
out.push(s.to_string());
|
||||
}
|
||||
}
|
||||
out.sort();
|
||||
out.dedup();
|
||||
if !out.is_empty() {
|
||||
return out;
|
||||
}
|
||||
}
|
||||
// 2) heuristic: tables with type_id or lifecycle/methods
|
||||
if let Some(tbl) = doc.as_table() {
|
||||
for (k, v) in tbl.iter() {
|
||||
if k == "box" || k == "implementation" || k == "artifacts" || k == "provides" { continue; }
|
||||
if k == "box" || k == "implementation" || k == "artifacts" || k == "provides" {
|
||||
continue;
|
||||
}
|
||||
if let Some(t) = v.as_table() {
|
||||
let looks_like_box = t.get("type_id").is_some()
|
||||
|| t.get("methods").is_some()
|
||||
|| t.get("lifecycle").is_some();
|
||||
if looks_like_box { out.push(k.clone()); }
|
||||
if looks_like_box {
|
||||
out.push(k.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out.sort(); out.dedup();
|
||||
out.sort();
|
||||
out.dedup();
|
||||
out
|
||||
}
|
||||
|
||||
|
||||
@ -245,16 +245,21 @@ pub fn handle_box_introspect(
|
||||
"kind" => {
|
||||
let value = args.get(0).ok_or(BidError::PluginError)?;
|
||||
let info = build_box_info(value.as_ref());
|
||||
if std::env::var("NYASH_BOX_INTROSPECT_TRACE")
|
||||
.ok()
|
||||
.as_deref() == Some("1")
|
||||
{
|
||||
if std::env::var("NYASH_BOX_INTROSPECT_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[box_introspect:plugin] kind={} type_name={} is_map={} is_array={}",
|
||||
info.get(Box::new(StringBox::new("kind"))).to_string_box().value,
|
||||
info.get(Box::new(StringBox::new("type_name"))).to_string_box().value,
|
||||
info.get(Box::new(StringBox::new("is_map"))).to_string_box().value,
|
||||
info.get(Box::new(StringBox::new("is_array"))).to_string_box().value,
|
||||
info.get(Box::new(StringBox::new("kind")))
|
||||
.to_string_box()
|
||||
.value,
|
||||
info.get(Box::new(StringBox::new("type_name")))
|
||||
.to_string_box()
|
||||
.value,
|
||||
info.get(Box::new(StringBox::new("is_map")))
|
||||
.to_string_box()
|
||||
.value,
|
||||
info.get(Box::new(StringBox::new("is_array")))
|
||||
.to_string_box()
|
||||
.value,
|
||||
);
|
||||
}
|
||||
Ok(Some(Box::new(info)))
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
use crate::bid::{BidError, BidResult};
|
||||
use crate::box_trait::NyashBox;
|
||||
use crate::runtime::plugin_loader_v2::enabled::PluginLoaderV2;
|
||||
use std::sync::Arc;
|
||||
use std::env;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn dbg_on() -> bool {
|
||||
std::env::var("PLUGIN_DEBUG").is_ok()
|
||||
@ -49,25 +49,36 @@ impl PluginLoaderV2 {
|
||||
}
|
||||
|
||||
// Optional C-core probe (design): emit tag and optionally call into c-core when enabled
|
||||
if env::var("HAKO_C_CORE_ENABLE").ok().as_deref() == Some("1") && should_route_ccore(box_type, method_name) {
|
||||
if env::var("HAKO_C_CORE_ENABLE").ok().as_deref() == Some("1")
|
||||
&& should_route_ccore(box_type, method_name)
|
||||
{
|
||||
eprintln!("[c-core:invoke:{}.{}]", box_type, method_name);
|
||||
#[cfg(feature = "c-core")]
|
||||
{
|
||||
// MapBox.set: call C-core stub (no-op) with available info
|
||||
if box_type == "MapBox" && method_name == "set" {
|
||||
let key = args.get(0).map(|b| b.to_string_box().value).unwrap_or_default();
|
||||
let val = args.get(1).map(|b| b.to_string_box().value).unwrap_or_default();
|
||||
let key = args
|
||||
.get(0)
|
||||
.map(|b| b.to_string_box().value)
|
||||
.unwrap_or_default();
|
||||
let val = args
|
||||
.get(1)
|
||||
.map(|b| b.to_string_box().value)
|
||||
.unwrap_or_default();
|
||||
let _ = nyash_c_core::core_map_set(type_id as i32, instance_id, &key, &val);
|
||||
} else if box_type == "ArrayBox" && method_name == "push" {
|
||||
// For design stage, pass 0 (we don't rely on c-core result)
|
||||
let _ = nyash_c_core::core_array_push(type_id as i32, instance_id, 0);
|
||||
} else if box_type == "ArrayBox" && method_name == "get" {
|
||||
let _ = nyash_c_core::core_array_get(type_id as i32, instance_id, 0);
|
||||
} else if box_type == "ArrayBox" && (method_name == "size" || method_name == "len" || method_name == "length") {
|
||||
} else if box_type == "ArrayBox"
|
||||
&& (method_name == "size" || method_name == "len" || method_name == "length")
|
||||
{
|
||||
let _ = nyash_c_core::core_array_len(type_id as i32, instance_id);
|
||||
} else {
|
||||
// Generic probe
|
||||
let _ = nyash_c_core::core_probe_invoke(box_type, method_name, args.len() as i32);
|
||||
let _ =
|
||||
nyash_c_core::core_probe_invoke(box_type, method_name, args.len() as i32);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -120,8 +131,12 @@ fn should_trace_tlv_shim(box_type: &str, method: &str) -> bool {
|
||||
let key = format!("{}.{}", box_type, method);
|
||||
for pat in flt.split(',') {
|
||||
let p = pat.trim();
|
||||
if p.is_empty() { continue; }
|
||||
if p == method || p == key { return true; }
|
||||
if p.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if p == method || p == key {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -135,8 +150,12 @@ fn should_trace_cwrap(box_type: &str, method: &str) -> bool {
|
||||
let key = format!("{}.{}", box_type, method);
|
||||
for pat in flt.split(',') {
|
||||
let p = pat.trim();
|
||||
if p.is_empty() { continue; }
|
||||
if p == method || p == key { return true; }
|
||||
if p.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if p == method || p == key {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -149,8 +168,12 @@ fn should_trace_call(target: &str, method: &str) -> bool {
|
||||
let key = format!("{}.{}", target, method);
|
||||
for pat in flt.split(',') {
|
||||
let p = pat.trim();
|
||||
if p.is_empty() { continue; }
|
||||
if p == method || p == key { return true; }
|
||||
if p.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if p == method || p == key {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -162,8 +185,12 @@ fn should_route_ccore(box_type: &str, method: &str) -> bool {
|
||||
let key = format!("{}.{}", box_type, method);
|
||||
for pat in flt.split(',') {
|
||||
let p = pat.trim();
|
||||
if p.is_empty() { continue; }
|
||||
if p == method || p == key { return true; }
|
||||
if p.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if p == method || p == key {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -203,9 +230,7 @@ fn resolve_type_info(loader: &PluginLoaderV2, box_type: &str) -> BidResult<(Stri
|
||||
|
||||
/// Decode TLV result into a NyashBox
|
||||
fn decode_tlv_result(box_type: &str, data: &[u8]) -> BidResult<Option<Box<dyn NyashBox>>> {
|
||||
if let Some((tag, _sz, payload)) =
|
||||
crate::runtime::plugin_ffi_common::decode::tlv_first(data)
|
||||
{
|
||||
if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(data) {
|
||||
let bx: Box<dyn NyashBox> = match tag {
|
||||
1 => Box::new(crate::box_trait::BoolBox::new(
|
||||
crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false),
|
||||
|
||||
@ -2,7 +2,10 @@
|
||||
|
||||
use crate::bid::{BidError, BidResult};
|
||||
use crate::box_trait::NyashBox;
|
||||
use crate::runtime::plugin_loader_v2::enabled::{PluginLoaderV2, types::{PluginBoxV2, PluginHandleInner}};
|
||||
use crate::runtime::plugin_loader_v2::enabled::{
|
||||
types::{PluginBoxV2, PluginHandleInner},
|
||||
PluginLoaderV2,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
fn dbg_on() -> bool {
|
||||
@ -88,10 +91,7 @@ impl PluginLoaderV2 {
|
||||
}
|
||||
|
||||
/// Resolve box IDs (type_id, birth_id, fini_id) from configuration or specs
|
||||
fn resolve_box_ids(
|
||||
loader: &PluginLoaderV2,
|
||||
box_type: &str,
|
||||
) -> BidResult<(u32, u32, Option<u32>)> {
|
||||
fn resolve_box_ids(loader: &PluginLoaderV2, box_type: &str) -> BidResult<(u32, u32, Option<u32>)> {
|
||||
let (mut type_id_opt, mut birth_id_opt, mut fini_id) = (None, None, None);
|
||||
|
||||
// Try config mapping first (when available)
|
||||
@ -138,4 +138,4 @@ fn resolve_box_ids(
|
||||
let birth_id = birth_id_opt.ok_or(BidError::InvalidMethod)?;
|
||||
|
||||
Ok((type_id, birth_id, fini_id))
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,10 +4,13 @@ use crate::runtime::plugin_loader_v2::enabled::{errors, host_bridge, types};
|
||||
|
||||
pub(super) fn prebirth_singletons(loader: &PluginLoaderV2) -> BidResult<()> {
|
||||
let config = loader.config.as_ref().ok_or(BidError::PluginError)?;
|
||||
let cfg_path = loader
|
||||
.config_path
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| if std::path::Path::new("hako.toml").exists() { "hako.toml" } else { "nyash.toml" });
|
||||
let cfg_path = loader.config_path.as_deref().unwrap_or_else(|| {
|
||||
if std::path::Path::new("hako.toml").exists() {
|
||||
"hako.toml"
|
||||
} else {
|
||||
"nyash.toml"
|
||||
}
|
||||
});
|
||||
if cfg_path == "nyash.toml" && !std::path::Path::new("hako.toml").exists() {
|
||||
crate::runtime::deprecations::warn_nyash_toml_used_once();
|
||||
}
|
||||
@ -38,10 +41,13 @@ pub(super) fn ensure_singleton_handle(
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
let cfg_path = loader
|
||||
.config_path
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| if std::path::Path::new("hako.toml").exists() { "hako.toml" } else { "nyash.toml" });
|
||||
let cfg_path = loader.config_path.as_deref().unwrap_or_else(|| {
|
||||
if std::path::Path::new("hako.toml").exists() {
|
||||
"hako.toml"
|
||||
} else {
|
||||
"nyash.toml"
|
||||
}
|
||||
});
|
||||
if cfg_path == "nyash.toml" && !std::path::Path::new("hako.toml").exists() {
|
||||
crate::runtime::deprecations::warn_nyash_toml_used_once();
|
||||
}
|
||||
|
||||
@ -11,17 +11,22 @@ impl PluginLoaderV2 {
|
||||
pub(crate) fn resolve_method_id(&self, box_type: &str, method_name: &str) -> BidResult<u32> {
|
||||
// First try config mapping
|
||||
if let Some(cfg) = self.config.as_ref() {
|
||||
let cfg_path = self
|
||||
.config_path
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| if std::path::Path::new("hako.toml").exists() { "hako.toml" } else { "nyash.toml" });
|
||||
let cfg_path = self.config_path.as_deref().unwrap_or_else(|| {
|
||||
if std::path::Path::new("hako.toml").exists() {
|
||||
"hako.toml"
|
||||
} else {
|
||||
"nyash.toml"
|
||||
}
|
||||
});
|
||||
if cfg_path == "nyash.toml" && !std::path::Path::new("hako.toml").exists() {
|
||||
crate::runtime::deprecations::warn_nyash_toml_used_once();
|
||||
}
|
||||
|
||||
// Load and parse TOML
|
||||
let toml_content = std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?;
|
||||
let toml_value: toml::Value = toml::from_str(&toml_content).map_err(|_| BidError::PluginError)?;
|
||||
let toml_content =
|
||||
std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?;
|
||||
let toml_value: toml::Value =
|
||||
toml::from_str(&toml_content).map_err(|_| BidError::PluginError)?;
|
||||
|
||||
// Find library for box
|
||||
if let Some((lib_name, _)) = cfg.find_library_for_box(box_type) {
|
||||
@ -75,10 +80,13 @@ impl PluginLoaderV2 {
|
||||
/// Check if a method returns a Result type
|
||||
pub fn method_returns_result(&self, box_type: &str, method_name: &str) -> bool {
|
||||
if let Some(cfg) = self.config.as_ref() {
|
||||
let cfg_path = self
|
||||
.config_path
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| if std::path::Path::new("hako.toml").exists() { "hako.toml" } else { "nyash.toml" });
|
||||
let cfg_path = self.config_path.as_deref().unwrap_or_else(|| {
|
||||
if std::path::Path::new("hako.toml").exists() {
|
||||
"hako.toml"
|
||||
} else {
|
||||
"nyash.toml"
|
||||
}
|
||||
});
|
||||
if cfg_path == "nyash.toml" && !std::path::Path::new("hako.toml").exists() {
|
||||
crate::runtime::deprecations::warn_nyash_toml_used_once();
|
||||
}
|
||||
@ -86,7 +94,8 @@ impl PluginLoaderV2 {
|
||||
if let Ok(toml_content) = std::fs::read_to_string(cfg_path) {
|
||||
if let Ok(toml_value) = toml::from_str::<toml::Value>(&toml_content) {
|
||||
if let Some((lib_name, _)) = cfg.find_library_for_box(box_type) {
|
||||
if let Some(box_conf) = cfg.get_box_config(lib_name, box_type, &toml_value) {
|
||||
if let Some(box_conf) = cfg.get_box_config(lib_name, box_type, &toml_value)
|
||||
{
|
||||
if let Some(method_spec) = box_conf.methods.get(method_name) {
|
||||
return method_spec.returns_result;
|
||||
}
|
||||
@ -107,10 +116,13 @@ impl PluginLoaderV2 {
|
||||
method_name: &str,
|
||||
) -> BidResult<(u32, u32, bool)> {
|
||||
let cfg = self.config.as_ref().ok_or(BidError::PluginError)?;
|
||||
let cfg_path = self
|
||||
.config_path
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| if std::path::Path::new("hako.toml").exists() { "hako.toml" } else { "nyash.toml" });
|
||||
let cfg_path = self.config_path.as_deref().unwrap_or_else(|| {
|
||||
if std::path::Path::new("hako.toml").exists() {
|
||||
"hako.toml"
|
||||
} else {
|
||||
"nyash.toml"
|
||||
}
|
||||
});
|
||||
if cfg_path == "nyash.toml" && !std::path::Path::new("hako.toml").exists() {
|
||||
crate::runtime::deprecations::warn_nyash_toml_used_once();
|
||||
}
|
||||
|
||||
@ -8,13 +8,13 @@ mod loader;
|
||||
mod method_resolver;
|
||||
mod types;
|
||||
|
||||
pub use extern_functions::handle_box_introspect;
|
||||
pub use globals::{get_global_loader_v2, init_global_loader_v2, shutdown_plugins_v2};
|
||||
pub use loader::PluginLoaderV2;
|
||||
pub use types::{
|
||||
construct_plugin_box, make_plugin_box_v2, NyashTypeBoxFfi, PluginBoxMetadata, PluginBoxV2,
|
||||
PluginHandleInner,
|
||||
};
|
||||
pub use extern_functions::handle_box_introspect;
|
||||
|
||||
pub fn metadata_for_type_id(type_id: u32) -> Option<PluginBoxMetadata> {
|
||||
let loader = get_global_loader_v2();
|
||||
|
||||
@ -5,29 +5,38 @@
|
||||
* 既定では挙動を変えず、環境変数により警告/エラー化のみ可能にする。
|
||||
*/
|
||||
|
||||
use crate::boxes::file::provider::{FileCaps, FileIo};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use crate::boxes::file::provider::{FileCaps, FileIo};
|
||||
|
||||
static LOCKED: AtomicBool = AtomicBool::new(false);
|
||||
static WARN_ONCE: OnceLock<()> = OnceLock::new();
|
||||
static FILEBOX_PROVIDER: OnceLock<Arc<dyn FileIo>> = OnceLock::new();
|
||||
|
||||
/// Return true when providers are locked
|
||||
pub fn is_locked() -> bool { LOCKED.load(Ordering::Relaxed) }
|
||||
pub fn is_locked() -> bool {
|
||||
LOCKED.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Lock providers (idempotent)
|
||||
pub fn lock_providers() { LOCKED.store(true, Ordering::Relaxed); }
|
||||
pub fn lock_providers() {
|
||||
LOCKED.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Guard called before creating a new box instance.
|
||||
/// Default: no-op. When NYASH_PROVIDER_LOCK_STRICT=1, returns Err if not locked.
|
||||
/// When NYASH_PROVIDER_LOCK_WARN=1, prints a warning once.
|
||||
pub fn guard_before_new_box(box_type: &str) -> Result<(), String> {
|
||||
if is_locked() { return Ok(()); }
|
||||
if is_locked() {
|
||||
return Ok(());
|
||||
}
|
||||
let strict = std::env::var("NYASH_PROVIDER_LOCK_STRICT").ok().as_deref() == Some("1");
|
||||
let warn = std::env::var("NYASH_PROVIDER_LOCK_WARN").ok().as_deref() == Some("1");
|
||||
if strict {
|
||||
return Err(format!("E_PROVIDER_NOT_LOCKED: attempted to create '{}' before Provider Lock", box_type));
|
||||
return Err(format!(
|
||||
"E_PROVIDER_NOT_LOCKED: attempted to create '{}' before Provider Lock",
|
||||
box_type
|
||||
));
|
||||
}
|
||||
if warn {
|
||||
// Print once per process
|
||||
@ -40,7 +49,8 @@ pub fn guard_before_new_box(box_type: &str) -> Result<(), String> {
|
||||
|
||||
/// Set the global FileBox provider (can only be called once)
|
||||
pub fn set_filebox_provider(provider: Arc<dyn FileIo>) -> Result<(), String> {
|
||||
FILEBOX_PROVIDER.set(provider)
|
||||
FILEBOX_PROVIDER
|
||||
.set(provider)
|
||||
.map_err(|_| "FileBox provider already set".to_string())
|
||||
}
|
||||
|
||||
|
||||
@ -26,7 +26,9 @@ fn parse_required_methods(spec: &str) -> HashMap<String, Vec<String>> {
|
||||
let mut map = HashMap::new();
|
||||
for part in spec.split(';') {
|
||||
let p = part.trim();
|
||||
if p.is_empty() { continue; }
|
||||
if p.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if let Some((ty, rest)) = p.split_once(':') {
|
||||
let methods: Vec<String> = rest
|
||||
.split(',')
|
||||
@ -44,15 +46,30 @@ fn parse_required_methods(spec: &str) -> HashMap<String, Vec<String>> {
|
||||
|
||||
fn load_required_methods_from_toml() -> HashMap<String, Vec<String>> {
|
||||
let mut map: HashMap<String, Vec<String>> = HashMap::new();
|
||||
let text = match std::fs::read_to_string("nyash.toml") { Ok(s) => s, Err(_) => return map };
|
||||
let doc: toml::Value = match toml::from_str(&text) { Ok(v) => v, Err(_) => return map };
|
||||
let text = match std::fs::read_to_string("nyash.toml") {
|
||||
Ok(s) => s,
|
||||
Err(_) => return map,
|
||||
};
|
||||
let doc: toml::Value = match toml::from_str(&text) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return map,
|
||||
};
|
||||
|
||||
// Helper: add entry if array-of-strings
|
||||
let mut add_arr = |ty: &str, arr: &toml::Value| {
|
||||
if let Some(a) = arr.as_array() {
|
||||
let mut v: Vec<String> = Vec::new();
|
||||
for e in a { if let Some(s) = e.as_str() { let s = s.trim(); if !s.is_empty() { v.push(s.to_string()); } } }
|
||||
if !v.is_empty() { map.insert(ty.to_string(), v); }
|
||||
for e in a {
|
||||
if let Some(s) = e.as_str() {
|
||||
let s = s.trim();
|
||||
if !s.is_empty() {
|
||||
v.push(s.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
if !v.is_empty() {
|
||||
map.insert(ty.to_string(), v);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -61,8 +78,15 @@ fn load_required_methods_from_toml() -> HashMap<String, Vec<String>> {
|
||||
if let Some(req) = vrfy.get("required_methods") {
|
||||
if let Some(tbl) = req.as_table() {
|
||||
for (k, v) in tbl.iter() {
|
||||
if v.is_array() { add_arr(k, v); continue; }
|
||||
if let Some(t) = v.as_table() { if let Some(m) = t.get("methods") { add_arr(k, m); } }
|
||||
if v.is_array() {
|
||||
add_arr(k, v);
|
||||
continue;
|
||||
}
|
||||
if let Some(t) = v.as_table() {
|
||||
if let Some(m) = t.get("methods") {
|
||||
add_arr(k, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -72,7 +96,11 @@ fn load_required_methods_from_toml() -> HashMap<String, Vec<String>> {
|
||||
if let Some(types) = doc.get("types") {
|
||||
if let Some(tbl) = types.as_table() {
|
||||
for (k, v) in tbl.iter() {
|
||||
if let Some(t) = v.as_table() { if let Some(m) = t.get("required_methods") { add_arr(k, m); } }
|
||||
if let Some(t) = v.as_table() {
|
||||
if let Some(m) = t.get("required_methods") {
|
||||
add_arr(k, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,16 +111,22 @@ fn load_required_methods_from_toml() -> HashMap<String, Vec<String>> {
|
||||
pub fn verify_from_env() -> Result<(), String> {
|
||||
let mode = std::env::var("NYASH_PROVIDER_VERIFY").unwrap_or_default();
|
||||
let mode = mode.to_ascii_lowercase();
|
||||
if !(mode == "warn" || mode == "strict") { return Ok(()); }
|
||||
if !(mode == "warn" || mode == "strict") {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Merge: nyash.toml + env override
|
||||
let mut req = load_required_methods_from_toml();
|
||||
let spec = std::env::var("NYASH_VERIFY_REQUIRED_METHODS").unwrap_or_default();
|
||||
if !spec.trim().is_empty() {
|
||||
let env_map = parse_required_methods(&spec);
|
||||
for (k, v) in env_map { req.entry(k).or_default().extend(v); }
|
||||
for (k, v) in env_map {
|
||||
req.entry(k).or_default().extend(v);
|
||||
}
|
||||
}
|
||||
if req.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
if req.is_empty() { return Ok(()); }
|
||||
|
||||
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host();
|
||||
let host = host.read().unwrap();
|
||||
@ -107,11 +141,18 @@ pub fn verify_from_env() -> Result<(), String> {
|
||||
}
|
||||
}
|
||||
|
||||
if failures.is_empty() { return Ok(()); }
|
||||
if failures.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let msg = format!(
|
||||
"Provider verify failed ({}): missing methods: {}",
|
||||
mode,
|
||||
failures.join(", ")
|
||||
);
|
||||
if mode == "warn" { eprintln!("[provider-verify][warn] {}", msg); Ok(()) } else { Err(msg) }
|
||||
if mode == "warn" {
|
||||
eprintln!("[provider-verify][warn] {}", msg);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(msg)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user