feat(phase90): Ring0Context fs/time/thread migration complete
Phase 90 完了: IO/fs/time/thread 系の Ring0 移行 **Phase 90-A: fs 系移行(7箇所)** - FsApi trait 追加(6メソッド) - StdFs 実装(std::fs ベース) - IoError 拡張(4バリアント追加) - 移行: strip.rs(4), dispatch.rs(1), mod.rs(3) **Phase 90-B: io 系移行** - Phase 88 完了済み(スキップ) **Phase 90-C: time 系移行(3箇所)** - TimeApi に elapsed() 追加 - 移行: selfhost_exe.rs(1), io.rs(1), plugin_loader_unified.rs(1) **Phase 90-D: thread 系移行(2箇所)** - ThreadApi trait 追加(sleep メソッド) - StdThread 実装 - 移行: global_hooks.rs(1), plugin_loader_unified.rs(1) **Phase 90-E: 統合テスト** - ビルド成功(6 warnings, 0 errors) - テスト: 522/554 passed (94.2%) - 退行なし **実装成果**: - Ring0Context 拡張: fs, thread フィールド追加 - 総移行: 12箇所(fs: 7, time: 3, thread: 2) - 移行率: fs(2.9%), time(2.1%), thread(5.4%) **次のステップ**: Phase 91 (PluginHost/CoreServices skeleton) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -357,7 +357,64 @@ Phase 89 以降で段階的に移行予定。
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. 今後の棚卸しタスク(Phase 91 以降)
|
## 5. Phase 90 移行実績(2025-12-03)
|
||||||
|
|
||||||
|
### Phase 90-A: fs 系移行(7箇所)
|
||||||
|
|
||||||
|
**FsApi trait 追加**:
|
||||||
|
- `read_to_string()`, `read()`, `write_all()`, `exists()`, `metadata()`, `canonicalize()`
|
||||||
|
|
||||||
|
**移行済みパス**:
|
||||||
|
|
||||||
|
| ファイル | 行 | 移行内容 |
|
||||||
|
|---------|---|---------|
|
||||||
|
| `src/runner/modes/common_util/resolve/strip.rs` | 550-555 | `std::fs::read_to_string` → `ring0.fs.read_to_string` |
|
||||||
|
| `src/runner/modes/common_util/resolve/strip.rs` | 650-655 | `std::fs::read_to_string` → `ring0.fs.read_to_string` |
|
||||||
|
| `src/runner/modes/common_util/resolve/strip.rs` | 901-906 | `std::fs::read_to_string` → `ring0.fs.read_to_string` |
|
||||||
|
| `src/runner/modes/common_util/resolve/strip.rs` | 941-946 | `std::fs::read_to_string` → `ring0.fs.read_to_string` |
|
||||||
|
| `src/runner/dispatch.rs` | 31-33 | `std::fs::read_to_string` → `ring0.fs.read_to_string` |
|
||||||
|
| `src/runner/mod.rs` | 122-124 | `std::fs::read_to_string` → `ring0.fs.read_to_string` |
|
||||||
|
| `src/runner/mod.rs` | 181-183 | `std::fs::read_to_string` → `ring0.fs.read_to_string` |
|
||||||
|
| `src/runner/mod.rs` | 288-290 | `std::fs::read_to_string` → `ring0.fs.read_to_string` |
|
||||||
|
|
||||||
|
**移行進捗**: 7/243箇所 (2.9%)
|
||||||
|
|
||||||
|
### Phase 90-B: io 系移行
|
||||||
|
|
||||||
|
Phase 88 で既に IoApi 実装済みのため、スキップ(stdin/stdout は既存の ring0.io.* を使用)。
|
||||||
|
|
||||||
|
### Phase 90-C: time 系移行(3箇所)
|
||||||
|
|
||||||
|
**TimeApi に elapsed() 追加**:
|
||||||
|
- `fn elapsed(&self, start: Instant) -> Duration`
|
||||||
|
|
||||||
|
**移行済みパス**:
|
||||||
|
|
||||||
|
| ファイル | 行 | 移行内容 |
|
||||||
|
|---------|---|---------|
|
||||||
|
| `src/runner/modes/common_util/selfhost_exe.rs` | 60-68 | `Instant::now() + elapsed()` → `ring0.time.monotonic_now() + ring0.time.elapsed()` |
|
||||||
|
| `src/runner/modes/common_util/io.rs` | 22-37 | `Instant::now() + elapsed()` → `ring0.time.monotonic_now() + ring0.time.elapsed()` |
|
||||||
|
| `src/runtime/plugin_loader_unified.rs` | 330-344 | `Instant::now() + elapsed()` → `ring0.time.monotonic_now() + ring0.time.elapsed()` |
|
||||||
|
|
||||||
|
**移行進捗**: 3/143箇所 (2.1%)
|
||||||
|
|
||||||
|
### Phase 90-D: thread 系移行(2箇所)
|
||||||
|
|
||||||
|
**ThreadApi trait 追加**:
|
||||||
|
- `fn sleep(&self, duration: Duration)`
|
||||||
|
|
||||||
|
**移行済みパス**:
|
||||||
|
|
||||||
|
| ファイル | 行 | 移行内容 |
|
||||||
|
|---------|---|---------|
|
||||||
|
| `src/runtime/global_hooks.rs` | 246-253 | `std::thread::sleep` → `ring0.thread.sleep` |
|
||||||
|
| `src/runtime/plugin_loader_unified.rs` | 342 | `std::thread::sleep` → `ring0.thread.sleep` |
|
||||||
|
|
||||||
|
**移行進捗**: 2/37箇所 (5.4%)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 今後の棚卸しタスク(Phase 91 以降)
|
||||||
|
|
||||||
- hakmem / nyrt 経由の低レベル API 呼び出し(alloc/free など)を一覧化。
|
- hakmem / nyrt 経由の低レベル API 呼び出し(alloc/free など)を一覧化。
|
||||||
- 代表パス(selfhost/hack_check/VM/LLVM)のみを対象にした「最小 Ring0 呼び出しセット」を定義する。
|
- 代表パス(selfhost/hack_check/VM/LLVM)のみを対象にした「最小 Ring0 呼び出しセット」を定義する。
|
||||||
|
|||||||
@ -28,7 +28,9 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
|||||||
let groups = runner.config.as_groups();
|
let groups = runner.config.as_groups();
|
||||||
// Diagnostic/Exec: accept MIR JSON file direct (experimental; default OFF)
|
// Diagnostic/Exec: accept MIR JSON file direct (experimental; default OFF)
|
||||||
if let Some(path) = groups.parser.mir_json_file.as_ref() {
|
if let Some(path) = groups.parser.mir_json_file.as_ref() {
|
||||||
match std::fs::read_to_string(path) {
|
// Phase 90-A: fs 系移行
|
||||||
|
let ring0 = crate::runtime::ring0::get_global_ring0();
|
||||||
|
match ring0.fs.read_to_string(std::path::Path::new(path)) {
|
||||||
Ok(text) => {
|
Ok(text) => {
|
||||||
// Try schema v1 first (preferred by emitter)
|
// Try schema v1 first (preferred by emitter)
|
||||||
match crate::runner::json_v1_bridge::try_parse_v1_to_module(&text) {
|
match crate::runner::json_v1_bridge::try_parse_v1_to_module(&text) {
|
||||||
|
|||||||
@ -119,7 +119,9 @@ impl NyashRunner {
|
|||||||
}
|
}
|
||||||
// Early: direct MIR JSON execution (no source file). Experimental diagnostics/exec.
|
// Early: direct MIR JSON execution (no source file). Experimental diagnostics/exec.
|
||||||
if let Some(path) = groups.parser.mir_json_file.as_ref() {
|
if let Some(path) = groups.parser.mir_json_file.as_ref() {
|
||||||
match std::fs::read_to_string(path) {
|
// Phase 90-A: fs 系移行
|
||||||
|
let ring0 = crate::runtime::ring0::get_global_ring0();
|
||||||
|
match ring0.fs.read_to_string(std::path::Path::new(path)) {
|
||||||
Ok(text) => match crate::runner::json_v1_bridge::try_parse_v1_to_module(&text) {
|
Ok(text) => match crate::runner::json_v1_bridge::try_parse_v1_to_module(&text) {
|
||||||
Ok(Some(module)) => {
|
Ok(Some(module)) => {
|
||||||
let rc = self.execute_mir_module_quiet_exit(&module);
|
let rc = self.execute_mir_module_quiet_exit(&module);
|
||||||
@ -176,7 +178,9 @@ impl NyashRunner {
|
|||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
} else if let Some(file) = groups.input.file.as_ref() {
|
} else if let Some(file) = groups.input.file.as_ref() {
|
||||||
match std::fs::read_to_string(file) {
|
// Phase 90-A: fs 系移行
|
||||||
|
let ring0 = crate::runtime::ring0::get_global_ring0();
|
||||||
|
match ring0.fs.read_to_string(std::path::Path::new(file)) {
|
||||||
Ok(code) => match crate::parser::NyashParser::parse_from_string(&code) {
|
Ok(code) => match crate::parser::NyashParser::parse_from_string(&code) {
|
||||||
Ok(ast) => {
|
Ok(ast) => {
|
||||||
let prog = crate::r#macro::ast_json::ast_to_json(&ast);
|
let prog = crate::r#macro::ast_json::ast_to_json(&ast);
|
||||||
@ -281,7 +285,9 @@ impl NyashRunner {
|
|||||||
}
|
}
|
||||||
// Optional dependency tree bridge (log-only)
|
// Optional dependency tree bridge (log-only)
|
||||||
if let Ok(dep_path) = std::env::var("NYASH_DEPS_JSON") {
|
if let Ok(dep_path) = std::env::var("NYASH_DEPS_JSON") {
|
||||||
match std::fs::read_to_string(&dep_path) {
|
// Phase 90-A: fs 系移行
|
||||||
|
let ring0 = crate::runtime::ring0::get_global_ring0();
|
||||||
|
match ring0.fs.read_to_string(std::path::Path::new(&dep_path)) {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
let bytes = s.as_bytes().len();
|
let bytes = s.as_bytes().len();
|
||||||
let mut root_info = String::new();
|
let mut root_info = String::new();
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::Duration;
|
||||||
|
|
||||||
pub struct ChildOutput {
|
pub struct ChildOutput {
|
||||||
pub stdout: Vec<u8>,
|
pub stdout: Vec<u8>,
|
||||||
@ -19,7 +19,12 @@ pub fn spawn_with_timeout(mut cmd: Command, timeout_ms: u64) -> std::io::Result<
|
|||||||
let mut child = cmd.spawn()?;
|
let mut child = cmd.spawn()?;
|
||||||
let ch_stdout = child.stdout.take();
|
let ch_stdout = child.stdout.take();
|
||||||
let ch_stderr = child.stderr.take();
|
let ch_stderr = child.stderr.take();
|
||||||
let start = Instant::now();
|
// Phase 90-C: time 系移行
|
||||||
|
let ring0 = crate::runtime::ring0::get_global_ring0();
|
||||||
|
let start = ring0
|
||||||
|
.time
|
||||||
|
.monotonic_now()
|
||||||
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
|
||||||
let mut timed_out = false;
|
let mut timed_out = false;
|
||||||
let mut exit_status: Option<std::process::ExitStatus> = None;
|
let mut exit_status: Option<std::process::ExitStatus> = None;
|
||||||
loop {
|
loop {
|
||||||
@ -29,7 +34,7 @@ pub fn spawn_with_timeout(mut cmd: Command, timeout_ms: u64) -> std::io::Result<
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if start.elapsed() >= Duration::from_millis(timeout_ms) {
|
if ring0.time.elapsed(start) >= Duration::from_millis(timeout_ms) {
|
||||||
let _ = child.kill();
|
let _ = child.kill();
|
||||||
let _ = child.wait();
|
let _ = child.wait();
|
||||||
timed_out = true;
|
timed_out = true;
|
||||||
|
|||||||
@ -547,7 +547,11 @@ pub fn resolve_prelude_paths_profiled(
|
|||||||
if !seen.insert(key.clone()) {
|
if !seen.insert(key.clone()) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let src = std::fs::read_to_string(&real_path)
|
// Phase 90-A: fs 系移行
|
||||||
|
let ring0 = crate::runtime::ring0::get_global_ring0();
|
||||||
|
let src = ring0
|
||||||
|
.fs
|
||||||
|
.read_to_string(std::path::Path::new(&real_path))
|
||||||
.map_err(|e| format!("using: failed to read '{}': {}", real_path, e))?;
|
.map_err(|e| format!("using: failed to read '{}': {}", real_path, e))?;
|
||||||
let (_cleaned, nested, _nested_imports) =
|
let (_cleaned, nested, _nested_imports) =
|
||||||
collect_using_and_strip(runner, &src, &real_path)?;
|
collect_using_and_strip(runner, &src, &real_path)?;
|
||||||
@ -643,7 +647,11 @@ pub fn parse_preludes_to_asts(
|
|||||||
prelude_path
|
prelude_path
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let src = std::fs::read_to_string(prelude_path)
|
// Phase 90-A: fs 系移行
|
||||||
|
let ring0 = crate::runtime::ring0::get_global_ring0();
|
||||||
|
let src = ring0
|
||||||
|
.fs
|
||||||
|
.read_to_string(std::path::Path::new(prelude_path))
|
||||||
.map_err(|e| format!("using: error reading {}: {}", prelude_path, e))?;
|
.map_err(|e| format!("using: error reading {}: {}", prelude_path, e))?;
|
||||||
let (clean_src, _nested, _nested_imports) =
|
let (clean_src, _nested, _nested_imports) =
|
||||||
collect_using_and_strip(runner, &src, prelude_path)?;
|
collect_using_and_strip(runner, &src, prelude_path)?;
|
||||||
@ -890,7 +898,11 @@ pub fn merge_prelude_text(
|
|||||||
if !seen.insert(key.clone()) {
|
if !seen.insert(key.clone()) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let src = std::fs::read_to_string(path)
|
// Phase 90-A: fs 系移行
|
||||||
|
let ring0 = crate::runtime::ring0::get_global_ring0();
|
||||||
|
let src = ring0
|
||||||
|
.fs
|
||||||
|
.read_to_string(std::path::Path::new(path))
|
||||||
.map_err(|e| format!("using: failed to read '{}': {}", path, e))?;
|
.map_err(|e| format!("using: failed to read '{}': {}", path, e))?;
|
||||||
let (_cleaned, nested, _nested_imports) = collect_using_and_strip(runner, &src, path)?;
|
let (_cleaned, nested, _nested_imports) = collect_using_and_strip(runner, &src, path)?;
|
||||||
for n in nested.iter() {
|
for n in nested.iter() {
|
||||||
@ -926,7 +938,11 @@ pub fn merge_prelude_text(
|
|||||||
|
|
||||||
// Add preludes in DFS order
|
// Add preludes in DFS order
|
||||||
for (idx, path) in prelude_paths.iter().enumerate() {
|
for (idx, path) in prelude_paths.iter().enumerate() {
|
||||||
let content = std::fs::read_to_string(path)
|
// Phase 90-A: fs 系移行
|
||||||
|
let ring0 = crate::runtime::ring0::get_global_ring0();
|
||||||
|
let content = ring0
|
||||||
|
.fs
|
||||||
|
.read_to_string(std::path::Path::new(path))
|
||||||
.map_err(|e| format!("using: failed to read '{}': {}", path, e))?;
|
.map_err(|e| format!("using: failed to read '{}': {}", path, e))?;
|
||||||
|
|
||||||
// Strip using lines from prelude and normalize
|
// Strip using lines from prelude and normalize
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::Duration;
|
||||||
|
|
||||||
/// Try external selfhost compiler EXE to parse Ny -> JSON v0 and return MIR module.
|
/// Try external selfhost compiler EXE to parse Ny -> JSON v0 and return MIR module.
|
||||||
/// Returns Some(module) on success, None on failure (timeout/invalid output/missing exe)
|
/// Returns Some(module) on success, None on failure (timeout/invalid output/missing exe)
|
||||||
@ -57,13 +57,15 @@ pub fn exe_try_parse_json_v0(filename: &str, timeout_ms: u64) -> Option<crate::m
|
|||||||
};
|
};
|
||||||
let ch_stdout = child.stdout.take();
|
let ch_stdout = child.stdout.take();
|
||||||
let ch_stderr = child.stderr.take();
|
let ch_stderr = child.stderr.take();
|
||||||
let start = Instant::now();
|
// Phase 90-C: time 系移行
|
||||||
|
let ring0 = crate::runtime::ring0::get_global_ring0();
|
||||||
|
let start = ring0.time.monotonic_now().ok()?;
|
||||||
let mut timed_out = false;
|
let mut timed_out = false;
|
||||||
loop {
|
loop {
|
||||||
match child.try_wait() {
|
match child.try_wait() {
|
||||||
Ok(Some(_)) => break,
|
Ok(Some(_)) => break,
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
if start.elapsed() >= Duration::from_millis(timeout_ms) {
|
if ring0.time.elapsed(start) >= Duration::from_millis(timeout_ms) {
|
||||||
let _ = child.kill();
|
let _ = child.kill();
|
||||||
let _ = child.wait();
|
let _ = child.wait();
|
||||||
timed_out = true;
|
timed_out = true;
|
||||||
|
|||||||
@ -243,8 +243,13 @@ pub fn spawn_task_after(delay_ms: u64, name: &str, f: Box<dyn FnOnce() + Send +
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Fallback: run inline after blocking sleep
|
// Fallback: run inline after blocking sleep
|
||||||
|
// Phase 90-D: thread 系移行
|
||||||
|
let ring0 = crate::runtime::ring0::get_global_ring0();
|
||||||
|
let ring0_clone = ring0.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
std::thread::sleep(std::time::Duration::from_millis(delay_ms));
|
ring0_clone
|
||||||
|
.thread
|
||||||
|
.sleep(std::time::Duration::from_millis(delay_ms));
|
||||||
f();
|
f();
|
||||||
});
|
});
|
||||||
false
|
false
|
||||||
|
|||||||
@ -327,16 +327,21 @@ impl PluginHost {
|
|||||||
.downcast_ref::<crate::boxes::future::FutureBox>()
|
.downcast_ref::<crate::boxes::future::FutureBox>()
|
||||||
{
|
{
|
||||||
let max_ms: u64 = crate::config::env::await_max_ms();
|
let max_ms: u64 = crate::config::env::await_max_ms();
|
||||||
let start = std::time::Instant::now();
|
// Phase 90-C/D: time/thread 系移行
|
||||||
|
let ring0 = crate::runtime::ring0::get_global_ring0();
|
||||||
|
let start = ring0
|
||||||
|
.time
|
||||||
|
.monotonic_now()
|
||||||
|
.map_err(|_e| crate::bid::error::BidError::PluginError)?;
|
||||||
let mut spins = 0usize;
|
let mut spins = 0usize;
|
||||||
while !fut.ready() {
|
while !fut.ready() {
|
||||||
crate::runtime::global_hooks::safepoint_and_poll();
|
crate::runtime::global_hooks::safepoint_and_poll();
|
||||||
std::thread::yield_now();
|
std::thread::yield_now();
|
||||||
spins += 1;
|
spins += 1;
|
||||||
if spins % 1024 == 0 {
|
if spins % 1024 == 0 {
|
||||||
std::thread::sleep(std::time::Duration::from_millis(1));
|
ring0.thread.sleep(std::time::Duration::from_millis(1));
|
||||||
}
|
}
|
||||||
if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
|
if ring0.time.elapsed(start) >= std::time::Duration::from_millis(max_ms) {
|
||||||
let err = crate::box_trait::StringBox::new("Timeout");
|
let err = crate::box_trait::StringBox::new("Timeout");
|
||||||
return Ok(Some(Box::new(NyashResultBox::new_err(Box::new(err)))));
|
return Ok(Some(Box::new(NyashResultBox::new_err(Box::new(err)))));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,38 @@
|
|||||||
//! Phase 88: Ring0 エラー型定義
|
//! Phase 88: Ring0 エラー型定義
|
||||||
|
|
||||||
/// IO 操作エラー
|
/// IO 操作エラー (Phase 90-A: fs 系エラー追加)
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct IoError(pub String);
|
pub enum IoError {
|
||||||
|
/// ファイル読み込み失敗
|
||||||
|
ReadFailed(String),
|
||||||
|
/// ファイル書き込み失敗
|
||||||
|
WriteFailed(String),
|
||||||
|
/// メタデータ取得失敗
|
||||||
|
MetadataFailed(String),
|
||||||
|
/// 正規化失敗
|
||||||
|
CanonicalizeFailed(String),
|
||||||
|
/// stdin 読み込み失敗
|
||||||
|
StdinReadFailed(String),
|
||||||
|
/// stdout 書き込み失敗
|
||||||
|
StdoutWriteFailed(String),
|
||||||
|
/// stderr 書き込み失敗
|
||||||
|
StderrWriteFailed(String),
|
||||||
|
/// その他のエラー(Phase 88 互換用)
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for IoError {
|
impl std::fmt::Display for IoError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
write!(f, "IoError: {}", self.0)
|
match self {
|
||||||
|
IoError::ReadFailed(msg) => write!(f, "IoError (ReadFailed): {}", msg),
|
||||||
|
IoError::WriteFailed(msg) => write!(f, "IoError (WriteFailed): {}", msg),
|
||||||
|
IoError::MetadataFailed(msg) => write!(f, "IoError (MetadataFailed): {}", msg),
|
||||||
|
IoError::CanonicalizeFailed(msg) => write!(f, "IoError (CanonicalizeFailed): {}", msg),
|
||||||
|
IoError::StdinReadFailed(msg) => write!(f, "IoError (StdinReadFailed): {}", msg),
|
||||||
|
IoError::StdoutWriteFailed(msg) => write!(f, "IoError (StdoutWriteFailed): {}", msg),
|
||||||
|
IoError::StderrWriteFailed(msg) => write!(f, "IoError (StderrWriteFailed): {}", msg),
|
||||||
|
IoError::Other(msg) => write!(f, "IoError: {}", msg),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,8 +7,10 @@ mod std_impls;
|
|||||||
mod traits;
|
mod traits;
|
||||||
|
|
||||||
pub use errors::{IoError, TimeError};
|
pub use errors::{IoError, TimeError};
|
||||||
pub use std_impls::{NoopMem, StdIo, StdLog, StdTime};
|
pub use std_impls::{NoopMem, StdFs, StdIo, StdLog, StdThread, StdTime};
|
||||||
pub use traits::{IoApi, LogApi, LogLevel, MemApi, MemStats, TimeApi};
|
pub use traits::{
|
||||||
|
FsApi, FsMetadata, IoApi, LogApi, LogLevel, MemApi, MemStats, ThreadApi, TimeApi,
|
||||||
|
};
|
||||||
|
|
||||||
use std::sync::{Arc, OnceLock};
|
use std::sync::{Arc, OnceLock};
|
||||||
|
|
||||||
@ -20,6 +22,8 @@ pub struct Ring0Context {
|
|||||||
pub io: Arc<dyn IoApi>,
|
pub io: Arc<dyn IoApi>,
|
||||||
pub time: Arc<dyn TimeApi>,
|
pub time: Arc<dyn TimeApi>,
|
||||||
pub log: Arc<dyn LogApi>,
|
pub log: Arc<dyn LogApi>,
|
||||||
|
pub fs: Arc<dyn FsApi>, // Phase 90-A
|
||||||
|
pub thread: Arc<dyn ThreadApi>, // Phase 90-D
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ring0Context {
|
impl Ring0Context {
|
||||||
@ -29,8 +33,17 @@ impl Ring0Context {
|
|||||||
io: Arc<dyn IoApi>,
|
io: Arc<dyn IoApi>,
|
||||||
time: Arc<dyn TimeApi>,
|
time: Arc<dyn TimeApi>,
|
||||||
log: Arc<dyn LogApi>,
|
log: Arc<dyn LogApi>,
|
||||||
|
fs: Arc<dyn FsApi>,
|
||||||
|
thread: Arc<dyn ThreadApi>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { mem, io, time, log }
|
Self {
|
||||||
|
mem,
|
||||||
|
io,
|
||||||
|
time,
|
||||||
|
log,
|
||||||
|
fs,
|
||||||
|
thread,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +54,8 @@ impl std::fmt::Debug for Ring0Context {
|
|||||||
.field("io", &"<dyn IoApi>")
|
.field("io", &"<dyn IoApi>")
|
||||||
.field("time", &"<dyn TimeApi>")
|
.field("time", &"<dyn TimeApi>")
|
||||||
.field("log", &"<dyn LogApi>")
|
.field("log", &"<dyn LogApi>")
|
||||||
|
.field("fs", &"<dyn FsApi>")
|
||||||
|
.field("thread", &"<dyn ThreadApi>")
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,6 +67,8 @@ pub fn default_ring0() -> Ring0Context {
|
|||||||
io: Arc::new(StdIo),
|
io: Arc::new(StdIo),
|
||||||
time: Arc::new(StdTime),
|
time: Arc::new(StdTime),
|
||||||
log: Arc::new(StdLog),
|
log: Arc::new(StdLog),
|
||||||
|
fs: Arc::new(StdFs),
|
||||||
|
thread: Arc::new(StdThread),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use super::errors::{IoError, TimeError};
|
use super::errors::{IoError, TimeError};
|
||||||
use super::traits::*;
|
use super::traits::*;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
/// noop メモリ実装(Phase 88: 将来 hakmem に接続)
|
/// noop メモリ実装(Phase 88: 将来 hakmem に接続)
|
||||||
@ -27,21 +28,21 @@ impl IoApi for StdIo {
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
std::io::stdout()
|
std::io::stdout()
|
||||||
.write_all(data)
|
.write_all(data)
|
||||||
.map_err(|e| IoError(format!("stdout write failed: {}", e)))
|
.map_err(|e| IoError::StdoutWriteFailed(format!("{}", e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stderr_write(&self, data: &[u8]) -> Result<(), IoError> {
|
fn stderr_write(&self, data: &[u8]) -> Result<(), IoError> {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
std::io::stderr()
|
std::io::stderr()
|
||||||
.write_all(data)
|
.write_all(data)
|
||||||
.map_err(|e| IoError(format!("stderr write failed: {}", e)))
|
.map_err(|e| IoError::StderrWriteFailed(format!("{}", e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stdin_read(&self, buf: &mut [u8]) -> Result<usize, IoError> {
|
fn stdin_read(&self, buf: &mut [u8]) -> Result<usize, IoError> {
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
std::io::stdin()
|
std::io::stdin()
|
||||||
.read(buf)
|
.read(buf)
|
||||||
.map_err(|e| IoError(format!("stdin read failed: {}", e)))
|
.map_err(|e| IoError::StdinReadFailed(format!("{}", e)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +57,10 @@ impl TimeApi for StdTime {
|
|||||||
fn monotonic_now(&self) -> Result<std::time::Instant, TimeError> {
|
fn monotonic_now(&self) -> Result<std::time::Instant, TimeError> {
|
||||||
Ok(std::time::Instant::now())
|
Ok(std::time::Instant::now())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn elapsed(&self, start: std::time::Instant) -> std::time::Duration {
|
||||||
|
start.elapsed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// eprintln!/println! ベースのログ実装
|
/// eprintln!/println! ベースのログ実装
|
||||||
@ -99,3 +104,54 @@ impl LogApi for StdLog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// std::fs ベースのファイルシステム実装 (Phase 90-A)
|
||||||
|
pub struct StdFs;
|
||||||
|
|
||||||
|
impl FsApi for StdFs {
|
||||||
|
fn read_to_string(&self, path: &Path) -> Result<String, IoError> {
|
||||||
|
std::fs::read_to_string(path).map_err(|e| {
|
||||||
|
IoError::ReadFailed(format!("read_to_string({}): {}", path.display(), e))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&self, path: &Path) -> Result<Vec<u8>, IoError> {
|
||||||
|
std::fs::read(path)
|
||||||
|
.map_err(|e| IoError::ReadFailed(format!("read({}): {}", path.display(), e)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_all(&self, path: &Path, data: &[u8]) -> Result<(), IoError> {
|
||||||
|
std::fs::write(path, data)
|
||||||
|
.map_err(|e| IoError::WriteFailed(format!("write({}): {}", path.display(), e)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exists(&self, path: &Path) -> bool {
|
||||||
|
path.exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn metadata(&self, path: &Path) -> Result<FsMetadata, IoError> {
|
||||||
|
let meta = std::fs::metadata(path).map_err(|e| {
|
||||||
|
IoError::MetadataFailed(format!("metadata({}): {}", path.display(), e))
|
||||||
|
})?;
|
||||||
|
Ok(FsMetadata {
|
||||||
|
is_file: meta.is_file(),
|
||||||
|
is_dir: meta.is_dir(),
|
||||||
|
len: meta.len(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn canonicalize(&self, path: &Path) -> Result<PathBuf, IoError> {
|
||||||
|
std::fs::canonicalize(path).map_err(|e| {
|
||||||
|
IoError::CanonicalizeFailed(format!("canonicalize({}): {}", path.display(), e))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// std::thread ベースのスレッド実装 (Phase 90-D)
|
||||||
|
pub struct StdThread;
|
||||||
|
|
||||||
|
impl ThreadApi for StdThread {
|
||||||
|
fn sleep(&self, duration: std::time::Duration) {
|
||||||
|
std::thread::sleep(duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
//! Box 名・Nyash 型を一切知らない。
|
//! Box 名・Nyash 型を一切知らない。
|
||||||
|
|
||||||
use super::errors::{IoError, TimeError};
|
use super::errors::{IoError, TimeError};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
/// メモリ API(Phase 88: noop、将来 hakmem 接続)
|
/// メモリ API(Phase 88: noop、将来 hakmem 接続)
|
||||||
@ -45,6 +46,9 @@ pub trait TimeApi: Send + Sync {
|
|||||||
|
|
||||||
/// モノトニック時刻取得(高精度タイマー用)
|
/// モノトニック時刻取得(高精度タイマー用)
|
||||||
fn monotonic_now(&self) -> Result<std::time::Instant, TimeError>;
|
fn monotonic_now(&self) -> Result<std::time::Instant, TimeError>;
|
||||||
|
|
||||||
|
/// 経過時間取得 (Phase 90-C)
|
||||||
|
fn elapsed(&self, start: std::time::Instant) -> std::time::Duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ログレベル
|
/// ログレベル
|
||||||
@ -81,3 +85,39 @@ pub trait LogApi: Send + Sync {
|
|||||||
self.log(LogLevel::Error, msg);
|
self.log(LogLevel::Error, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ファイルシステムメタデータ
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FsMetadata {
|
||||||
|
pub is_file: bool,
|
||||||
|
pub is_dir: bool,
|
||||||
|
pub len: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ファイルシステム API (Phase 90-A)
|
||||||
|
pub trait FsApi: Send + Sync {
|
||||||
|
/// ファイルを文字列として読み込む
|
||||||
|
fn read_to_string(&self, path: &Path) -> Result<String, IoError>;
|
||||||
|
|
||||||
|
/// ファイルをバイト列として読み込む
|
||||||
|
fn read(&self, path: &Path) -> Result<Vec<u8>, IoError>;
|
||||||
|
|
||||||
|
/// ファイルに書き込む
|
||||||
|
fn write_all(&self, path: &Path, data: &[u8]) -> Result<(), IoError>;
|
||||||
|
|
||||||
|
/// パスが存在するか確認
|
||||||
|
fn exists(&self, path: &Path) -> bool;
|
||||||
|
|
||||||
|
/// ファイルメタデータを取得
|
||||||
|
fn metadata(&self, path: &Path) -> Result<FsMetadata, IoError>;
|
||||||
|
|
||||||
|
/// パスを正規化
|
||||||
|
fn canonicalize(&self, path: &Path) -> Result<PathBuf, IoError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// スレッド API (Phase 90-D)
|
||||||
|
pub trait ThreadApi: Send + Sync {
|
||||||
|
/// 指定時間スリープ
|
||||||
|
fn sleep(&self, duration: std::time::Duration);
|
||||||
|
// spawn は Phase 91 以降で追加予定
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user