feat(phase88): Ring0Context Skeleton implementation

Phase 88: OS API abstraction layer implementation

**Implementation**:
- Ring0Context module added (4 files)
  - mod.rs: Public API, global initialization (OnceLock)
  - traits.rs: MemApi, IoApi, TimeApi, LogApi trait definitions
  - std_impls.rs: std-based default implementations
  - errors.rs: IoError, TimeError type definitions

**Design Principles**:
- Ring0 knows nothing about Box
- Ring0 knows nothing about Nyash
- Pure OS API abstraction

**Global Initialization**:
- NyashRunner::new() initializes Ring0Context globally
- OnceLock ensures safe initialization (idempotent)

**Migration (2 paths)**:
- src/runner/selfhost.rs:27: eprintln! → ring0.log.error() (OOB strict)
- src/runner/selfhost.rs:177: eprintln! → ring0.log.error() (PyVM error)

**Tests**:
- 4 unit tests added (ring0 module)
- All tests passed
- Build successful (0 errors)

**Migration Status**:
- Migrated: 2/3,955 (0.05%)
- Remaining: 3,953 paths (Phase 89+)

**Files Changed**:
- src/runtime/ring0/mod.rs (new, 100 lines)
- src/runtime/ring0/traits.rs (new, 93 lines)
- src/runtime/ring0/std_impls.rs (new, 77 lines)
- src/runtime/ring0/errors.rs (new, 26 lines)
- src/runtime/mod.rs (Ring0Context export)
- src/runner/mod.rs (global initialization)
- src/runner/selfhost.rs (2 paths migrated)
- docs/development/current/main/ring0-inventory.md (Phase 88 status)

Phase 88 complete. Ready for Phase 89 (gradual migration).

🐱
This commit is contained in:
nyash-codex
2025-12-02 22:38:27 +09:00
parent 8cd9729375
commit 42b09b3c1c
9 changed files with 331 additions and 5 deletions

View File

@ -208,9 +208,10 @@
- `NYASH_PHI_FALLBACK_DISABLED=1 cargo test --release --lib` 実行時の Case D panic は 4 件 → 0 件となり、Case D 完全解消を達成。型生成Const/BoxCall・型伝播CopyTypePropagator/PhiTypeResolver・統合GenericTypeResolverの 3 層構造が箱として完成し、if_phi フォールバック削除に進める状態になったPhase 84-5 / 82 の最終仕上げ)。
11. **Phase 85-ring0-runtime: Ring0/Ring1/Plugin 層の設計整理****設計中**
- Ring0 は Box を知らない最小カーネル APIMem/Io/Time/Log 等)に限定し、実装は `Ring0Context` + adapter 1 箇所に集約する方針を docs に固定。
- Ring1-coreStringBox/ArrayBox/MapBox/FileBox/ConsoleBox 等)と ring1-optional/selfhost/user_plugin を 4 層に分類し、「core_required は静的必須セット、optional と user は PluginHost の上に載る」設計を言語化。
- `docs/development/current/main/ring0-inventory.md` に println!/eprintln! を含む Ring0 候補呼び出しや Box/プラグイン/カーネル実装数の調査結果をまとめ、Phase 86 以降で Ring0Context に寄せていくためのインベントリを準備。
- Ring0 は Box を知らない最小カーネル APIMem/Io/Time/Log 等)に限定し、実装は `Ring0Context` + adapter 1 箇所に集約する方針を docs に固定済み
- Ring1-coreStringBox/ArrayBox/MapBox/FileBox/ConsoleBox 等)と ring1-optional/selfhost/user_plugin を 4 層に分類し、「core_required は静的必須セット、optional と user は PluginHost の上に載る」設計を言語化済み
- `docs/development/current/main/ring0-inventory.md` に println!/eprintln! を含む Ring0 候補呼び出しや Box/プラグイン/カーネル実装数の調査結果をまとめ、Phase 86/87 以降で Ring0Context に寄せていくためのインベントリを準備。
- Phase 87 で CoreBoxId/CoreMethodId の実装とテストが完了し、Box 名・メソッド名のハードコードは `src/runtime/core_box_ids.rs` に集約された。今後の ring0/ring1-core 変更はこの SSOT に対してのみ行えばよい状態になっている。
12. **Phase 86: BoxFactory Priority 正常化****完了**2025-12-02
- **目的**: BoxFactory のデフォルトポリシーを `BuiltinFirst` から `StrictPluginFirst` に変更し、プラグイン版 Box が正常に使用できるよう正常化。

View File

@ -14,6 +14,24 @@
- 将来的には `Ring0Context.log` / `Ring0Context.io` 経由に寄せる。
- 代表パスselfhost/hack_check/VM/LLVM`println!/eprintln!` から段階的に移行する。
### Phase 88 移行状況2025-12-02
**移行済みパス2箇所**:
| ファイル | 行 | Before | After |
|---------|---|--------|-------|
| `src/runner/selfhost.rs` | 27 | `eprintln!("[selfhost][oob-strict] ...")` | `ring0.log.error("[selfhost][oob-strict] ...")` |
| `src/runner/selfhost.rs` | 177 | `eprintln!("❌ PyVM error ...")` | `ring0.log.error("❌ PyVM error ...")` |
**残存パス3,953箇所**:
| カテゴリ | 出現回数 | 移行予定 |
|---------|---------|---------|
| println! | 2,200 | Phase 89-A |
| eprintln! | 1,753 | Phase 89-B |
Phase 89 以降で段階的に移行予定。
---
## 2. Box / プラグイン / カーネル実装の数

View File

@ -60,6 +60,12 @@ use tasks::run_named_task;
impl NyashRunner {
/// Create a new runner with the given configuration
pub fn new(config: CliConfig) -> Self {
// Phase 88: Ring0Context 初期化(グローバル、一度だけ)
// OnceLock により、複数回呼ばれても最初の一度だけ初期化される
let _ = runtime::ring0::GLOBAL_RING0.get_or_init(|| {
std::sync::Arc::new(runtime::ring0::default_ring0())
});
Self { config }
}

View File

@ -24,7 +24,9 @@ fn maybe_dump_mir_verbose(module: &crate::mir::MirModule) {
/// Check OOB strict mode and exit(1) if out-of-bounds was observed.
fn check_oob_strict_exit() {
if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
eprintln!("[selfhost][oob-strict] Out-of-bounds observed → exit(1)");
// Phase 88: Ring0Context 経由でエラーログ出力
let ring0 = crate::runtime::ring0::get_global_ring0();
ring0.log.error("[selfhost][oob-strict] Out-of-bounds observed → exit(1)");
std::process::exit(1);
}
}
@ -170,7 +172,9 @@ impl NyashRunner {
println!("Result: {}", code);
std::process::exit(code);
} else {
eprintln!("❌ PyVM error (selfhost-preexpand)");
// Phase 88: Ring0Context 経由でエラーログ出力
let ring0 = crate::runtime::ring0::get_global_ring0();
ring0.log.error("❌ PyVM error (selfhost-preexpand)");
std::process::exit(1);
}
} else {

View File

@ -6,6 +6,7 @@ pub mod box_registry;
pub mod core_box_ids; // Phase 87: CoreBoxId/CoreMethodId 型安全enum
pub mod deprecations;
pub mod gc;
pub mod ring0; // Phase 88: Ring0Context - OS API 抽象化レイヤー
pub mod gc_controller;
pub mod gc_mode;
pub mod gc_trace;
@ -38,6 +39,7 @@ mod tests;
pub use box_registry::{get_global_registry, BoxFactoryRegistry, BoxProvider};
pub use core_box_ids::{CoreBoxCategory, CoreBoxId, CoreMethodId}; // Phase 87: 型安全enum
pub use plugin_config::PluginConfig;
pub use ring0::{get_global_ring0, init_global_ring0, Ring0Context}; // Phase 88: Ring0 公開 API
pub use plugin_loader_unified::{
get_global_plugin_host, init_global_plugin_host, MethodHandle, PluginBoxType, PluginHost,
PluginLibraryHandle,

View File

@ -0,0 +1,25 @@
//! Phase 88: Ring0 エラー型定義
/// IO 操作エラー
#[derive(Debug, Clone)]
pub struct IoError(pub String);
impl std::fmt::Display for IoError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "IoError: {}", self.0)
}
}
impl std::error::Error for IoError {}
/// 時刻取得エラー
#[derive(Debug, Clone)]
pub struct TimeError(pub String);
impl std::fmt::Display for TimeError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "TimeError: {}", self.0)
}
}
impl std::error::Error for TimeError {}

114
src/runtime/ring0/mod.rs Normal file
View File

@ -0,0 +1,114 @@
//! Phase 88: Ring0Context - OS API 抽象化レイヤー
//!
//! Ring0 は Box を知らない、Nyash を知らない純粋な OS API 層。
mod errors;
mod std_impls;
mod traits;
pub use errors::{IoError, TimeError};
pub use std_impls::{NoopMem, StdIo, StdLog, StdTime};
pub use traits::{IoApi, LogApi, LogLevel, MemApi, MemStats, TimeApi};
use std::sync::{Arc, OnceLock};
/// Phase 88: Ring0 コンテキスト
///
/// OS API レイヤーを trait で抽象化し、1つの構造体に束ねる。
pub struct Ring0Context {
pub mem: Arc<dyn MemApi>,
pub io: Arc<dyn IoApi>,
pub time: Arc<dyn TimeApi>,
pub log: Arc<dyn LogApi>,
}
impl Ring0Context {
/// 新規 Ring0Context を作成
pub fn new(
mem: Arc<dyn MemApi>,
io: Arc<dyn IoApi>,
time: Arc<dyn TimeApi>,
log: Arc<dyn LogApi>,
) -> Self {
Self { mem, io, time, log }
}
}
impl std::fmt::Debug for Ring0Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ring0Context")
.field("mem", &"<dyn MemApi>")
.field("io", &"<dyn IoApi>")
.field("time", &"<dyn TimeApi>")
.field("log", &"<dyn LogApi>")
.finish()
}
}
/// デフォルト Ring0Context を作成std ベース)
pub fn default_ring0() -> Ring0Context {
Ring0Context {
mem: Arc::new(NoopMem),
io: Arc::new(StdIo),
time: Arc::new(StdTime),
log: Arc::new(StdLog),
}
}
// ===== グローバル Ring0Context =====
pub static GLOBAL_RING0: OnceLock<Arc<Ring0Context>> = OnceLock::new();
/// グローバル Ring0Context を初期化
pub fn init_global_ring0(ctx: Ring0Context) {
GLOBAL_RING0
.set(Arc::new(ctx))
.expect("[Phase 88] Ring0Context already initialized");
}
/// グローバル Ring0Context を取得
pub fn get_global_ring0() -> Arc<Ring0Context> {
GLOBAL_RING0
.get()
.expect("[Phase 88] Ring0Context not initialized")
.clone()
}
// ===== テスト =====
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ring0_context_creation() {
let ring0 = default_ring0();
ring0.log.info("test message");
}
#[test]
fn test_io_api() {
let ring0 = default_ring0();
let result = ring0.io.stdout_write(b"test\n");
assert!(result.is_ok());
}
#[test]
fn test_time_api() {
let ring0 = default_ring0();
let now = ring0.time.now();
assert!(now.is_ok());
let instant = ring0.time.monotonic_now();
assert!(instant.is_ok());
}
#[test]
fn test_log_levels() {
let ring0 = default_ring0();
ring0.log.debug("debug message");
ring0.log.info("info message");
ring0.log.warn("warn message");
ring0.log.error("error message");
}
}

View File

@ -0,0 +1,73 @@
//! Phase 88: std ベースの Ring0 デフォルト実装
use super::errors::{IoError, TimeError};
use super::traits::*;
use std::time::SystemTime;
/// noop メモリ実装Phase 88: 将来 hakmem に接続)
pub struct NoopMem;
impl MemApi for NoopMem {
fn alloc(&self, _size: usize) -> *mut u8 {
std::ptr::null_mut()
}
fn free(&self, _ptr: *mut u8) {}
fn stats(&self) -> MemStats {
MemStats::default()
}
}
/// std::io ベースの IO 実装
pub struct StdIo;
impl IoApi for StdIo {
fn stdout_write(&self, data: &[u8]) -> Result<(), IoError> {
use std::io::Write;
std::io::stdout()
.write_all(data)
.map_err(|e| IoError(format!("stdout write failed: {}", e)))
}
fn stderr_write(&self, data: &[u8]) -> Result<(), IoError> {
use std::io::Write;
std::io::stderr()
.write_all(data)
.map_err(|e| IoError(format!("stderr write failed: {}", e)))
}
fn stdin_read(&self, buf: &mut [u8]) -> Result<usize, IoError> {
use std::io::Read;
std::io::stdin()
.read(buf)
.map_err(|e| IoError(format!("stdin read failed: {}", e)))
}
}
/// std::time ベースの時刻実装
pub struct StdTime;
impl TimeApi for StdTime {
fn now(&self) -> Result<SystemTime, TimeError> {
Ok(SystemTime::now())
}
fn monotonic_now(&self) -> Result<std::time::Instant, TimeError> {
Ok(std::time::Instant::now())
}
}
/// eprintln!/println! ベースのログ実装
pub struct StdLog;
impl LogApi for StdLog {
fn log(&self, level: LogLevel, msg: &str) {
match level {
LogLevel::Debug => eprintln!("[DEBUG] {}", msg),
LogLevel::Info => println!("[INFO] {}", msg),
LogLevel::Warn => eprintln!("[WARN] {}", msg),
LogLevel::Error => eprintln!("[ERROR] {}", msg),
}
}
}

View File

@ -0,0 +1,83 @@
//! Phase 88: Ring0 trait 定義
//!
//! OS API レイヤーの純粋な抽象化。
//! Box 名・Nyash 型を一切知らない。
use super::errors::{IoError, TimeError};
use std::time::SystemTime;
/// メモリ APIPhase 88: noop、将来 hakmem 接続)
pub trait MemApi: Send + Sync {
/// メモリ割り当てPhase 88: 未実装)
fn alloc(&self, size: usize) -> *mut u8;
/// メモリ解放Phase 88: 未実装)
fn free(&self, ptr: *mut u8);
/// メモリ統計Phase 88: 未実装)
fn stats(&self) -> MemStats;
}
/// メモリ統計情報
#[derive(Debug, Default, Clone)]
pub struct MemStats {
pub allocated: usize,
pub freed: usize,
pub current: usize,
}
/// IO API
pub trait IoApi: Send + Sync {
/// 標準出力への書き込み
fn stdout_write(&self, data: &[u8]) -> Result<(), IoError>;
/// 標準エラー出力への書き込み
fn stderr_write(&self, data: &[u8]) -> Result<(), IoError>;
/// 標準入力からの読み込み
fn stdin_read(&self, buf: &mut [u8]) -> Result<usize, IoError>;
}
/// 時刻 API
pub trait TimeApi: Send + Sync {
/// 現在時刻取得
fn now(&self) -> Result<SystemTime, TimeError>;
/// モノトニック時刻取得(高精度タイマー用)
fn monotonic_now(&self) -> Result<std::time::Instant, TimeError>;
}
/// ログレベル
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogLevel {
Debug,
Info,
Warn,
Error,
}
/// ログ API
pub trait LogApi: Send + Sync {
/// ログ出力
fn log(&self, level: LogLevel, msg: &str);
/// デバッグログ(便利メソッド)
fn debug(&self, msg: &str) {
self.log(LogLevel::Debug, msg);
}
/// 情報ログ(便利メソッド)
fn info(&self, msg: &str) {
self.log(LogLevel::Info, msg);
}
/// 警告ログ(便利メソッド)
fn warn(&self, msg: &str) {
self.log(LogLevel::Warn, msg);
}
/// エラーログ(便利メソッド)
fn error(&self, msg: &str) {
self.log(LogLevel::Error, msg);
}
}