Files
hakorune/src/interpreter/system_methods.rs
Moe Charm 3d8ba3f3ec fix(jit): NewBoxのJIT安全化とDebugBox Phase 1実装
- NewBoxのJIT扱いを安全化(src/jit/lower/core.rs)
  - NYASH_USE_PLUGIN_BUILTINS=1 && args.is_empty() かつ StringBox/IntegerBox のみJIT許可
  - ArrayBox/MapBox等のプラグインBoxまたは引数ありはunsupportedとしてカウント
  - unsupported>0の関数はJIT対象外となりVM実行にフォールバック(Segfault回避)

- DebugBox Phase 1実装(JITトレース機能)
  - tracePluginCalls(bool)でJITシムトレースON/OFF
  - getJitEvents()で直近のJITイベント取得
  - src/jit/shim_trace.rs追加でトレース基盤実装

- Printのサポート
  - PrintはJIT非対応に戻しVM経路で確実に出力(出力消失解消)

- テストとサンプル追加
  - examples/jit_plugin_invoke_param_array.nyash: 最小JITスモークテスト
  - examples/py_result_*.nyash: Python plugin結果チェーン処理デモ

- PyRuntimeBox拡張
  - str()メソッドでPyObjectのstring表現を取得可能に
  - エラーハンドリング改善とResultチェーンサポート

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 13:46:18 +09:00

409 lines
17 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*!
* System Methods Module
*
* Extracted from box_methods.rs
* Contains system-level Box method implementations:
* - TimeBox methods (now, fromTimestamp, parse, sleep, format)
* - DateTimeBox methods (year, month, day, hour, minute, second, timestamp, toISOString, format, addDays, addHours)
* - TimerBox methods (elapsed, reset)
* - DebugBox methods (startTracking, stopTracking, trackBox, dumpAll, saveToFile, watch, etc.)
*/
use super::*;
use crate::box_trait::StringBox;
use crate::boxes::{TimeBox, DateTimeBox};
impl NyashInterpreter {
/// TimeBoxのメソッド呼び出しを実行
pub(super) fn execute_time_method(&mut self, time_box: &TimeBox, method: &str, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"now" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("now() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(time_box.now())
}
"fromTimestamp" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("fromTimestamp() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(time_box.fromTimestamp(arg_values[0].clone_box()))
}
"parse" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("parse() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(time_box.parse(arg_values[0].clone_box()))
}
"sleep" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("sleep() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(time_box.sleep(arg_values[0].clone_box()))
}
"format" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("format() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(time_box.format(arg_values[0].clone_box()))
}
_ => {
Err(RuntimeError::InvalidOperation {
message: format!("Unknown TimeBox method: {}", method),
})
}
}
}
/// DateTimeBoxのメソッド呼び出しを実行
pub(super) fn execute_datetime_method(&mut self, datetime_box: &DateTimeBox, method: &str, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"year" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("year() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(datetime_box.year())
}
"month" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("month() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(datetime_box.month())
}
"day" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("day() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(datetime_box.day())
}
"hour" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("hour() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(datetime_box.hour())
}
"minute" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("minute() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(datetime_box.minute())
}
"second" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("second() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(datetime_box.second())
}
"timestamp" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("timestamp() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(datetime_box.timestamp())
}
"toISOString" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toISOString() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(datetime_box.toISOString())
}
"format" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("format() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(datetime_box.format(arg_values[0].clone_box()))
}
"addDays" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("addDays() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(datetime_box.addDays(arg_values[0].clone_box()))
}
"addHours" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("addHours() expects 1 argument, got {}", arg_values.len()),
});
}
Ok(datetime_box.addHours(arg_values[0].clone_box()))
}
"toString" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("toString() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(Box::new(datetime_box.to_string_box()))
}
_ => {
Err(RuntimeError::InvalidOperation {
message: format!("Unknown DateTimeBox method: {}", method),
})
}
}
}
/// TimerBoxのメソッド呼び出しを実行
pub(super) fn execute_timer_method(&mut self, timer_box: &TimerBox, method: &str, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"elapsed" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("elapsed() expects 0 arguments, got {}", arg_values.len()),
});
}
Ok(timer_box.elapsed())
}
"reset" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("reset() expects 0 arguments, got {}", arg_values.len()),
});
}
// NOTE: resetはmutableメソッドなので、ここでは新しいTimerBoxを作成
let timer_box = Box::new(TimerBox::new()) as Box<dyn NyashBox>;
// 🌍 革命的実装Environment tracking廃止
Ok(timer_box)
}
_ => {
Err(RuntimeError::InvalidOperation {
message: format!("Unknown TimerBox method: {}", method),
})
}
}
}
/// DebugBoxのメソッド呼び出しを実行
pub(super) fn execute_debug_method(&mut self, debug_box: &DebugBox, method: &str, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> {
// 引数を評価
let mut arg_values = Vec::new();
for arg in arguments {
arg_values.push(self.execute_expression(arg)?);
}
// メソッドを実行
match method {
"startTracking" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("startTracking() expects 0 arguments, got {}", arg_values.len()),
});
}
debug_box.start_tracking()
}
"stopTracking" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("stopTracking() expects 0 arguments, got {}", arg_values.len()),
});
}
debug_box.stop_tracking()
}
"trackBox" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("trackBox() expects 2 arguments, got {}", arg_values.len()),
});
}
// 第2引数をStringBoxとして取得
let name = if let Some(str_box) = arg_values[1].as_any().downcast_ref::<StringBox>() {
str_box.value.clone()
} else {
return Err(RuntimeError::InvalidOperation {
message: "trackBox() second argument must be a string".to_string(),
});
};
debug_box.track_box(arg_values[0].as_ref(), &name)
}
"dumpAll" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("dumpAll() expects 0 arguments, got {}", arg_values.len()),
});
}
debug_box.dump_all()
}
"saveToFile" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("saveToFile() expects 1 argument, got {}", arg_values.len()),
});
}
let filename = if let Some(str_box) = arg_values[0].as_any().downcast_ref::<StringBox>() {
str_box.value.clone()
} else {
return Err(RuntimeError::InvalidOperation {
message: "saveToFile() argument must be a string".to_string(),
});
};
debug_box.save_to_file(&filename)
}
"watch" => {
if arg_values.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("watch() expects 2 arguments, got {}", arg_values.len()),
});
}
let name = if let Some(str_box) = arg_values[1].as_any().downcast_ref::<StringBox>() {
str_box.value.clone()
} else {
return Err(RuntimeError::InvalidOperation {
message: "watch() second argument must be a string".to_string(),
});
};
debug_box.watch(arg_values[0].as_ref(), &name)
}
"memoryReport" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("memoryReport() expects 0 arguments, got {}", arg_values.len()),
});
}
debug_box.memory_report()
}
"setBreakpoint" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("setBreakpoint() expects 1 argument, got {}", arg_values.len()),
});
}
let func_name = if let Some(str_box) = arg_values[0].as_any().downcast_ref::<StringBox>() {
str_box.value.clone()
} else {
return Err(RuntimeError::InvalidOperation {
message: "setBreakpoint() argument must be a string".to_string(),
});
};
debug_box.set_breakpoint(&func_name)
}
"traceCall" => {
if arg_values.len() < 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("traceCall() expects at least 1 argument, got {}", arg_values.len()),
});
}
let func_name = if let Some(str_box) = arg_values[0].as_any().downcast_ref::<StringBox>() {
str_box.value.clone()
} else {
return Err(RuntimeError::InvalidOperation {
message: "traceCall() first argument must be a string".to_string(),
});
};
// 残りの引数を文字列として収集
let args: Vec<String> = arg_values[1..].iter()
.map(|v| v.to_string_box().value)
.collect();
debug_box.trace_call(&func_name, args)
}
"showCallStack" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("showCallStack() expects 0 arguments, got {}", arg_values.len()),
});
}
debug_box.show_call_stack()
}
"tracePluginCalls" => {
if arg_values.len() != 1 {
return Err(RuntimeError::InvalidOperation {
message: format!("tracePluginCalls(on:bool) expects 1 argument, got {}", arg_values.len()),
});
}
let on = if let Some(b) = arg_values[0].as_any().downcast_ref::<crate::box_trait::BoolBox>() { b.value } else { false };
debug_box.trace_plugin_calls(on)
}
"getJitEvents" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("getJitEvents() expects 0 arguments, got {}", arg_values.len()),
});
}
debug_box.get_jit_events()
}
"clear" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("clear() expects 0 arguments, got {}", arg_values.len()),
});
}
debug_box.clear()
}
"isTracking" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("isTracking() expects 0 arguments, got {}", arg_values.len()),
});
}
debug_box.is_tracking()
}
"getTrackedCount" => {
if !arg_values.is_empty() {
return Err(RuntimeError::InvalidOperation {
message: format!("getTrackedCount() expects 0 arguments, got {}", arg_values.len()),
});
}
debug_box.get_tracked_count()
}
_ => {
Err(RuntimeError::InvalidOperation {
message: format!("Unknown DebugBox method: {}", method),
})
}
}
}
}