chore(fmt): add legacy stubs and strip trailing whitespace to unblock cargo fmt

This commit is contained in:
Selfhosting Dev
2025-09-17 07:43:07 +09:00
parent fcf8ce1f3c
commit adbb0201a9
385 changed files with 35622 additions and 15004 deletions

View File

@ -342,3 +342,58 @@ Namespaces / Using現状
**AOT Quick** **AOT Quick**
- Array literal: `NYASH_SYNTAX_SUGAR_LEVEL=basic ./tools/build_llvm.sh tmp/aot_array_literal_main.nyash -o app && ./app` - Array literal: `NYASH_SYNTAX_SUGAR_LEVEL=basic ./tools/build_llvm.sh tmp/aot_array_literal_main.nyash -o app && ./app`
- Map literal: `NYASH_SYNTAX_SUGAR_LEVEL=basic NYASH_ENABLE_MAP_LITERAL=1 ./tools/build_llvm.sh tmp/aot_map_literal_main.nyash -o app && ./app` - Map literal: `NYASH_SYNTAX_SUGAR_LEVEL=basic NYASH_ENABLE_MAP_LITERAL=1 ./tools/build_llvm.sh tmp/aot_map_literal_main.nyash -o app && ./app`
Refactoring Plan (Phase15 followup)
- 背景: 巨大ファイルAST/MIR/LLVMが保守コスト増の主因。機能非変化の小刻みリファクタで視認性と変更容易性を上げる。
- 目的と範囲(非機能変更・段階適用)
1) ASTsrc/ast.rs:1: 定義とヘルパの分離、将来のサブenum活用導線の整備現行API互換
2) MIR Buildersrc/mir/builder.rs:1: build_module の骨格化・責務分離、既存分割builder/*)の徹底。
3) Python LLVMsrc/llvm_py/llvm_builder.py:1: lower_function の前処理/本体分離、lower_block の責務拡張。
4) MIR 命令src/mir/instruction.rs:1: 構造体+トレイト導線の導入enum への委譲を維持した非破壊移行)。
- 実施順小PR単位、CI緑維持
PR1: AST utils 抽出(非破壊)
- 追加: `src/ast/utils.rs` に classify/info/span/to_string などのヘルパを移設。
- `src/ast.rs` は ASTNode/StructureNode/ExpressionNode/StatementNode の定義中心に縮退。
- 互換維持: `pub use ast::utils::*;` で既存呼び出しを壊さない。
- 受入: 全ビルド/スモーク緑、差分はファイル移動のみ。
PR2: MIR Builder build_module 分割(非破壊)
- `build_module``prepare_module`/`lower_root`/`finalize_module` に3分割。
- 型推定value_types→返り値は finalize 側へ集約(現行ロジック移設)。
- 既存の exprs/stmts などの委譲を明示し、build_module 本体を「骨格のみ」に縮退。
- 受入: LLVM/PyVM/Bridge スモーク緑(挙動非変化)。
PR3: Python LLVM lower_function の前処理抽出
- 新設: `setup_phi_placeholders()` を導入し、PHI 宣言/CFG 依存前処理をここへ移設。
- `lower_block()` に snapshotblock_end_values 収集)までの責務を移動。メインループは薄い周回に。
- 受入: `tools/smokes/curated_llvm.sh` / `curated_llvm_stage3.sh` 緑。
PR4: AST ラッパー導入(非破壊導線)
- 追加: `src/ast/nodes.rs` に小さな構造体群Assign/Return/...)。
- enum `StatementNode/ExpressionNode` を構造体で保持。`From<T> for ASTNode` / `TryFrom<ASTNode> for T` を提供。
- Builder 入口(`builder/stmts.rs`, `builder/exprs.rs`)で ASTNode → TryFrom 変換を 1 行追加し以降はサブ型で match。
- 受入: ビルド/スモーク緑(機能非変化)。
PR5: MIR 命令トレイト POCConst/BinOp
- 追加: `src/mir/instruction_kinds/``const_.rs`, `binop.rs`(各命令を struct 化)。
- 共通トレイト `InstructionMeta { effects(), dst(), used() }` を定義。
- `MirInstruction::{effects,dst_value,used_values}` から一部を構造体 impl に委譲match 縮退の礎)。
- 受入: スモーク緑、`instruction_introspection.rs` の挙動非変化。
- リスクとガード
- 機能非変化を原則(挙動差分は不可)。
- CI で LLVM/Bridge を優先確認。Selfhost/E2E は任意ジョブで回す。
- PR は 400 行未満/ファイル移動中心を目安に分割。
- 参考ファイル
- AST: `src/ast.rs`, (新規)`src/ast/utils.rs`, (将来)`src/ast/nodes.rs`
- Builder: `src/mir/builder.rs`, `src/mir/builder/*`
- Python LLVM: `src/llvm_py/llvm_builder.py`
- MIR 命令: `src/mir/instruction.rs`, (新規)`src/mir/instruction_kinds/*`
Acceptance Criteria
- すべての変更は機能非変化(スモーク/CI 緑)。
- 大型関数・巨大match の見通しが改善し、追従点が局所化。
- 新規追加の導線AST サブ型/命令トレイト)は既存 API と共存し、段階移行を可能にする。

View File

@ -198,7 +198,7 @@ pub enum LiteralValue {
impl LiteralValue { impl LiteralValue {
/// LiteralValueをNyashBoxに変換 /// LiteralValueをNyashBoxに変換
pub fn to_nyash_box(&self) -> Box<dyn NyashBox> { pub fn to_nyash_box(&self) -> Box<dyn NyashBox> {
use crate::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox}; use crate::box_trait::{BoolBox, IntegerBox, StringBox, VoidBox};
use crate::boxes::FloatBox; use crate::boxes::FloatBox;
match self { match self {
@ -213,10 +213,10 @@ impl LiteralValue {
/// NyashBoxからLiteralValueに変換 /// NyashBoxからLiteralValueに変換
pub fn from_nyash_box(box_val: &dyn NyashBox) -> Option<LiteralValue> { pub fn from_nyash_box(box_val: &dyn NyashBox) -> Option<LiteralValue> {
use crate::box_trait::{BoolBox, IntegerBox, StringBox, VoidBox};
use crate::boxes::FloatBox;
#[allow(unused_imports)] #[allow(unused_imports)]
use std::any::Any; use std::any::Any;
use crate::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox};
use crate::boxes::FloatBox;
if let Some(string_box) = box_val.as_any().downcast_ref::<StringBox>() { if let Some(string_box) = box_val.as_any().downcast_ref::<StringBox>() {
Some(LiteralValue::String(string_box.value.clone())) Some(LiteralValue::String(string_box.value.clone()))
@ -226,7 +226,11 @@ impl LiteralValue {
Some(LiteralValue::Float(float_box.value)) Some(LiteralValue::Float(float_box.value))
} else if let Some(bool_box) = box_val.as_any().downcast_ref::<BoolBox>() { } else if let Some(bool_box) = box_val.as_any().downcast_ref::<BoolBox>() {
Some(LiteralValue::Bool(bool_box.value)) Some(LiteralValue::Bool(bool_box.value))
} else if box_val.as_any().downcast_ref::<crate::boxes::null_box::NullBox>().is_some() { } else if box_val
.as_any()
.downcast_ref::<crate::boxes::null_box::NullBox>()
.is_some()
{
Some(LiteralValue::Null) Some(LiteralValue::Null)
} else if box_val.as_any().downcast_ref::<VoidBox>().is_some() { } else if box_val.as_any().downcast_ref::<VoidBox>().is_some() {
Some(LiteralValue::Void) Some(LiteralValue::Void)
@ -325,7 +329,6 @@ pub enum ASTNode {
}, },
// ===== 文 (Statements) ===== // ===== 文 (Statements) =====
/// 代入文: target = value /// 代入文: target = value
Assignment { Assignment {
target: Box<ASTNode>, target: Box<ASTNode>,
@ -361,19 +364,12 @@ pub enum ASTNode {
}, },
/// break文 /// break文
Break { Break { span: Span },
span: Span,
},
/// continue文 /// continue文
Continue { Continue { span: Span },
span: Span,
},
/// using文: using namespace_name /// using文: using namespace_name
UsingStatement { UsingStatement { namespace_name: String, span: Span },
namespace_name: String,
span: Span,
},
/// import文: import "path" (as Alias)? /// import文: import "path" (as Alias)?
ImportStatement { ImportStatement {
path: String, path: String,
@ -408,10 +404,7 @@ pub enum ASTNode {
span: Span, span: Span,
}, },
/// 配列リテラル(糖衣): [e1, e2, ...] /// 配列リテラル(糖衣): [e1, e2, ...]
ArrayLiteral { ArrayLiteral { elements: Vec<ASTNode>, span: Span },
elements: Vec<ASTNode>,
span: Span,
},
/// マップリテラル(糖衣): { "k": v, ... } Stage2: 文字列キー限定) /// マップリテラル(糖衣): { "k": v, ... } Stage2: 文字列キー限定)
MapLiteral { MapLiteral {
entries: Vec<(String, ASTNode)>, entries: Vec<(String, ASTNode)>,
@ -447,7 +440,6 @@ pub enum ASTNode {
}, },
// ===== 宣言 (Declarations) ===== // ===== 宣言 (Declarations) =====
/// box宣言: box Name { fields... methods... } /// box宣言: box Name { fields... methods... }
BoxDeclaration { BoxDeclaration {
name: String, name: String,
@ -489,18 +481,11 @@ pub enum ASTNode {
}, },
// ===== 式 (Expressions) ===== // ===== 式 (Expressions) =====
/// リテラル値: "string", 42, true, etc /// リテラル値: "string", 42, true, etc
Literal { Literal { value: LiteralValue, span: Span },
value: LiteralValue,
span: Span,
},
/// 変数参照: variableName /// 変数参照: variableName
Variable { Variable { name: String, span: Span },
name: String,
span: Span,
},
/// 単項演算: operator operand /// 単項演算: operator operand
UnaryOp { UnaryOp {
@ -541,14 +526,10 @@ pub enum ASTNode {
}, },
/// this参照 /// this参照
This { This { span: Span },
span: Span,
},
/// me参照 /// me参照
Me { Me { span: Span },
span: Span,
},
/// 🔥 from呼び出し: from Parent.method(arguments) or from Parent.constructor(arguments) /// 🔥 from呼び出し: from Parent.method(arguments) or from Parent.constructor(arguments)
FromCall { FromCall {
@ -559,22 +540,13 @@ pub enum ASTNode {
}, },
/// thisフィールドアクセス: this.field /// thisフィールドアクセス: this.field
ThisField { ThisField { field: String, span: Span },
field: String,
span: Span,
},
/// meフィールドアクセス: me.field /// meフィールドアクセス: me.field
MeField { MeField { field: String, span: Span },
field: String,
span: Span,
},
/// ファイル読み込み: include "filename.nyash" /// ファイル読み込み: include "filename.nyash"
Include { Include { filename: String, span: Span },
filename: String,
span: Span,
},
/// ローカル変数宣言: local x, y, z /// ローカル変数宣言: local x, y, z
Local { Local {

View File

@ -12,12 +12,22 @@ pub struct Span {
impl Span { impl Span {
/// 新しいSpanを作成 /// 新しいSpanを作成
pub fn new(start: usize, end: usize, line: usize, column: usize) -> Self { pub fn new(start: usize, end: usize, line: usize, column: usize) -> Self {
Self { start, end, line, column } Self {
start,
end,
line,
column,
}
} }
/// デフォルトのSpan不明な位置 /// デフォルトのSpan不明な位置
pub fn unknown() -> Self { pub fn unknown() -> Self {
Self { start: 0, end: 0, line: 1, column: 1 } Self {
start: 0,
end: 0,
line: 1,
column: 1,
}
} }
/// 2つのSpanを結合開始位置から終了位置まで /// 2つのSpanを結合開始位置から終了位置まで
@ -46,11 +56,17 @@ impl Span {
// カーソル位置を表示(簡易版) // カーソル位置を表示(簡易版)
if self.column > 0 && self.column <= line_content.len() + 1 { if self.column > 0 && self.column <= line_content.len() + 1 {
context.push_str(" | "); context.push_str(" | ");
for _ in 1..self.column { context.push(' '); } for _ in 1..self.column {
context.push(' ');
}
let span_length = if self.end > self.start { let span_length = if self.end > self.start {
(self.end - self.start).min(line_content.len() - self.column + 1) (self.end - self.start).min(line_content.len() - self.column + 1)
} else { 1 }; } else {
for _ in 0..span_length.max(1) { context.push('^'); } 1
};
for _ in 0..span_length.max(1) {
context.push('^');
}
context.push('\n'); context.push('\n');
} }
@ -68,4 +84,3 @@ impl fmt::Display for Span {
write!(f, "line {}, column {}", self.line, self.column) write!(f, "line {}, column {}", self.line, self.column)
} }
} }

View File

@ -5,8 +5,8 @@
* Initial scope focuses on value coercions used by the MIR interpreter and JIT. * Initial scope focuses on value coercions used by the MIR interpreter and JIT.
*/ */
use crate::box_trait::{NyashBox, IntegerBox, BoolBox, StringBox, VoidBox};
use crate::backend::vm::VMValue; use crate::backend::vm::VMValue;
use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox};
use std::sync::Arc; use std::sync::Arc;
/// Opaque handle type used by JIT/runtime bridges. /// Opaque handle type used by JIT/runtime bridges.
@ -80,4 +80,3 @@ pub fn handle_of(boxref: Arc<dyn NyashBox>) -> Handle {
pub fn handle_get(h: Handle) -> Option<Arc<dyn NyashBox>> { pub fn handle_get(h: Handle) -> Option<Arc<dyn NyashBox>> {
crate::jit::rt::handles::get(h) crate::jit::rt::handles::get(h)
} }

View File

@ -4,11 +4,11 @@
* Handles the MIR -> WASM -> Native compilation pipeline * Handles the MIR -> WASM -> Native compilation pipeline
*/ */
use super::{AotError, AotConfig, AotStats}; use super::{AotConfig, AotError, AotStats};
use crate::mir::MirModule;
use crate::backend::wasm::{WasmBackend, WasmError}; use crate::backend::wasm::{WasmBackend, WasmError};
use wasmtime::{Engine, Module}; use crate::mir::MirModule;
use std::time::Instant; use std::time::Instant;
use wasmtime::{Engine, Module};
/// AOT compiler that handles the full compilation pipeline /// AOT compiler that handles the full compilation pipeline
pub struct AotCompiler { pub struct AotCompiler {
@ -21,8 +21,9 @@ impl AotCompiler {
/// Create a new AOT compiler with the given configuration /// Create a new AOT compiler with the given configuration
pub fn new(config: &AotConfig) -> Result<Self, AotError> { pub fn new(config: &AotConfig) -> Result<Self, AotError> {
// Create wasmtime engine with optimized configuration // Create wasmtime engine with optimized configuration
let engine = Engine::new(config.wasmtime_config()) let engine = Engine::new(config.wasmtime_config()).map_err(|e| {
.map_err(|e| AotError::WasmtimeError(format!("Failed to create wasmtime engine: {}", e)))?; AotError::WasmtimeError(format!("Failed to create wasmtime engine: {}", e))
})?;
// Create WASM backend for MIR -> WASM compilation // Create WASM backend for MIR -> WASM compilation
let wasm_backend = WasmBackend::new(); let wasm_backend = WasmBackend::new();
@ -46,12 +47,22 @@ impl AotCompiler {
let start_time = Instant::now(); let start_time = Instant::now();
// Use existing WASM backend to compile MIR to WASM // Use existing WASM backend to compile MIR to WASM
let wasm_bytes = self.wasm_backend.compile_module(mir_module) let wasm_bytes = self
.wasm_backend
.compile_module(mir_module)
.map_err(|e| match e { .map_err(|e| match e {
WasmError::CodegenError(msg) => AotError::CompilationError(format!("WASM codegen failed: {}", msg)), WasmError::CodegenError(msg) => {
WasmError::MemoryError(msg) => AotError::CompilationError(format!("WASM memory error: {}", msg)), AotError::CompilationError(format!("WASM codegen failed: {}", msg))
WasmError::UnsupportedInstruction(msg) => AotError::CompilationError(format!("Unsupported MIR instruction: {}", msg)), }
WasmError::WasmValidationError(msg) => AotError::CompilationError(format!("WASM validation failed: {}", msg)), WasmError::MemoryError(msg) => {
AotError::CompilationError(format!("WASM memory error: {}", msg))
}
WasmError::UnsupportedInstruction(msg) => {
AotError::CompilationError(format!("Unsupported MIR instruction: {}", msg))
}
WasmError::WasmValidationError(msg) => {
AotError::CompilationError(format!("WASM validation failed: {}", msg))
}
WasmError::IOError(msg) => AotError::IOError(msg), WasmError::IOError(msg) => AotError::IOError(msg),
})?; })?;
@ -70,8 +81,9 @@ impl AotCompiler {
.map_err(|e| AotError::WasmtimeError(format!("Failed to parse WASM module: {}", e)))?; .map_err(|e| AotError::WasmtimeError(format!("Failed to parse WASM module: {}", e)))?;
// Serialize the precompiled module to bytes // Serialize the precompiled module to bytes
let precompiled_bytes = module.serialize() let precompiled_bytes = module.serialize().map_err(|e| {
.map_err(|e| AotError::WasmtimeError(format!("Failed to serialize precompiled module: {}", e)))?; AotError::WasmtimeError(format!("Failed to serialize precompiled module: {}", e))
})?;
self.stats.precompiled_size = precompiled_bytes.len(); self.stats.precompiled_size = precompiled_bytes.len();
self.stats.compilation_time_ms += start_time.elapsed().as_millis() as u64; self.stats.compilation_time_ms += start_time.elapsed().as_millis() as u64;
@ -89,8 +101,9 @@ impl AotCompiler {
pub fn execute_precompiled(&self, precompiled_bytes: &[u8]) -> Result<i32, AotError> { pub fn execute_precompiled(&self, precompiled_bytes: &[u8]) -> Result<i32, AotError> {
// Deserialize the precompiled module // Deserialize the precompiled module
let module = unsafe { let module = unsafe {
Module::deserialize(&self.wasmtime_engine, precompiled_bytes) Module::deserialize(&self.wasmtime_engine, precompiled_bytes).map_err(|e| {
.map_err(|e| AotError::WasmtimeError(format!("Failed to deserialize module: {}", e)))? AotError::WasmtimeError(format!("Failed to deserialize module: {}", e))
})?
}; };
// Create instance and execute // Create instance and execute
@ -106,7 +119,8 @@ impl AotCompiler {
.map_err(|e| AotError::RuntimeError(format!("No main function found: {}", e)))?; .map_err(|e| AotError::RuntimeError(format!("No main function found: {}", e)))?;
// Execute the function // Execute the function
let result = main_func.call(&mut store, ()) let result = main_func
.call(&mut store, ())
.map_err(|e| AotError::RuntimeError(format!("Execution failed: {}", e)))?; .map_err(|e| AotError::RuntimeError(format!("Execution failed: {}", e)))?;
Ok(result) Ok(result)

View File

@ -49,7 +49,8 @@ impl AotConfig {
"x86" "x86"
} else { } else {
"unknown" "unknown"
}.to_string(); }
.to_string();
Ok(Self { Ok(Self {
wasmtime_config: config, wasmtime_config: config,
@ -98,19 +99,22 @@ impl AotConfig {
// Enable all advanced features for x86_64 // Enable all advanced features for x86_64
config.enable_simd = true; config.enable_simd = true;
config.enable_multi_memory = true; config.enable_multi_memory = true;
}, }
"aarch64" => { "aarch64" => {
// ARM64 - enable SIMD but be conservative with memory features // ARM64 - enable SIMD but be conservative with memory features
config.enable_simd = true; config.enable_simd = true;
config.enable_multi_memory = false; config.enable_multi_memory = false;
}, }
"x86" => { "x86" => {
// x86 - be conservative // x86 - be conservative
config.enable_simd = false; config.enable_simd = false;
config.enable_multi_memory = false; config.enable_multi_memory = false;
}, }
_ => { _ => {
return Err(AotError::ConfigError(format!("Unsupported target architecture: {}", target))); return Err(AotError::ConfigError(format!(
"Unsupported target architecture: {}",
target
)));
} }
} }
@ -188,7 +192,9 @@ impl AotConfig {
/// Set custom optimization level /// Set custom optimization level
pub fn set_optimization_level(&mut self, level: u8) -> Result<(), AotError> { pub fn set_optimization_level(&mut self, level: u8) -> Result<(), AotError> {
if level > 3 { if level > 3 {
return Err(AotError::ConfigError("Optimization level must be 0-3".to_string())); return Err(AotError::ConfigError(
"Optimization level must be 0-3".to_string(),
));
} }
self.optimization_level = level; self.optimization_level = level;
@ -244,7 +250,9 @@ mod tests {
#[test] #[test]
fn test_optimization_level_setting() { fn test_optimization_level_setting() {
let mut config = AotConfig::new().expect("Failed to create config"); let mut config = AotConfig::new().expect("Failed to create config");
config.set_optimization_level(1).expect("Failed to set opt level"); config
.set_optimization_level(1)
.expect("Failed to set opt level");
assert_eq!(config.optimization_level(), 1); assert_eq!(config.optimization_level(), 1);
} }

View File

@ -4,9 +4,9 @@
* Embeds precompiled WASM modules into self-contained executables * Embeds precompiled WASM modules into self-contained executables
*/ */
use super::{AotError, AotConfig}; use super::{AotConfig, AotError};
use std::path::Path;
use std::fs; use std::fs;
use std::path::Path;
/// Builder for creating standalone executable files /// Builder for creating standalone executable files
pub struct ExecutableBuilder<'a> { pub struct ExecutableBuilder<'a> {
@ -33,8 +33,9 @@ impl<'a> ExecutableBuilder<'a> {
/// Create the standalone executable /// Create the standalone executable
pub fn create_executable<P: AsRef<Path>>(&self, output_path: P) -> Result<(), AotError> { pub fn create_executable<P: AsRef<Path>>(&self, output_path: P) -> Result<(), AotError> {
let module_data = self.precompiled_module.as_ref() let module_data = self.precompiled_module.as_ref().ok_or_else(|| {
.ok_or_else(|| AotError::CompilationError("No precompiled module embedded".to_string()))?; AotError::CompilationError("No precompiled module embedded".to_string())
})?;
// Generate the runtime code with embedded module // Generate the runtime code with embedded module
let runtime_code = self.generate_runtime_code(module_data)?; let runtime_code = self.generate_runtime_code(module_data)?;
@ -62,10 +63,14 @@ impl<'a> ExecutableBuilder<'a> {
let module_bytes = self.format_module_bytes(module_data); let module_bytes = self.format_module_bytes(module_data);
let compatibility_key = self.config.compatibility_key(); let compatibility_key = self.config.compatibility_key();
let runtime_code = self.runtime_template let runtime_code = self
.runtime_template
.replace("{{MODULE_BYTES}}", &module_bytes) .replace("{{MODULE_BYTES}}", &module_bytes)
.replace("{{COMPATIBILITY_KEY}}", &compatibility_key) .replace("{{COMPATIBILITY_KEY}}", &compatibility_key)
.replace("{{OPTIMIZATION_LEVEL}}", &self.config.optimization_level().to_string()) .replace(
"{{OPTIMIZATION_LEVEL}}",
&self.config.optimization_level().to_string(),
)
.replace("{{TARGET_ARCH}}", self.config.target_arch()) .replace("{{TARGET_ARCH}}", self.config.target_arch())
.replace("{{WASMTIME_VERSION}}", "18.0"); .replace("{{WASMTIME_VERSION}}", "18.0");
@ -90,7 +95,8 @@ impl<'a> ExecutableBuilder<'a> {
/// Generate Cargo.toml for the executable /// Generate Cargo.toml for the executable
fn generate_cargo_toml(&self) -> String { fn generate_cargo_toml(&self) -> String {
format!(r#"[package] format!(
r#"[package]
name = "nyash-aot-executable" name = "nyash-aot-executable"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
@ -108,11 +114,16 @@ strip = true
[[bin]] [[bin]]
name = "nyash-aot-executable" name = "nyash-aot-executable"
path = "nyash_aot_main.rs" path = "nyash_aot_main.rs"
"#) "#
)
} }
/// Compile the Rust executable /// Compile the Rust executable
fn compile_rust_executable<P: AsRef<Path>, Q: AsRef<Path>>(&self, temp_dir: P, output_path: Q) -> Result<(), AotError> { fn compile_rust_executable<P: AsRef<Path>, Q: AsRef<Path>>(
&self,
temp_dir: P,
output_path: Q,
) -> Result<(), AotError> {
let temp_dir = temp_dir.as_ref(); let temp_dir = temp_dir.as_ref();
let output_path = output_path.as_ref(); let output_path = output_path.as_ref();
@ -121,12 +132,16 @@ path = "nyash_aot_main.rs"
cmd.current_dir(temp_dir) cmd.current_dir(temp_dir)
.args(&["build", "--release", "--bin", "nyash-aot-executable"]); .args(&["build", "--release", "--bin", "nyash-aot-executable"]);
let output = cmd.output() let output = cmd
.output()
.map_err(|e| AotError::CompilationError(format!("Failed to run cargo: {}", e)))?; .map_err(|e| AotError::CompilationError(format!("Failed to run cargo: {}", e)))?;
if !output.status.success() { if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr); let stderr = String::from_utf8_lossy(&output.stderr);
return Err(AotError::CompilationError(format!("Cargo build failed: {}", stderr))); return Err(AotError::CompilationError(format!(
"Cargo build failed: {}",
stderr
)));
} }
// Copy the compiled executable to the desired location // Copy the compiled executable to the desired location
@ -138,7 +153,9 @@ path = "nyash_aot_main.rs"
}; };
if !compiled_exe.exists() { if !compiled_exe.exists() {
return Err(AotError::CompilationError("Compiled executable not found".to_string())); return Err(AotError::CompilationError(
"Compiled executable not found".to_string(),
));
} }
fs::copy(&compiled_exe, output_path) fs::copy(&compiled_exe, output_path)
@ -238,7 +255,9 @@ mod tests {
let mut builder = ExecutableBuilder::new(&config); let mut builder = ExecutableBuilder::new(&config);
let test_data = vec![1, 2, 3, 4, 5]; let test_data = vec![1, 2, 3, 4, 5];
builder.embed_precompiled_module(test_data).expect("Failed to embed module"); builder
.embed_precompiled_module(test_data)
.expect("Failed to embed module");
assert!(builder.precompiled_module.is_some()); assert!(builder.precompiled_module.is_some());
} }
@ -272,7 +291,9 @@ mod tests {
let builder = ExecutableBuilder::new(&config); let builder = ExecutableBuilder::new(&config);
let test_data = vec![0x00, 0x61, 0x73, 0x6d]; let test_data = vec![0x00, 0x61, 0x73, 0x6d];
let runtime_code = builder.generate_runtime_code(&test_data).expect("Failed to generate runtime"); let runtime_code = builder
.generate_runtime_code(&test_data)
.expect("Failed to generate runtime");
assert!(runtime_code.contains("MODULE_DATA")); assert!(runtime_code.contains("MODULE_DATA"));
assert!(runtime_code.contains("0x00")); assert!(runtime_code.contains("0x00"));
assert!(runtime_code.contains("18.0")); // Wasmtime version assert!(runtime_code.contains("18.0")); // Wasmtime version

View File

@ -6,12 +6,12 @@
*/ */
mod compiler; mod compiler;
mod executable;
mod config; mod config;
mod executable;
pub use compiler::AotCompiler; pub use compiler::AotCompiler;
pub use executable::ExecutableBuilder;
pub use config::AotConfig; pub use config::AotConfig;
pub use executable::ExecutableBuilder;
use crate::mir::MirModule; use crate::mir::MirModule;
use std::path::Path; use std::path::Path;
@ -65,27 +65,21 @@ impl AotBackend {
let config = AotConfig::new()?; let config = AotConfig::new()?;
let compiler = AotCompiler::new(&config)?; let compiler = AotCompiler::new(&config)?;
Ok(Self { Ok(Self { compiler, config })
compiler,
config,
})
} }
/// Create AOT backend with custom configuration /// Create AOT backend with custom configuration
pub fn with_config(config: AotConfig) -> Result<Self, AotError> { pub fn with_config(config: AotConfig) -> Result<Self, AotError> {
let compiler = AotCompiler::new(&config)?; let compiler = AotCompiler::new(&config)?;
Ok(Self { Ok(Self { compiler, config })
compiler,
config,
})
} }
/// Compile MIR module to standalone native executable /// Compile MIR module to standalone native executable
pub fn compile_to_executable<P: AsRef<Path>>( pub fn compile_to_executable<P: AsRef<Path>>(
&mut self, &mut self,
mir_module: MirModule, mir_module: MirModule,
output_path: P output_path: P,
) -> Result<(), AotError> { ) -> Result<(), AotError> {
// For now, just create a .cwasm precompiled module // For now, just create a .cwasm precompiled module
// TODO: Implement full standalone executable generation // TODO: Implement full standalone executable generation
@ -97,7 +91,7 @@ impl AotBackend {
pub fn compile_to_precompiled<P: AsRef<Path>>( pub fn compile_to_precompiled<P: AsRef<Path>>(
&mut self, &mut self,
mir_module: MirModule, mir_module: MirModule,
output_path: P output_path: P,
) -> Result<(), AotError> { ) -> Result<(), AotError> {
// Compile MIR to WASM // Compile MIR to WASM
let wasm_bytes = self.compiler.compile_mir_to_wasm(mir_module)?; let wasm_bytes = self.compiler.compile_mir_to_wasm(mir_module)?;

View File

@ -5,8 +5,8 @@
* Status: Initial skeleton for future extraction from vm.rs * Status: Initial skeleton for future extraction from vm.rs
*/ */
use super::vm::VMError;
use crate::mir::BasicBlockId; use crate::mir::BasicBlockId;
use super::vm::{VMError};
/// Result of a block step when evaluating a terminator /// Result of a block step when evaluating a terminator
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -30,4 +30,3 @@ pub fn record_transition(
loop_recorder.record_transition(from, to); loop_recorder.record_transition(from, to);
Ok(()) Ok(())
} }

View File

@ -7,11 +7,16 @@
#![cfg(feature = "cranelift-jit")] #![cfg(feature = "cranelift-jit")]
use crate::jit::lower::builder::{IRBuilder, BinOpKind, CmpKind, ParamKind}; use crate::jit::lower::builder::{BinOpKind, CmpKind, IRBuilder, ParamKind};
use cranelift_codegen::ir::InstBuilder; use cranelift_codegen::ir::InstBuilder;
// Minimal recorded opcodes for Const/Add/Return first // Minimal recorded opcodes for Const/Add/Return first
enum RecOp { ConstI64(i64), ConstF64(f64), BinOp(BinOpKind), Return } enum RecOp {
ConstI64(i64),
ConstF64(f64),
BinOp(BinOpKind),
Return,
}
pub struct ClifBuilder { pub struct ClifBuilder {
pub consts: usize, pub consts: usize,
@ -23,24 +28,36 @@ pub struct ClifBuilder {
} }
impl ClifBuilder { impl ClifBuilder {
pub fn new() -> Self { Self { consts: 0, binops: 0, cmps: 0, branches: 0, rets: 0, ops: Vec::new() } } pub fn new() -> Self {
Self {
consts: 0,
binops: 0,
cmps: 0,
branches: 0,
rets: 0,
ops: Vec::new(),
}
}
/// Build and execute the recorded ops as a native function using Cranelift /// Build and execute the recorded ops as a native function using Cranelift
pub fn finish_and_execute(&self) -> Result<i64, String> { pub fn finish_and_execute(&self) -> Result<i64, String> {
use cranelift_codegen::ir::{Signature, AbiParam, types}; use cranelift_codegen::ir::{types, AbiParam, Signature};
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use cranelift_module::{Module, Linkage}; use cranelift_module::{Linkage, Module};
// JIT setup // JIT setup
let isa_builder = cranelift_native::builder().map_err(|e| e.to_string())?; let isa_builder = cranelift_native::builder().map_err(|e| e.to_string())?;
let flag_builder = cranelift_codegen::settings::builder(); let flag_builder = cranelift_codegen::settings::builder();
let flags = cranelift_codegen::settings::Flags::new(flag_builder); let flags = cranelift_codegen::settings::Flags::new(flag_builder);
let isa = isa_builder.finish(flags).map_err(|e| e.to_string())?; let isa = isa_builder.finish(flags).map_err(|e| e.to_string())?;
let jit_builder = cranelift_jit::JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); let jit_builder =
cranelift_jit::JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
let mut module = cranelift_jit::JITModule::new(jit_builder); let mut module = cranelift_jit::JITModule::new(jit_builder);
// Signature ()->i64 // Signature ()->i64
let mut sig = Signature::new(module.target_config().default_call_conv); let mut sig = Signature::new(module.target_config().default_call_conv);
sig.returns.push(AbiParam::new(types::I64)); sig.returns.push(AbiParam::new(types::I64));
let func_id = module.declare_function("ny_lowercore_main", Linkage::Export, &sig).map_err(|e| e.to_string())?; let func_id = module
.declare_function("ny_lowercore_main", Linkage::Export, &sig)
.map_err(|e| e.to_string())?;
let mut ctx = module.make_context(); let mut ctx = module.make_context();
ctx.func.signature = sig; ctx.func.signature = sig;
let mut fbc = FunctionBuilderContext::new(); let mut fbc = FunctionBuilderContext::new();
@ -53,16 +70,31 @@ impl ClifBuilder {
let mut did_return = false; let mut did_return = false;
for op in &self.ops { for op in &self.ops {
match *op { match *op {
RecOp::ConstI64(i) => { vs.push(fb.ins().iconst(types::I64, i)); } RecOp::ConstI64(i) => {
RecOp::ConstF64(f) => { let fv = fb.ins().f64const(f); let iv = fb.ins().fcvt_to_sint(types::I64, fv); vs.push(iv); } vs.push(fb.ins().iconst(types::I64, i));
}
RecOp::ConstF64(f) => {
let fv = fb.ins().f64const(f);
let iv = fb.ins().fcvt_to_sint(types::I64, fv);
vs.push(iv);
}
RecOp::BinOp(BinOpKind::Add) => { RecOp::BinOp(BinOpKind::Add) => {
if vs.len() < 2 { vs.clear(); vs.push(fb.ins().iconst(types::I64, 0)); } else { if vs.len() < 2 {
let r = vs.pop().unwrap(); let l = vs.pop().unwrap(); vs.push(fb.ins().iadd(l, r)); vs.clear();
vs.push(fb.ins().iconst(types::I64, 0));
} else {
let r = vs.pop().unwrap();
let l = vs.pop().unwrap();
vs.push(fb.ins().iadd(l, r));
} }
} }
RecOp::BinOp(_) => { /* ignore others for now */ } RecOp::BinOp(_) => { /* ignore others for now */ }
RecOp::Return => { RecOp::Return => {
let retv = if let Some(v) = vs.last().copied() { v } else { fb.ins().iconst(types::I64, 0) }; let retv = if let Some(v) = vs.last().copied() {
v
} else {
fb.ins().iconst(types::I64, 0)
};
fb.ins().return_(&[retv]); fb.ins().return_(&[retv]);
did_return = true; did_return = true;
} }
@ -70,12 +102,18 @@ impl ClifBuilder {
} }
// Ensure function ends with return // Ensure function ends with return
if !did_return { if !did_return {
let retv = if let Some(v) = vs.last().copied() { v } else { fb.ins().iconst(types::I64, 0) }; let retv = if let Some(v) = vs.last().copied() {
v
} else {
fb.ins().iconst(types::I64, 0)
};
fb.ins().return_(&[retv]); fb.ins().return_(&[retv]);
} }
fb.seal_block(entry); fb.seal_block(entry);
fb.finalize(); fb.finalize();
module.define_function(func_id, &mut ctx).map_err(|e| e.to_string())?; module
.define_function(func_id, &mut ctx)
.map_err(|e| e.to_string())?;
module.clear_context(&mut ctx); module.clear_context(&mut ctx);
let _ = module.finalize_definitions(); let _ = module.finalize_definitions();
let code = module.get_finalized_function(func_id); let code = module.get_finalized_function(func_id);
@ -90,16 +128,40 @@ impl IRBuilder for ClifBuilder {
fn prepare_signature_i64(&mut self, _argc: usize, _has_ret: bool) {} fn prepare_signature_i64(&mut self, _argc: usize, _has_ret: bool) {}
fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) {} fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) {}
fn emit_param_i64(&mut self, _index: usize) {} fn emit_param_i64(&mut self, _index: usize) {}
fn emit_const_i64(&mut self, val: i64) { self.consts += 1; self.ops.push(RecOp::ConstI64(val)); } fn emit_const_i64(&mut self, val: i64) {
fn emit_const_f64(&mut self, val: f64) { self.consts += 1; self.ops.push(RecOp::ConstF64(val)); } self.consts += 1;
fn emit_binop(&mut self, op: BinOpKind) { self.binops += 1; self.ops.push(RecOp::BinOp(op)); } self.ops.push(RecOp::ConstI64(val));
fn emit_compare(&mut self, _op: CmpKind) { self.cmps += 1; } }
fn emit_const_f64(&mut self, val: f64) {
self.consts += 1;
self.ops.push(RecOp::ConstF64(val));
}
fn emit_binop(&mut self, op: BinOpKind) {
self.binops += 1;
self.ops.push(RecOp::BinOp(op));
}
fn emit_compare(&mut self, _op: CmpKind) {
self.cmps += 1;
}
fn emit_jump(&mut self) {} fn emit_jump(&mut self) {}
fn emit_branch(&mut self) { self.branches += 1; } fn emit_branch(&mut self) {
fn emit_return(&mut self) { self.rets += 1; self.ops.push(RecOp::Return); } self.branches += 1;
}
fn emit_return(&mut self) {
self.rets += 1;
self.ops.push(RecOp::Return);
}
fn emit_host_call(&mut self, _symbol: &str, _argc: usize, _has_ret: bool) {} fn emit_host_call(&mut self, _symbol: &str, _argc: usize, _has_ret: bool) {}
fn emit_host_call_typed(&mut self, _symbol: &str, _params: &[ParamKind], _has_ret: bool, _ret_is_f64: bool) { } fn emit_host_call_typed(
fn emit_plugin_invoke(&mut self, _type_id: u32, _method_id: u32, _argc: usize, _has_ret: bool) { } &mut self,
_symbol: &str,
_params: &[ParamKind],
_has_ret: bool,
_ret_is_f64: bool,
) {
}
fn emit_plugin_invoke(&mut self, _type_id: u32, _method_id: u32, _argc: usize, _has_ret: bool) {
}
fn emit_plugin_invoke_by_name(&mut self, _method: &str, _argc: usize, _has_ret: bool) {} fn emit_plugin_invoke_by_name(&mut self, _method: &str, _argc: usize, _has_ret: bool) {}
fn prepare_blocks(&mut self, _count: usize) {} fn prepare_blocks(&mut self, _count: usize) {}
fn switch_to_block(&mut self, _index: usize) {} fn switch_to_block(&mut self, _index: usize) {}
@ -107,13 +169,31 @@ impl IRBuilder for ClifBuilder {
fn br_if_top_is_true(&mut self, _then_index: usize, _else_index: usize) {} fn br_if_top_is_true(&mut self, _then_index: usize, _else_index: usize) {}
fn jump_to(&mut self, _target_index: usize) {} fn jump_to(&mut self, _target_index: usize) {}
fn ensure_block_params_i64(&mut self, _index: usize, _count: usize) {} fn ensure_block_params_i64(&mut self, _index: usize, _count: usize) {}
fn ensure_block_params_b1(&mut self, index: usize, count: usize) { self.ensure_block_params_i64(index, count); } fn ensure_block_params_b1(&mut self, index: usize, count: usize) {
fn ensure_block_param_i64(&mut self, index: usize) { self.ensure_block_params_i64(index, 1); } self.ensure_block_params_i64(index, count);
}
fn ensure_block_param_i64(&mut self, index: usize) {
self.ensure_block_params_i64(index, 1);
}
fn push_block_param_i64_at(&mut self, _pos: usize) {} fn push_block_param_i64_at(&mut self, _pos: usize) {}
fn push_block_param_b1_at(&mut self, pos: usize) { self.push_block_param_i64_at(pos); } fn push_block_param_b1_at(&mut self, pos: usize) {
fn push_block_param_i64(&mut self) { self.push_block_param_i64_at(0); } self.push_block_param_i64_at(pos);
fn br_if_with_args(&mut self, _then_index: usize, _else_index: usize, _then_n: usize, _else_n: usize) { self.emit_branch(); } }
fn jump_with_args(&mut self, _target_index: usize, _n: usize) { self.emit_jump(); } fn push_block_param_i64(&mut self) {
self.push_block_param_i64_at(0);
}
fn br_if_with_args(
&mut self,
_then_index: usize,
_else_index: usize,
_then_n: usize,
_else_n: usize,
) {
self.emit_branch();
}
fn jump_with_args(&mut self, _target_index: usize, _n: usize) {
self.emit_jump();
}
fn hint_ret_bool(&mut self, _is_b1: bool) {} fn hint_ret_bool(&mut self, _is_b1: bool) {}
fn ensure_local_i64(&mut self, _index: usize) {} fn ensure_local_i64(&mut self, _index: usize) {}
fn store_local_i64(&mut self, _index: usize) {} fn store_local_i64(&mut self, _index: usize) {}

View File

@ -2,10 +2,10 @@
use std::collections::HashMap; use std::collections::HashMap;
use cranelift_codegen::ir::{Block, Signature, AbiParam, types}; use cranelift_codegen::ir::{types, AbiParam, Block, Signature};
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use cranelift_jit::{JITBuilder, JITModule}; use cranelift_jit::{JITBuilder, JITModule};
use cranelift_module::{Module, Linkage, FuncId}; use cranelift_module::{FuncId, Linkage, Module};
use crate::mir::{BasicBlockId, MirFunction, ValueId}; use crate::mir::{BasicBlockId, MirFunction, ValueId};
@ -13,9 +13,15 @@ use crate::mir::{BasicBlockId, MirFunction, ValueId};
pub struct BlockMap(pub HashMap<BasicBlockId, Block>); pub struct BlockMap(pub HashMap<BasicBlockId, Block>);
impl BlockMap { impl BlockMap {
pub fn new() -> Self { Self(HashMap::new()) } pub fn new() -> Self {
pub fn get(&self, bb: &BasicBlockId) -> Option<&Block> { self.0.get(bb) } Self(HashMap::new())
pub fn insert(&mut self, bb: BasicBlockId, blk: Block) { self.0.insert(bb, blk); } }
pub fn get(&self, bb: &BasicBlockId) -> Option<&Block> {
self.0.get(bb)
}
pub fn insert(&mut self, bb: BasicBlockId, blk: Block) {
self.0.insert(bb, blk);
}
/// Create a CLIF block for each MIR block id /// Create a CLIF block for each MIR block id
pub fn create_for_function(func: &MirFunction, builder: &mut FunctionBuilder) -> Self { pub fn create_for_function(func: &MirFunction, builder: &mut FunctionBuilder) -> Self {
let mut m = HashMap::new(); let mut m = HashMap::new();
@ -33,15 +39,31 @@ pub struct ValueEnv {
} }
impl ValueEnv { impl ValueEnv {
pub fn new() -> Self { Self { vals: HashMap::new(), mem: HashMap::new() } } pub fn new() -> Self {
pub fn get_val(&self, id: &ValueId) -> Result<cranelift_codegen::ir::Value, String> { Self {
self.vals.get(id).cloned().ok_or_else(|| format!("undef {:?}", id)) vals: HashMap::new(),
mem: HashMap::new(),
} }
pub fn set_val(&mut self, id: ValueId, v: cranelift_codegen::ir::Value) { self.vals.insert(id, v); } }
pub fn get_mem_or(&self, id: &ValueId, default: cranelift_codegen::ir::Value) -> cranelift_codegen::ir::Value { pub fn get_val(&self, id: &ValueId) -> Result<cranelift_codegen::ir::Value, String> {
self.vals
.get(id)
.cloned()
.ok_or_else(|| format!("undef {:?}", id))
}
pub fn set_val(&mut self, id: ValueId, v: cranelift_codegen::ir::Value) {
self.vals.insert(id, v);
}
pub fn get_mem_or(
&self,
id: &ValueId,
default: cranelift_codegen::ir::Value,
) -> cranelift_codegen::ir::Value {
*self.mem.get(id).unwrap_or(&default) *self.mem.get(id).unwrap_or(&default)
} }
pub fn set_mem(&mut self, id: ValueId, v: cranelift_codegen::ir::Value) { self.mem.insert(id, v); } pub fn set_mem(&mut self, id: ValueId, v: cranelift_codegen::ir::Value) {
self.mem.insert(id, v);
}
} }
/// Cranelift JIT module wrapper (context) /// Cranelift JIT module wrapper (context)
@ -56,24 +78,37 @@ impl ClifContext {
let flags = cranelift_codegen::settings::Flags::new(flag_builder); let flags = cranelift_codegen::settings::Flags::new(flag_builder);
let isa = isa_builder.finish(flags).map_err(|e| e.to_string())?; let isa = isa_builder.finish(flags).map_err(|e| e.to_string())?;
let jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); let jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
Ok(Self { module: JITModule::new(jit_builder) }) Ok(Self {
module: JITModule::new(jit_builder),
})
} }
/// Declare an exported i64-return function and return its id and Cranelift context/signature /// Declare an exported i64-return function and return its id and Cranelift context/signature
pub fn declare_i64_fn(&mut self, name: &str) -> Result<(FuncId, cranelift_codegen::Context, Signature), String> { pub fn declare_i64_fn(
&mut self,
name: &str,
) -> Result<(FuncId, cranelift_codegen::Context, Signature), String> {
let mut sig = Signature::new(self.module.target_config().default_call_conv); let mut sig = Signature::new(self.module.target_config().default_call_conv);
sig.returns.push(AbiParam::new(types::I64)); sig.returns.push(AbiParam::new(types::I64));
let func_id = self.module.declare_function(name, Linkage::Export, &sig).map_err(|e| e.to_string())?; let func_id = self
.module
.declare_function(name, Linkage::Export, &sig)
.map_err(|e| e.to_string())?;
let mut ctx = self.module.make_context(); let mut ctx = self.module.make_context();
ctx.func.signature = sig.clone(); ctx.func.signature = sig.clone();
Ok((func_id, ctx, sig)) Ok((func_id, ctx, sig))
} }
pub fn finalize(&mut self, func_id: FuncId, ctx: &mut cranelift_codegen::Context) -> Result<*const u8, String> { pub fn finalize(
self.module.define_function(func_id, ctx).map_err(|e| e.to_string())?; &mut self,
func_id: FuncId,
ctx: &mut cranelift_codegen::Context,
) -> Result<*const u8, String> {
self.module
.define_function(func_id, ctx)
.map_err(|e| e.to_string())?;
self.module.clear_context(ctx); self.module.clear_context(ctx);
let _ = self.module.finalize_definitions(); let _ = self.module.finalize_definitions();
Ok(self.module.get_finalized_function(func_id)) Ok(self.module.get_finalized_function(func_id))
} }
} }

View File

@ -1,12 +1,15 @@
#![cfg(feature = "cranelift-jit")] #![cfg(feature = "cranelift-jit")]
use cranelift_codegen::ir::{AbiParam, InstBuilder, Signature, types, condcodes::IntCC, StackSlot, StackSlotData, StackSlotKind}; use cranelift_codegen::ir::{
condcodes::IntCC, types, AbiParam, InstBuilder, Signature, StackSlot, StackSlotData,
StackSlotKind,
};
use cranelift_codegen::isa; use cranelift_codegen::isa;
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use cranelift_jit::{JITBuilder, JITModule}; use cranelift_jit::{JITBuilder, JITModule};
use cranelift_module::{Module, Linkage}; use cranelift_module::{Linkage, Module};
use crate::mir::{MirFunction, MirInstruction, ConstValue, ValueId, BasicBlockId, CompareOp}; use crate::mir::{BasicBlockId, CompareOp, ConstValue, MirFunction, MirInstruction, ValueId};
/// Compile a minimal subset of MIR(main) to a native function and execute it. /// Compile a minimal subset of MIR(main) to a native function and execute it.
/// Supported: Const(Integer), BinOp(Add for integers), Return(Integer or default 0). /// Supported: Const(Integer), BinOp(Add for integers), Return(Integer or default 0).
@ -22,8 +25,11 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
// Signature: () -> i64 // Signature: () -> i64
let mut sig = Signature::new(module.target_config().default_call_conv); let mut sig = Signature::new(module.target_config().default_call_conv);
sig.returns.push(AbiParam::new(cranelift_codegen::ir::types::I64)); sig.returns
let func_id = module.declare_function("ny_main", Linkage::Export, &sig).map_err(|e| e.to_string())?; .push(AbiParam::new(cranelift_codegen::ir::types::I64));
let func_id = module
.declare_function("ny_main", Linkage::Export, &sig)
.map_err(|e| e.to_string())?;
let mut ctx = module.make_context(); let mut ctx = module.make_context();
ctx.func.signature = sig; ctx.func.signature = sig;
@ -36,7 +42,9 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
let mut vals: HashMap<ValueId, cranelift_codegen::ir::Value> = HashMap::new(); let mut vals: HashMap<ValueId, cranelift_codegen::ir::Value> = HashMap::new();
let mut slots: HashMap<ValueId, StackSlot> = HashMap::new(); let mut slots: HashMap<ValueId, StackSlot> = HashMap::new();
for (bb_id, _) in &main.blocks { clif_blocks.insert(*bb_id, builder.create_block()); } for (bb_id, _) in &main.blocks {
clif_blocks.insert(*bb_id, builder.create_block());
}
// Switch to entry // Switch to entry
let entry = *clif_blocks.get(&main.entry_block).unwrap(); let entry = *clif_blocks.get(&main.entry_block).unwrap();
builder.switch_to_block(entry); builder.switch_to_block(entry);
@ -55,9 +63,16 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
MirInstruction::Const { dst, value } => { MirInstruction::Const { dst, value } => {
let v = match value { let v = match value {
ConstValue::Integer(i) => builder.ins().iconst(types::I64, *i), ConstValue::Integer(i) => builder.ins().iconst(types::I64, *i),
ConstValue::Bool(b) => builder.ins().iconst(types::I64, if *b { 1 } else { 0 }), ConstValue::Bool(b) => {
ConstValue::Float(f) => { let fv = builder.ins().f64const(*f); builder.ins().fcvt_to_sint(types::I64, fv) }, builder.ins().iconst(types::I64, if *b { 1 } else { 0 })
ConstValue::String(_) | ConstValue::Null | ConstValue::Void => builder.ins().iconst(types::I64, 0), }
ConstValue::Float(f) => {
let fv = builder.ins().f64const(*f);
builder.ins().fcvt_to_sint(types::I64, fv)
}
ConstValue::String(_) | ConstValue::Null | ConstValue::Void => {
builder.ins().iconst(types::I64, 0)
}
}; };
vals.insert(*dst, v); vals.insert(*dst, v);
} }
@ -78,7 +93,14 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
MirInstruction::Compare { dst, op, lhs, rhs } => { MirInstruction::Compare { dst, op, lhs, rhs } => {
let l = *vals.get(lhs).ok_or_else(|| format!("undef {:?}", lhs))?; let l = *vals.get(lhs).ok_or_else(|| format!("undef {:?}", lhs))?;
let r = *vals.get(rhs).ok_or_else(|| format!("undef {:?}", rhs))?; let r = *vals.get(rhs).ok_or_else(|| format!("undef {:?}", rhs))?;
let cc = match op { CompareOp::Eq => IntCC::Equal, CompareOp::Ne => IntCC::NotEqual, CompareOp::Lt => IntCC::SignedLessThan, CompareOp::Le => IntCC::SignedLessThanOrEqual, CompareOp::Gt => IntCC::SignedGreaterThan, CompareOp::Ge => IntCC::SignedGreaterThanOrEqual }; let cc = match op {
CompareOp::Eq => IntCC::Equal,
CompareOp::Ne => IntCC::NotEqual,
CompareOp::Lt => IntCC::SignedLessThan,
CompareOp::Le => IntCC::SignedLessThanOrEqual,
CompareOp::Gt => IntCC::SignedGreaterThan,
CompareOp::Ge => IntCC::SignedGreaterThanOrEqual,
};
let b1 = builder.ins().icmp(cc, l, r); let b1 = builder.ins().icmp(cc, l, r);
let one = builder.ins().iconst(types::I64, 1); let one = builder.ins().iconst(types::I64, 1);
let zero = builder.ins().iconst(types::I64, 0); let zero = builder.ins().iconst(types::I64, 0);
@ -94,8 +116,15 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
} }
} }
MirInstruction::Store { value, ptr } => { MirInstruction::Store { value, ptr } => {
let v = *vals.get(value).ok_or_else(|| format!("undef {:?}", value))?; let v = *vals
let ss = *slots.entry(*ptr).or_insert_with(|| builder.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8))); .get(value)
.ok_or_else(|| format!("undef {:?}", value))?;
let ss = *slots.entry(*ptr).or_insert_with(|| {
builder.create_sized_stack_slot(StackSlotData::new(
StackSlotKind::ExplicitSlot,
8,
))
});
builder.ins().stack_store(v, ss, 0); builder.ins().stack_store(v, ss, 0);
} }
MirInstruction::Copy { dst, src } => { MirInstruction::Copy { dst, src } => {
@ -108,21 +137,32 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
// Terminator // Terminator
match &bb.terminator { match &bb.terminator {
Some(MirInstruction::Return { value }) => { Some(MirInstruction::Return { value }) => {
let retv = if let Some(v) = value { *vals.get(v).unwrap_or(&builder.ins().iconst(types::I64, 0)) } else { builder.ins().iconst(types::I64, 0) }; let retv = if let Some(v) = value {
*vals.get(v).unwrap_or(&builder.ins().iconst(types::I64, 0))
} else {
builder.ins().iconst(types::I64, 0)
};
builder.ins().return_(&[retv]); builder.ins().return_(&[retv]);
} }
Some(MirInstruction::Jump { target }) => { Some(MirInstruction::Jump { target }) => {
let t = *clif_blocks.get(target).unwrap(); let t = *clif_blocks.get(target).unwrap();
builder.ins().jump(t, &[]); builder.ins().jump(t, &[]);
} }
Some(MirInstruction::Branch { condition, then_bb, else_bb }) => { Some(MirInstruction::Branch {
let cond_i64 = *vals.get(condition).unwrap_or(&builder.ins().iconst(types::I64, 0)); condition,
then_bb,
else_bb,
}) => {
let cond_i64 = *vals
.get(condition)
.unwrap_or(&builder.ins().iconst(types::I64, 0));
let is_true = builder.ins().icmp_imm(IntCC::NotEqual, cond_i64, 0); let is_true = builder.ins().icmp_imm(IntCC::NotEqual, cond_i64, 0);
let tb = *clif_blocks.get(then_bb).unwrap(); let tb = *clif_blocks.get(then_bb).unwrap();
let eb = *clif_blocks.get(else_bb).unwrap(); let eb = *clif_blocks.get(else_bb).unwrap();
builder.ins().brif(is_true, tb, &[], eb, &[]); builder.ins().brif(is_true, tb, &[], eb, &[]);
} }
_ => { /* fallthrough not allowed: insert return 0 to keep verifier happy */ _ => {
/* fallthrough not allowed: insert return 0 to keep verifier happy */
let z = builder.ins().iconst(types::I64, 0); let z = builder.ins().iconst(types::I64, 0);
builder.ins().return_(&[z]); builder.ins().return_(&[z]);
} }
@ -132,7 +172,9 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
builder.finalize(); builder.finalize();
module.define_function(func_id, &mut ctx).map_err(|e| e.to_string())?; module
.define_function(func_id, &mut ctx)
.map_err(|e| e.to_string())?;
module.clear_context(&mut ctx); module.clear_context(&mut ctx);
let _ = module.finalize_definitions(); let _ = module.finalize_definitions();

View File

@ -6,31 +6,43 @@
#![cfg(feature = "cranelift-jit")] #![cfg(feature = "cranelift-jit")]
use crate::mir::{function::MirModule, MirInstruction, ConstValue, ValueId, BinaryOp}; use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox};
use crate::jit::lower::{
builder::{IRBuilder, NoopBuilder},
core::LowerCore,
};
use crate::jit::semantics::clif::ClifSemanticsSkeleton;
use crate::mir::{function::MirModule, BinaryOp, ConstValue, MirInstruction, ValueId};
#[cfg(feature = "cranelift-jit")] #[cfg(feature = "cranelift-jit")]
use crate::semantics::Semantics; use crate::semantics::Semantics;
use crate::box_trait::{NyashBox, IntegerBox, BoolBox, StringBox, VoidBox};
use std::collections::HashMap; use std::collections::HashMap;
use crate::jit::lower::{builder::{NoopBuilder, IRBuilder}, core::LowerCore};
use crate::jit::semantics::clif::ClifSemanticsSkeleton;
pub mod context; // Context/Block/Value env wrappers pub mod builder;
pub mod builder; // Clif IRBuilder implementation (skeleton) pub mod context; // Context/Block/Value env wrappers // Clif IRBuilder implementation (skeleton)
pub mod lower {} pub mod lower {}
pub mod jit; // JIT compile/execute using Cranelift (minimal) pub mod jit; // JIT compile/execute using Cranelift (minimal)
pub mod object {} pub mod object {}
/// JIT: compile and execute a MIR module (skeleton) /// JIT: compile and execute a MIR module (skeleton)
pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result<Box<dyn NyashBox>, String> { pub fn compile_and_execute(
mir_module: &MirModule,
_temp_name: &str,
) -> Result<Box<dyn NyashBox>, String> {
// Minimal semantics: Const/Return/Add only (straight-line code) // Minimal semantics: Const/Return/Add only (straight-line code)
let main = mir_module.functions.get("main").ok_or("missing main function")?; let main = mir_module
.functions
.get("main")
.ok_or("missing main function")?;
// Minimal ClifSem lowering pass (NoopBuilder): Const/Return/Add カバレッジ確認 // Minimal ClifSem lowering pass (NoopBuilder): Const/Return/Add カバレッジ確認
if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") { if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") {
let mut builder = NoopBuilder::new(); let mut builder = NoopBuilder::new();
let mut lower = LowerCore::new(); let mut lower = LowerCore::new();
let _ = lower.lower_function(main, &mut builder); let _ = lower.lower_function(main, &mut builder);
eprintln!("[CLIF-LOWER] covered={} unsupported={}", lower.covered, lower.unsupported); eprintln!(
"[CLIF-LOWER] covered={} unsupported={}",
lower.covered, lower.unsupported
);
} }
// まずは新JITエンジン経路を試すLowerCore -> CraneliftBuilder -> 実行) // まずは新JITエンジン経路を試すLowerCore -> CraneliftBuilder -> 実行)
@ -41,10 +53,13 @@ pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result<B
// 実行(引数なし)。戻り値は MIR の型に合わせて変換 // 実行(引数なし)。戻り値は MIR の型に合わせて変換
let out = engine.execute_handle(h, &[]); let out = engine.execute_handle(h, &[]);
if let Some(jv) = out { if let Some(jv) = out {
let vmv = crate::jit::boundary::CallBoundaryBox::to_vm(&main.signature.return_type, jv); let vmv =
crate::jit::boundary::CallBoundaryBox::to_vm(&main.signature.return_type, jv);
let boxed: Box<dyn NyashBox> = match vmv { let boxed: Box<dyn NyashBox> = match vmv {
crate::backend::vm::VMValue::Integer(i) => Box::new(IntegerBox::new(i)), crate::backend::vm::VMValue::Integer(i) => Box::new(IntegerBox::new(i)),
crate::backend::vm::VMValue::Float(f) => Box::new(crate::boxes::FloatBox::new(f)), crate::backend::vm::VMValue::Float(f) => {
Box::new(crate::boxes::FloatBox::new(f))
}
crate::backend::vm::VMValue::Bool(b) => Box::new(BoolBox::new(b)), crate::backend::vm::VMValue::Bool(b) => Box::new(BoolBox::new(b)),
crate::backend::vm::VMValue::String(s) => Box::new(StringBox::new(&s)), crate::backend::vm::VMValue::String(s) => Box::new(StringBox::new(&s)),
crate::backend::vm::VMValue::BoxRef(b) => b.share_box(), crate::backend::vm::VMValue::BoxRef(b) => b.share_box(),
@ -63,13 +78,24 @@ pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result<B
let mut cur = main.entry_block; let mut cur = main.entry_block;
let mut last_pred: Option<crate::mir::BasicBlockId> = None; let mut last_pred: Option<crate::mir::BasicBlockId> = None;
loop { loop {
let bb = main.blocks.get(&cur).ok_or_else(|| format!("invalid bb {:?}", cur))?; let bb = main
.blocks
.get(&cur)
.ok_or_else(|| format!("invalid bb {:?}", cur))?;
// PHI (very minimal): choose first input or predecessor match // PHI (very minimal): choose first input or predecessor match
for inst in &bb.instructions { for inst in &bb.instructions {
if let MirInstruction::Phi { dst, inputs } = inst { if let MirInstruction::Phi { dst, inputs } = inst {
if let Some(pred) = last_pred { if let Some(pred) = last_pred {
if let Some((_, v)) = inputs.iter().find(|(b, _)| *b == pred) { if let Some(val) = regs.get(v).cloned() { regs.insert(*dst, val); } } if let Some((_, v)) = inputs.iter().find(|(b, _)| *b == pred) {
} else if let Some((_, v)) = inputs.first() { if let Some(val) = regs.get(v).cloned() { regs.insert(*dst, val); } } if let Some(val) = regs.get(v).cloned() {
regs.insert(*dst, val);
}
}
} else if let Some((_, v)) = inputs.first() {
if let Some(val) = regs.get(v).cloned() {
regs.insert(*dst, val);
}
}
} }
} }
let mut sem = ClifSemanticsSkeleton::new(); let mut sem = ClifSemanticsSkeleton::new();
@ -87,36 +113,84 @@ pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result<B
} }
MirInstruction::BinOp { dst, op, lhs, rhs } if matches!(op, BinaryOp::Add) => { MirInstruction::BinOp { dst, op, lhs, rhs } if matches!(op, BinaryOp::Add) => {
use crate::backend::vm::VMValue as V; use crate::backend::vm::VMValue as V;
let a = regs.get(lhs).cloned().ok_or_else(|| format!("undef {:?}", lhs))?; let a = regs
let b = regs.get(rhs).cloned().ok_or_else(|| format!("undef {:?}", rhs))?; .get(lhs)
.cloned()
.ok_or_else(|| format!("undef {:?}", lhs))?;
let b = regs
.get(rhs)
.cloned()
.ok_or_else(|| format!("undef {:?}", rhs))?;
let out = sem.add(a, b); let out = sem.add(a, b);
regs.insert(*dst, out); regs.insert(*dst, out);
} }
MirInstruction::Copy { dst, src } => { MirInstruction::Copy { dst, src } => {
if let Some(v) = regs.get(src).cloned() { regs.insert(*dst, v); } if let Some(v) = regs.get(src).cloned() {
regs.insert(*dst, v);
} }
MirInstruction::Debug { .. } | MirInstruction::Print { .. } | MirInstruction::Barrier { .. } | MirInstruction::BarrierRead { .. } | MirInstruction::BarrierWrite { .. } | MirInstruction::Safepoint | MirInstruction::Load { .. } | MirInstruction::Store { .. } | MirInstruction::TypeOp { .. } | MirInstruction::Compare { .. } | MirInstruction::NewBox { .. } | MirInstruction::PluginInvoke { .. } | MirInstruction::BoxCall { .. } | MirInstruction::RefGet { .. } | MirInstruction::RefSet { .. } | MirInstruction::WeakRef { .. } | MirInstruction::FutureNew { .. } | MirInstruction::FutureSet { .. } | MirInstruction::Await { .. } | MirInstruction::Throw { .. } | MirInstruction::Catch { .. } => { }
MirInstruction::Debug { .. }
| MirInstruction::Print { .. }
| MirInstruction::Barrier { .. }
| MirInstruction::BarrierRead { .. }
| MirInstruction::BarrierWrite { .. }
| MirInstruction::Safepoint
| MirInstruction::Load { .. }
| MirInstruction::Store { .. }
| MirInstruction::TypeOp { .. }
| MirInstruction::Compare { .. }
| MirInstruction::NewBox { .. }
| MirInstruction::PluginInvoke { .. }
| MirInstruction::BoxCall { .. }
| MirInstruction::RefGet { .. }
| MirInstruction::RefSet { .. }
| MirInstruction::WeakRef { .. }
| MirInstruction::FutureNew { .. }
| MirInstruction::FutureSet { .. }
| MirInstruction::Await { .. }
| MirInstruction::Throw { .. }
| MirInstruction::Catch { .. } => {
// ignore for minimal path // ignore for minimal path
} }
MirInstruction::ExternCall { dst, iface_name, method_name, args, .. } => { MirInstruction::ExternCall {
dst,
iface_name,
method_name,
args,
..
} => {
use crate::backend::vm::VMValue as V; use crate::backend::vm::VMValue as V;
match (iface_name.as_str(), method_name.as_str()) { match (iface_name.as_str(), method_name.as_str()) {
("env.local", "get") => { ("env.local", "get") => {
if let Some(d) = dst { if let Some(a0) = args.get(0) { if let Some(v) = regs.get(a0).cloned() { regs.insert(*d, v); } } } if let Some(d) = dst {
if let Some(a0) = args.get(0) {
if let Some(v) = regs.get(a0).cloned() {
regs.insert(*d, v);
}
}
}
} }
("env.local", "set") => { ("env.local", "set") => {
if args.len() >= 2 { if let Some(v) = regs.get(&args[1]).cloned() { regs.insert(args[0], v); } } if args.len() >= 2 {
if let Some(v) = regs.get(&args[1]).cloned() {
regs.insert(args[0], v);
}
}
// dst ignored // dst ignored
} }
("env.box", "new") => { ("env.box", "new") => {
if let Some(d) = dst { if let Some(d) = dst {
if let Some(a0) = args.get(0) { if let Some(a0) = args.get(0) {
if let Some(V::String(ty)) = regs.get(a0).cloned() { if let Some(V::String(ty)) = regs.get(a0).cloned() {
let reg = crate::runtime::box_registry::get_global_registry(); let reg =
crate::runtime::box_registry::get_global_registry();
// Collect args as NyashBox // Collect args as NyashBox
let mut ny_args: Vec<Box<dyn crate::box_trait::NyashBox>> = Vec::new(); let mut ny_args: Vec<Box<dyn crate::box_trait::NyashBox>> =
Vec::new();
for vid in args.iter().skip(1) { for vid in args.iter().skip(1) {
if let Some(v) = regs.get(vid).cloned() { ny_args.push(v.to_nyash_box()); } if let Some(v) = regs.get(vid).cloned() {
ny_args.push(v.to_nyash_box());
}
} }
if let Ok(b) = reg.create_box(&ty, &ny_args) { if let Ok(b) = reg.create_box(&ty, &ny_args) {
regs.insert(*d, V::from_nyash_box(b)); regs.insert(*d, V::from_nyash_box(b));
@ -151,11 +225,25 @@ pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result<B
}; };
return Ok(out); return Ok(out);
} }
Some(MirInstruction::Jump { target }) => { last_pred = Some(bb.id); cur = *target; } Some(MirInstruction::Jump { target }) => {
Some(MirInstruction::Branch { condition, then_bb, else_bb }) => { last_pred = Some(bb.id);
cur = *target;
}
Some(MirInstruction::Branch {
condition,
then_bb,
else_bb,
}) => {
// Minimal: integer/bool truthiness // Minimal: integer/bool truthiness
let c = regs.get(condition).cloned().unwrap_or(crate::backend::vm::VMValue::Void); let c = regs
let t = match c { crate::backend::vm::VMValue::Bool(b) => b, crate::backend::vm::VMValue::Integer(i) => i != 0, _ => false }; .get(condition)
.cloned()
.unwrap_or(crate::backend::vm::VMValue::Void);
let t = match c {
crate::backend::vm::VMValue::Bool(b) => b,
crate::backend::vm::VMValue::Integer(i) => i != 0,
_ => false,
};
last_pred = Some(bb.id); last_pred = Some(bb.id);
cur = if t { *then_bb } else { *else_bb }; cur = if t { *then_bb } else { *else_bb };
} }

View File

@ -5,15 +5,19 @@
* Status: Initial skeleton; currently unused. Future: build static table for hot-path dispatch. * Status: Initial skeleton; currently unused. Future: build static table for hot-path dispatch.
*/ */
use crate::mir::MirInstruction;
use super::vm::{VM, VMError};
use super::vm::ControlFlow; use super::vm::ControlFlow;
use crate::mir::CompareOp;
use super::vm::VMValue; use super::vm::VMValue;
use super::vm::{VMError, VM};
use crate::mir::CompareOp;
use crate::mir::MirInstruction;
/// Minimal dispatcher that routes a single instruction to the appropriate handler. /// Minimal dispatcher that routes a single instruction to the appropriate handler.
/// Keeps behavior identical to the big match in vm.rs but centralized here. /// Keeps behavior identical to the big match in vm.rs but centralized here.
pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, debug_global: bool) -> Result<ControlFlow, VMError> { pub(super) fn execute_instruction(
vm: &mut VM,
instruction: &MirInstruction,
debug_global: bool,
) -> Result<ControlFlow, VMError> {
match instruction { match instruction {
// Basic operations // Basic operations
MirInstruction::Const { dst, value } => vm.execute_const(*dst, value), MirInstruction::Const { dst, value } => vm.execute_const(*dst, value),
@ -23,30 +27,59 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
eprintln!("[VM] execute_instruction -> BinOp({:?})", op); eprintln!("[VM] execute_instruction -> BinOp({:?})", op);
} }
vm.execute_binop(*dst, op, *lhs, *rhs) vm.execute_binop(*dst, op, *lhs, *rhs)
}, }
MirInstruction::UnaryOp { dst, op, operand } => vm.execute_unaryop(*dst, op, *operand), MirInstruction::UnaryOp { dst, op, operand } => vm.execute_unaryop(*dst, op, *operand),
MirInstruction::Compare { dst, op, lhs, rhs } => { MirInstruction::Compare { dst, op, lhs, rhs } => {
let debug_cmp = debug_global || std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1"); let debug_cmp =
if debug_cmp { eprintln!("[VM] dispatch Compare op={:?} lhs={:?} rhs={:?}", op, lhs, rhs); } debug_global || std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1");
if debug_cmp {
eprintln!(
"[VM] dispatch Compare op={:?} lhs={:?} rhs={:?}",
op, lhs, rhs
);
}
if let (Ok(lv), Ok(rv)) = (vm.get_value(*lhs), vm.get_value(*rhs)) { if let (Ok(lv), Ok(rv)) = (vm.get_value(*lhs), vm.get_value(*rhs)) {
if debug_cmp { eprintln!("[VM] values before fastpath: left={:?} right={:?}", lv, rv); } if debug_cmp {
eprintln!("[VM] values before fastpath: left={:?} right={:?}", lv, rv);
}
if let (VMValue::BoxRef(lb), VMValue::BoxRef(rb)) = (&lv, &rv) { if let (VMValue::BoxRef(lb), VMValue::BoxRef(rb)) = (&lv, &rv) {
if debug_cmp { eprintln!("[VM] BoxRef types: lty={} rty={} lstr={} rstr={}", lb.type_name(), rb.type_name(), lb.to_string_box().value, rb.to_string_box().value); } if debug_cmp {
let li = lb.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value) eprintln!(
"[VM] BoxRef types: lty={} rty={} lstr={} rstr={}",
lb.type_name(),
rb.type_name(),
lb.to_string_box().value,
rb.to_string_box().value
);
}
let li = lb
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| lb.to_string_box().value.trim().parse::<i64>().ok()); .or_else(|| lb.to_string_box().value.trim().parse::<i64>().ok());
let ri = rb.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value) let ri = rb
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| rb.to_string_box().value.trim().parse::<i64>().ok()); .or_else(|| rb.to_string_box().value.trim().parse::<i64>().ok());
if let (Some(li), Some(ri)) = (li, ri) { if let (Some(li), Some(ri)) = (li, ri) {
let out = match op { CompareOp::Eq => li == ri, CompareOp::Ne => li != ri, CompareOp::Lt => li < ri, CompareOp::Le => li <= ri, CompareOp::Gt => li > ri, CompareOp::Ge => li >= ri }; let out = match op {
CompareOp::Eq => li == ri,
CompareOp::Ne => li != ri,
CompareOp::Lt => li < ri,
CompareOp::Le => li <= ri,
CompareOp::Gt => li > ri,
CompareOp::Ge => li >= ri,
};
vm.set_value(*dst, VMValue::Bool(out)); vm.set_value(*dst, VMValue::Bool(out));
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
} }
} }
vm.execute_compare(*dst, op, *lhs, *rhs) vm.execute_compare(*dst, op, *lhs, *rhs)
}, }
// I/O operations // I/O operations
MirInstruction::Print { value, .. } => vm.execute_print(*value), MirInstruction::Print { value, .. } => vm.execute_print(*value),
@ -57,12 +90,20 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
// Control flow // Control flow
MirInstruction::Return { value } => { MirInstruction::Return { value } => {
if crate::config::env::vm_vt_trace() { if crate::config::env::vm_vt_trace() {
if let Some(v) = value { eprintln!("[VT] Dispatch Return val_id={}", v.to_usize()); } else { eprintln!("[VT] Dispatch Return void"); } if let Some(v) = value {
eprintln!("[VT] Dispatch Return val_id={}", v.to_usize());
} else {
eprintln!("[VT] Dispatch Return void");
}
} }
vm.execute_return(*value) vm.execute_return(*value)
}, }
MirInstruction::Jump { target } => vm.execute_jump(*target), MirInstruction::Jump { target } => vm.execute_jump(*target),
MirInstruction::Branch { condition, then_bb, else_bb } => vm.execute_branch(*condition, *then_bb, *else_bb), MirInstruction::Branch {
condition,
then_bb,
else_bb,
} => vm.execute_branch(*condition, *then_bb, *else_bb),
MirInstruction::Phi { dst, inputs } => vm.execute_phi(*dst, inputs), MirInstruction::Phi { dst, inputs } => vm.execute_phi(*dst, inputs),
// Memory operations // Memory operations
@ -71,20 +112,63 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
MirInstruction::Copy { dst, src } => vm.execute_copy(*dst, *src), MirInstruction::Copy { dst, src } => vm.execute_copy(*dst, *src),
// Complex operations // Complex operations
MirInstruction::Call { dst, func, args, effects: _ } => vm.execute_call(*dst, *func, args), MirInstruction::Call {
MirInstruction::FunctionNew { dst, params, body, captures, me } => vm.execute_function_new(*dst, params, body, captures, me), dst,
MirInstruction::BoxCall { dst, box_val, method, method_id, args, effects: _ , .. } => vm.execute_boxcall(*dst, *box_val, method, *method_id, args), func,
MirInstruction::PluginInvoke { dst, box_val, method, args, effects: _ } => vm.execute_plugin_invoke(*dst, *box_val, method, args), args,
MirInstruction::NewBox { dst, box_type, args } => vm.execute_newbox(*dst, box_type, args), effects: _,
} => vm.execute_call(*dst, *func, args),
MirInstruction::FunctionNew {
dst,
params,
body,
captures,
me,
} => vm.execute_function_new(*dst, params, body, captures, me),
MirInstruction::BoxCall {
dst,
box_val,
method,
method_id,
args,
effects: _,
..
} => vm.execute_boxcall(*dst, *box_val, method, *method_id, args),
MirInstruction::PluginInvoke {
dst,
box_val,
method,
args,
effects: _,
} => vm.execute_plugin_invoke(*dst, *box_val, method, args),
MirInstruction::NewBox {
dst,
box_type,
args,
} => vm.execute_newbox(*dst, box_type, args),
// Array operations // Array operations
MirInstruction::ArrayGet { dst, array, index } => vm.execute_array_get(*dst, *array, *index), MirInstruction::ArrayGet { dst, array, index } => {
MirInstruction::ArraySet { array, index, value } => vm.execute_array_set(*array, *index, *value), vm.execute_array_get(*dst, *array, *index)
}
MirInstruction::ArraySet {
array,
index,
value,
} => vm.execute_array_set(*array, *index, *value),
// Reference operations // Reference operations
MirInstruction::RefNew { dst, box_val } => vm.execute_ref_new(*dst, *box_val), MirInstruction::RefNew { dst, box_val } => vm.execute_ref_new(*dst, *box_val),
MirInstruction::RefGet { dst, reference, field } => vm.execute_ref_get(*dst, *reference, field), MirInstruction::RefGet {
MirInstruction::RefSet { reference, field, value } => vm.execute_ref_set(*reference, field, *value), dst,
reference,
field,
} => vm.execute_ref_get(*dst, *reference, field),
MirInstruction::RefSet {
reference,
field,
value,
} => vm.execute_ref_set(*reference, field, *value),
// Weak references // Weak references
MirInstruction::WeakNew { dst, box_val } => vm.execute_weak_new(*dst, *box_val), MirInstruction::WeakNew { dst, box_val } => vm.execute_weak_new(*dst, *box_val),
@ -108,11 +192,16 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
let (func, bb, pc) = vm.gc_site_info(); let (func, bb, pc) = vm.gc_site_info();
eprintln!("[GC] barrier: Write @{} bb={} pc={}", func, bb, pc); eprintln!("[GC] barrier: Write @{} bb={} pc={}", func, bb, pc);
} }
vm.runtime.gc.barrier(crate::runtime::gc::BarrierKind::Write); vm.runtime
.gc
.barrier(crate::runtime::gc::BarrierKind::Write);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
MirInstruction::Barrier { op, .. } => { MirInstruction::Barrier { op, .. } => {
let k = match op { crate::mir::BarrierOp::Read => crate::runtime::gc::BarrierKind::Read, crate::mir::BarrierOp::Write => crate::runtime::gc::BarrierKind::Write }; let k = match op {
crate::mir::BarrierOp::Read => crate::runtime::gc::BarrierKind::Read,
crate::mir::BarrierOp::Write => crate::runtime::gc::BarrierKind::Write,
};
if crate::config::env::gc_trace() { if crate::config::env::gc_trace() {
let (func, bb, pc) = vm.gc_site_info(); let (func, bb, pc) = vm.gc_site_info();
eprintln!("[GC] barrier: {:?} @{} bb={} pc={}", k, func, bb, pc); eprintln!("[GC] barrier: {:?} @{} bb={} pc={}", k, func, bb, pc);
@ -123,7 +212,9 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
// Exceptions // Exceptions
MirInstruction::Throw { exception, .. } => vm.execute_throw(*exception), MirInstruction::Throw { exception, .. } => vm.execute_throw(*exception),
MirInstruction::Catch { exception_value, .. } => vm.execute_catch(*exception_value), MirInstruction::Catch {
exception_value, ..
} => vm.execute_catch(*exception_value),
// Futures // Futures
MirInstruction::FutureNew { dst, value } => { MirInstruction::FutureNew { dst, value } => {
@ -141,15 +232,31 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
future_box.set_result(new_value.to_nyash_box()); future_box.set_result(new_value.to_nyash_box());
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} else { } else {
Err(VMError::TypeError(format!("Expected Future, got {:?}", future_val))) Err(VMError::TypeError(format!(
"Expected Future, got {:?}",
future_val
)))
} }
} }
// Special // Special
MirInstruction::Await { dst, future } => vm.execute_await(*dst, *future), MirInstruction::Await { dst, future } => vm.execute_await(*dst, *future),
MirInstruction::ExternCall { dst, iface_name, method_name, args, .. } => vm.execute_extern_call(*dst, iface_name, method_name, args), MirInstruction::ExternCall {
MirInstruction::TypeCheck { dst, .. } => { vm.set_value(*dst, VMValue::Bool(true)); Ok(ControlFlow::Continue) } dst,
MirInstruction::Cast { dst, value, .. } => { let val = vm.get_value(*value)?; vm.set_value(*dst, val); Ok(ControlFlow::Continue) } iface_name,
method_name,
args,
..
} => vm.execute_extern_call(*dst, iface_name, method_name, args),
MirInstruction::TypeCheck { dst, .. } => {
vm.set_value(*dst, VMValue::Bool(true));
Ok(ControlFlow::Continue)
}
MirInstruction::Cast { dst, value, .. } => {
let val = vm.get_value(*value)?;
vm.set_value(*dst, val);
Ok(ControlFlow::Continue)
}
MirInstruction::Debug { .. } => Ok(ControlFlow::Continue), MirInstruction::Debug { .. } => Ok(ControlFlow::Continue),
MirInstruction::Nop => Ok(ControlFlow::Continue), MirInstruction::Nop => Ok(ControlFlow::Continue),
MirInstruction::Safepoint => { MirInstruction::Safepoint => {
@ -159,9 +266,11 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
} }
vm.runtime.gc.safepoint(); vm.runtime.gc.safepoint();
// Cooperative scheduling: poll single-thread scheduler // Cooperative scheduling: poll single-thread scheduler
if let Some(s) = &vm.runtime.scheduler { s.poll(); } if let Some(s) = &vm.runtime.scheduler {
s.poll();
}
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
}, }
} }
} }
@ -172,11 +281,17 @@ pub struct DispatchEntry;
pub struct DispatchTable; pub struct DispatchTable;
impl DispatchTable { impl DispatchTable {
pub fn new() -> Self { Self } pub fn new() -> Self {
Self
}
/// Example API for future use: resolve a handler for an instruction /// Example API for future use: resolve a handler for an instruction
pub fn resolve(&self, _instr: &MirInstruction) -> Option<DispatchEntry> { None } pub fn resolve(&self, _instr: &MirInstruction) -> Option<DispatchEntry> {
None
}
} }
/// Example execution of a dispatch entry /// Example execution of a dispatch entry
pub fn execute_entry(_entry: &DispatchEntry) -> Result<(), VMError> { Ok(()) } pub fn execute_entry(_entry: &DispatchEntry) -> Result<(), VMError> {
Ok(())
}

View File

@ -15,7 +15,16 @@ pub struct ExecutionFrame {
} }
impl ExecutionFrame { impl ExecutionFrame {
pub fn new() -> Self { Self { current_block: None, pc: 0, last_result: None } } pub fn new() -> Self {
pub fn reset(&mut self) { self.current_block = None; self.pc = 0; self.last_result = None; } Self {
current_block: None,
pc: 0,
last_result: None,
}
}
pub fn reset(&mut self) {
self.current_block = None;
self.pc = 0;
self.last_result = None;
}
} }

View File

@ -12,10 +12,16 @@ pub fn is_mutating_builtin_call(recv: &VMValue, method: &str) -> bool {
match recv { match recv {
VMValue::BoxRef(b) => { VMValue::BoxRef(b) => {
if b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>().is_some() { if b.as_any()
.downcast_ref::<crate::boxes::array::ArrayBox>()
.is_some()
{
return ARRAY_METHODS.iter().any(|m| *m == method); return ARRAY_METHODS.iter().any(|m| *m == method);
} }
if b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>().is_some() { if b.as_any()
.downcast_ref::<crate::boxes::map_box::MapBox>()
.is_some()
{
return MAP_METHODS.iter().any(|m| *m == method); return MAP_METHODS.iter().any(|m| *m == method);
} }
false false
@ -28,7 +34,11 @@ pub fn is_mutating_builtin_call(recv: &VMValue, method: &str) -> bool {
pub fn gc_write_barrier_site(runtime: &crate::runtime::NyashRuntime, site: &str) { pub fn gc_write_barrier_site(runtime: &crate::runtime::NyashRuntime, site: &str) {
let trace = crate::config::env::gc_trace(); let trace = crate::config::env::gc_trace();
let strict = crate::config::env::gc_barrier_strict(); let strict = crate::config::env::gc_barrier_strict();
let before = if strict { runtime.gc.snapshot_counters() } else { None }; let before = if strict {
runtime.gc.snapshot_counters()
} else {
None
};
if trace { if trace {
eprintln!("[GC] barrier: Write @{}", site); eprintln!("[GC] barrier: Write @{}", site);
} }
@ -38,10 +48,16 @@ pub fn gc_write_barrier_site(runtime: &crate::runtime::NyashRuntime, site: &str)
match (before, after) { match (before, after) {
(Some((_, _, bw)), Some((_, _, aw))) if aw > bw => {} (Some((_, _, bw)), Some((_, _, aw))) if aw > bw => {}
(Some(_), Some(_)) => { (Some(_), Some(_)) => {
panic!("[GC][STRICT] write barrier did not increment at site='{}'", site); panic!(
"[GC][STRICT] write barrier did not increment at site='{}'",
site
);
} }
_ => { _ => {
panic!("[GC][STRICT] CountingGc required for strict verification at site='{}'", site); panic!(
"[GC][STRICT] CountingGc required for strict verification at site='{}'",
site
);
} }
} }
} }

View File

@ -13,4 +13,3 @@ pub mod compiler {
pub mod box_types { pub mod box_types {
pub use crate::backend::llvm_legacy::box_types::*; pub use crate::backend::llvm_legacy::box_types::*;
} }

View File

@ -3,4 +3,3 @@
pub fn load_box_type_ids() -> std::collections::HashMap<String, u32> { pub fn load_box_type_ids() -> std::collections::HashMap<String, u32> {
std::collections::HashMap::new() std::collections::HashMap::new()
} }

View File

@ -1,2 +1 @@
// legacy aot placeholder; full implementation retained in archived branch or prior history // legacy aot placeholder; full implementation retained in archived branch or prior history

View File

@ -0,0 +1,10 @@
//! Legacy LLVM codegen placeholder.
//!
//! The original inkwell-based implementation was removed during the Phase-15
//! refactor. We keep a stub module so tools like `cargo fmt` can resolve the
//! module tree even when the legacy feature is gated off.
#[allow(dead_code)]
pub(crate) fn lower_module() {
// Intentionally left blank.
}

View File

@ -1,2 +1 @@
// legacy helpers placeholder; kept to satisfy module structure after move // legacy helpers placeholder; kept to satisfy module structure after move

View File

@ -0,0 +1,6 @@
//! Legacy LLVM interpreter placeholder.
#[allow(dead_code)]
pub(crate) fn execute() {
// Stub implementation.
}

View File

@ -1,5 +1,5 @@
use crate::box_trait::{IntegerBox, NyashBox};
use crate::mir::function::MirModule; use crate::mir::function::MirModule;
use crate::box_trait::{NyashBox, IntegerBox};
use std::collections::HashMap; use std::collections::HashMap;
pub struct LLVMCompiler { pub struct LLVMCompiler {
@ -8,7 +8,9 @@ pub struct LLVMCompiler {
impl LLVMCompiler { impl LLVMCompiler {
pub fn new() -> Result<Self, String> { pub fn new() -> Result<Self, String> {
Ok(Self { values: HashMap::new() }) Ok(Self {
values: HashMap::new(),
})
} }
pub fn compile_module(&self, _mir: &MirModule, _out: &str) -> Result<(), String> { pub fn compile_module(&self, _mir: &MirModule, _out: &str) -> Result<(), String> {
@ -16,8 +18,11 @@ impl LLVMCompiler {
Ok(()) Ok(())
} }
pub fn compile_and_execute(&mut self, _mir: &MirModule, _out: &str) -> Result<Box<dyn NyashBox>, String> { pub fn compile_and_execute(
&mut self,
_mir: &MirModule,
_out: &str,
) -> Result<Box<dyn NyashBox>, String> {
Ok(Box::new(IntegerBox::new(0))) Ok(Box::new(IntegerBox::new(0)))
} }
} }

View File

@ -31,4 +31,3 @@ mod tests {
assert!(true); assert!(true);
} }
} }

View File

@ -11,19 +11,21 @@ pub struct CodegenContext {
#[cfg(not(feature = "llvm-inkwell-legacy"))] #[cfg(not(feature = "llvm-inkwell-legacy"))]
impl CodegenContext { impl CodegenContext {
pub fn new(_module_name: &str) -> Result<Self, String> { pub fn new(_module_name: &str) -> Result<Self, String> {
Ok(Self { _phantom: std::marker::PhantomData }) Ok(Self {
_phantom: std::marker::PhantomData,
})
} }
} }
// Real implementation (compiled only when feature "llvm-inkwell-legacy" is enabled) // Real implementation (compiled only when feature "llvm-inkwell-legacy" is enabled)
#[cfg(feature = "llvm-inkwell-legacy")] #[cfg(feature = "llvm-inkwell-legacy")]
use inkwell::builder::Builder;
#[cfg(feature = "llvm-inkwell-legacy")]
use inkwell::context::Context; use inkwell::context::Context;
#[cfg(feature = "llvm-inkwell-legacy")] #[cfg(feature = "llvm-inkwell-legacy")]
use inkwell::module::Module; use inkwell::module::Module;
#[cfg(feature = "llvm-inkwell-legacy")] #[cfg(feature = "llvm-inkwell-legacy")]
use inkwell::builder::Builder; use inkwell::targets::{InitializationConfig, Target, TargetMachine};
#[cfg(feature = "llvm-inkwell-legacy")]
use inkwell::targets::{Target, TargetMachine, InitializationConfig};
#[cfg(feature = "llvm-inkwell-legacy")] #[cfg(feature = "llvm-inkwell-legacy")]
pub struct CodegenContext<'ctx> { pub struct CodegenContext<'ctx> {
@ -40,8 +42,8 @@ impl<'ctx> CodegenContext<'ctx> {
.map_err(|e| format!("Failed to initialize native target: {}", e))?; .map_err(|e| format!("Failed to initialize native target: {}", e))?;
let module = context.create_module(module_name); let module = context.create_module(module_name);
let triple = TargetMachine::get_default_triple(); let triple = TargetMachine::get_default_triple();
let target = Target::from_triple(&triple) let target =
.map_err(|e| format!("Failed to get target: {}", e))?; Target::from_triple(&triple).map_err(|e| format!("Failed to get target: {}", e))?;
let target_machine = target let target_machine = target
.create_target_machine( .create_target_machine(
&triple, &triple,
@ -53,7 +55,11 @@ impl<'ctx> CodegenContext<'ctx> {
) )
.ok_or_else(|| "Failed to create target machine".to_string())?; .ok_or_else(|| "Failed to create target machine".to_string())?;
let builder = context.create_builder(); let builder = context.create_builder();
Ok(Self { context, module, builder, target_machine }) Ok(Self {
context,
module,
builder,
target_machine,
})
} }
} }

View File

@ -34,4 +34,3 @@ mod tests {
assert!(true); assert!(true);
} }
} }

View File

@ -8,10 +8,12 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::backend::vm::{VMValue, VMError}; use crate::backend::abi_util::{eq_vm, to_bool_vm};
use crate::backend::vm::{VMError, VMValue};
use crate::box_trait::NyashBox; use crate::box_trait::NyashBox;
use crate::mir::{MirModule, MirFunction, MirInstruction, ConstValue, BinaryOp, CompareOp, ValueId, BasicBlockId}; use crate::mir::{
use crate::backend::abi_util::{to_bool_vm, eq_vm}; BasicBlockId, BinaryOp, CompareOp, ConstValue, MirFunction, MirInstruction, MirModule, ValueId,
};
pub struct MirInterpreter { pub struct MirInterpreter {
// SSA value table // SSA value table
@ -23,11 +25,20 @@ pub struct MirInterpreter {
} }
impl MirInterpreter { impl MirInterpreter {
pub fn new() -> Self { Self { regs: HashMap::new(), mem: HashMap::new(), obj_fields: HashMap::new() } } pub fn new() -> Self {
Self {
regs: HashMap::new(),
mem: HashMap::new(),
obj_fields: HashMap::new(),
}
}
/// Execute module entry (main) and return boxed result /// Execute module entry (main) and return boxed result
pub fn execute_module(&mut self, module: &MirModule) -> Result<Box<dyn NyashBox>, VMError> { pub fn execute_module(&mut self, module: &MirModule) -> Result<Box<dyn NyashBox>, VMError> {
let func = module.functions.get("main").ok_or_else(|| VMError::InvalidInstruction("missing main".into()))?; let func = module
.functions
.get("main")
.ok_or_else(|| VMError::InvalidInstruction("missing main".into()))?;
let ret = self.execute_function(func)?; let ret = self.execute_function(func)?;
Ok(ret.to_nyash_box()) Ok(ret.to_nyash_box())
} }
@ -36,7 +47,10 @@ impl MirInterpreter {
let mut cur = func.entry_block; let mut cur = func.entry_block;
let mut last_pred: Option<BasicBlockId> = None; let mut last_pred: Option<BasicBlockId> = None;
loop { loop {
let block = func.blocks.get(&cur).ok_or_else(|| VMError::InvalidBasicBlock(format!("bb {:?} not found", cur)))?; let block = func
.blocks
.get(&cur)
.ok_or_else(|| VMError::InvalidBasicBlock(format!("bb {:?} not found", cur)))?;
// Resolve incoming phi nodes using predecessor // Resolve incoming phi nodes using predecessor
for inst in &block.instructions { for inst in &block.instructions {
if let MirInstruction::Phi { dst, inputs } = inst { if let MirInstruction::Phi { dst, inputs } = inst {
@ -67,17 +81,34 @@ impl MirInterpreter {
}; };
self.regs.insert(*dst, v); self.regs.insert(*dst, v);
} }
MirInstruction::NewBox { dst, box_type, args } => { MirInstruction::NewBox {
dst,
box_type,
args,
} => {
// Build arg boxes // Build arg boxes
let mut a: Vec<Box<dyn crate::box_trait::NyashBox>> = Vec::new(); let mut a: Vec<Box<dyn crate::box_trait::NyashBox>> = Vec::new();
for vid in args { a.push(self.reg_load(*vid)?.to_nyash_box()); } for vid in args {
a.push(self.reg_load(*vid)?.to_nyash_box());
}
// Use unified global registry (plugins already initialized by runner) // Use unified global registry (plugins already initialized by runner)
let reg = crate::runtime::unified_registry::get_global_unified_registry(); let reg = crate::runtime::unified_registry::get_global_unified_registry();
let created = reg.lock().unwrap().create_box(box_type, &a) let created =
.map_err(|e| VMError::InvalidInstruction(format!("NewBox {} failed: {}", box_type, e)))?; reg.lock().unwrap().create_box(box_type, &a).map_err(|e| {
VMError::InvalidInstruction(format!(
"NewBox {} failed: {}",
box_type, e
))
})?;
self.regs.insert(*dst, VMValue::from_nyash_box(created)); self.regs.insert(*dst, VMValue::from_nyash_box(created));
} }
MirInstruction::PluginInvoke { dst, box_val, method, args, .. } => { MirInstruction::PluginInvoke {
dst,
box_val,
method,
args,
..
} => {
// Resolve receiver // Resolve receiver
let recv = self.reg_load(*box_val)?; let recv = self.reg_load(*box_val)?;
let recv_box: Box<dyn crate::box_trait::NyashBox> = match recv.clone() { let recv_box: Box<dyn crate::box_trait::NyashBox> = match recv.clone() {
@ -85,40 +116,101 @@ impl MirInterpreter {
other => other.to_nyash_box(), other => other.to_nyash_box(),
}; };
// If PluginBoxV2 → invoke via unified plugin host // If PluginBoxV2 → invoke via unified plugin host
if let Some(p) = recv_box.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() { if let Some(p) = recv_box
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host(); .as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>(
) {
let host =
crate::runtime::plugin_loader_unified::get_global_plugin_host();
let host = host.read().unwrap(); let host = host.read().unwrap();
let mut argv: Vec<Box<dyn crate::box_trait::NyashBox>> = Vec::new(); let mut argv: Vec<Box<dyn crate::box_trait::NyashBox>> = Vec::new();
for a in args { argv.push(self.reg_load(*a)?.to_nyash_box()); } for a in args {
match host.invoke_instance_method(&p.box_type, method, p.inner.instance_id, &argv) { argv.push(self.reg_load(*a)?.to_nyash_box());
Ok(Some(ret)) => { }
if let Some(d) = dst { self.regs.insert(*d, VMValue::from_nyash_box(ret)); } match host.invoke_instance_method(
&p.box_type,
method,
p.inner.instance_id,
&argv,
) {
Ok(Some(ret)) => {
if let Some(d) = dst {
self.regs.insert(*d, VMValue::from_nyash_box(ret));
}
}
Ok(None) => {
if let Some(d) = dst {
self.regs.insert(*d, VMValue::Void);
}
}
Err(e) => {
return Err(VMError::InvalidInstruction(format!(
"PluginInvoke {}.{} failed: {:?}",
p.box_type, method, e
)));
} }
Ok(None) => { if let Some(d) = dst { self.regs.insert(*d, VMValue::Void); } }
Err(e) => { return Err(VMError::InvalidInstruction(format!("PluginInvoke {}.{} failed: {:?}", p.box_type, method, e))); }
} }
} else { } else {
// Minimal fallback: toString // Minimal fallback: toString
if method == "toString" { if method == "toString" {
if let Some(d) = dst { self.regs.insert(*d, VMValue::String(recv_box.to_string_box().value)); } if let Some(d) = dst {
self.regs.insert(
*d,
VMValue::String(recv_box.to_string_box().value),
);
}
} else { } else {
return Err(VMError::InvalidInstruction(format!("PluginInvoke unsupported on {} for method {}", recv_box.type_name(), method))); return Err(VMError::InvalidInstruction(format!(
"PluginInvoke unsupported on {} for method {}",
recv_box.type_name(),
method
)));
} }
} }
} }
MirInstruction::BoxCall { dst, box_val, method, args, .. } => { MirInstruction::BoxCall {
dst,
box_val,
method,
args,
..
} => {
// Support getField/setField for normalized RefGet/RefSet // Support getField/setField for normalized RefGet/RefSet
if method == "getField" { if method == "getField" {
if args.len() != 1 { return Err(VMError::InvalidInstruction("getField expects 1 arg".into())); } if args.len() != 1 {
let fname = match self.reg_load(args[0].clone())? { VMValue::String(s) => s, v => v.to_string() }; return Err(VMError::InvalidInstruction(
let v = self.obj_fields.get(box_val).and_then(|m| m.get(&fname)).cloned().unwrap_or(VMValue::Void); "getField expects 1 arg".into(),
if let Some(d) = dst { self.regs.insert(*d, v); } ));
}
let fname = match self.reg_load(args[0].clone())? {
VMValue::String(s) => s,
v => v.to_string(),
};
let v = self
.obj_fields
.get(box_val)
.and_then(|m| m.get(&fname))
.cloned()
.unwrap_or(VMValue::Void);
if let Some(d) = dst {
self.regs.insert(*d, v);
}
continue; continue;
} else if method == "setField" { } else if method == "setField" {
if args.len() != 2 { return Err(VMError::InvalidInstruction("setField expects 2 args".into())); } if args.len() != 2 {
let fname = match self.reg_load(args[0].clone())? { VMValue::String(s) => s, v => v.to_string() }; return Err(VMError::InvalidInstruction(
"setField expects 2 args".into(),
));
}
let fname = match self.reg_load(args[0].clone())? {
VMValue::String(s) => s,
v => v.to_string(),
};
let valv = self.reg_load(args[1].clone())?; let valv = self.reg_load(args[1].clone())?;
self.obj_fields.entry(*box_val).or_default().insert(fname, valv); self.obj_fields
.entry(*box_val)
.or_default()
.insert(fname, valv);
continue; continue;
} }
// Fallback: treat like PluginInvoke for plugin-backed boxes // Fallback: treat like PluginInvoke for plugin-backed boxes
@ -127,7 +219,10 @@ impl MirInterpreter {
VMValue::BoxRef(b) => b.share_box(), VMValue::BoxRef(b) => b.share_box(),
other => other.to_nyash_box(), other => other.to_nyash_box(),
}; };
if let Some(p) = recv_box.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() { if let Some(p) = recv_box
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>(
) {
// Special-case: ConsoleBox.readLine → stdin fallback if not provided by plugin // Special-case: ConsoleBox.readLine → stdin fallback if not provided by plugin
if p.box_type == "ConsoleBox" && method == "readLine" { if p.box_type == "ConsoleBox" && method == "readLine" {
use std::io::{self, Read}; use std::io::{self, Read};
@ -136,64 +231,145 @@ impl MirInterpreter {
// Read a single line (blocking) // Read a single line (blocking)
let mut buf = [0u8; 1]; let mut buf = [0u8; 1];
while let Ok(n) = stdin.read(&mut buf) { while let Ok(n) = stdin.read(&mut buf) {
if n == 0 { break; } if n == 0 {
let ch = buf[0] as char; break;
if ch == '\n' { break; } }
s.push(ch); let ch = buf[0] as char;
if s.len() > 1_000_000 { break; } if ch == '\n' {
break;
}
s.push(ch);
if s.len() > 1_000_000 {
break;
}
}
if let Some(d) = dst {
self.regs.insert(*d, VMValue::String(s));
} }
if let Some(d) = dst { self.regs.insert(*d, VMValue::String(s)); }
continue; continue;
} }
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host(); let host =
crate::runtime::plugin_loader_unified::get_global_plugin_host();
let host = host.read().unwrap(); let host = host.read().unwrap();
let mut argv: Vec<Box<dyn crate::box_trait::NyashBox>> = Vec::new(); let mut argv: Vec<Box<dyn crate::box_trait::NyashBox>> = Vec::new();
for a in args { argv.push(self.reg_load(*a)?.to_nyash_box()); } for a in args {
match host.invoke_instance_method(&p.box_type, method, p.inner.instance_id, &argv) { argv.push(self.reg_load(*a)?.to_nyash_box());
Ok(Some(ret)) => { if let Some(d) = dst { self.regs.insert(*d, VMValue::from_nyash_box(ret)); } } }
Ok(None) => { if let Some(d) = dst { self.regs.insert(*d, VMValue::Void); } } match host.invoke_instance_method(
Err(e) => { return Err(VMError::InvalidInstruction(format!("BoxCall {}.{} failed: {:?}", p.box_type, method, e))); } &p.box_type,
method,
p.inner.instance_id,
&argv,
) {
Ok(Some(ret)) => {
if let Some(d) = dst {
self.regs.insert(*d, VMValue::from_nyash_box(ret));
}
}
Ok(None) => {
if let Some(d) = dst {
self.regs.insert(*d, VMValue::Void);
}
}
Err(e) => {
return Err(VMError::InvalidInstruction(format!(
"BoxCall {}.{} failed: {:?}",
p.box_type, method, e
)));
}
} }
} else { } else {
return Err(VMError::InvalidInstruction(format!("BoxCall unsupported on {}.{}", recv_box.type_name(), method))); return Err(VMError::InvalidInstruction(format!(
"BoxCall unsupported on {}.{}",
recv_box.type_name(),
method
)));
} }
} }
MirInstruction::ExternCall { dst, iface_name, method_name, args, .. } => { MirInstruction::ExternCall {
dst,
iface_name,
method_name,
args,
..
} => {
// Minimal env.console.log bridge // Minimal env.console.log bridge
if iface_name == "env.console" && method_name == "log" { if iface_name == "env.console" && method_name == "log" {
if let Some(a0) = args.get(0) { if let Some(a0) = args.get(0) {
let v = self.reg_load(*a0)?; let v = self.reg_load(*a0)?;
println!("{}", v.to_string()); println!("{}", v.to_string());
} }
if let Some(d) = dst { self.regs.insert(*d, VMValue::Void); } if let Some(d) = dst {
self.regs.insert(*d, VMValue::Void);
}
} else { } else {
return Err(VMError::InvalidInstruction(format!("ExternCall {}.{} not supported", iface_name, method_name))); return Err(VMError::InvalidInstruction(format!(
"ExternCall {}.{} not supported",
iface_name, method_name
)));
} }
} }
MirInstruction::RefSet { reference, field, value } => { MirInstruction::RefSet {
reference,
field,
value,
} => {
let v = self.reg_load(*value)?; let v = self.reg_load(*value)?;
self.obj_fields.entry(*reference).or_default().insert(field.clone(), v); self.obj_fields
.entry(*reference)
.or_default()
.insert(field.clone(), v);
} }
MirInstruction::RefGet { dst, reference, field } => { MirInstruction::RefGet {
let v = self.obj_fields.get(reference).and_then(|m| m.get(field)).cloned().unwrap_or(VMValue::Void); dst,
reference,
field,
} => {
let v = self
.obj_fields
.get(reference)
.and_then(|m| m.get(field))
.cloned()
.unwrap_or(VMValue::Void);
self.regs.insert(*dst, v); self.regs.insert(*dst, v);
} }
MirInstruction::BinOp { dst, op, lhs, rhs } => { MirInstruction::BinOp { dst, op, lhs, rhs } => {
let a = self.reg_load(*lhs)?; let b = self.reg_load(*rhs)?; let a = self.reg_load(*lhs)?;
let b = self.reg_load(*rhs)?;
let v = self.eval_binop(*op, a, b)?; let v = self.eval_binop(*op, a, b)?;
self.regs.insert(*dst, v); self.regs.insert(*dst, v);
} }
MirInstruction::UnaryOp { dst, op, operand } => { MirInstruction::UnaryOp { dst, op, operand } => {
let x = self.reg_load(*operand)?; let x = self.reg_load(*operand)?;
let v = match op { let v = match op {
crate::mir::UnaryOp::Neg => match x { VMValue::Integer(i) => VMValue::Integer(-i), VMValue::Float(f)=>VMValue::Float(-f), _=> return Err(VMError::TypeError(format!("neg expects number, got {:?}", x))) }, crate::mir::UnaryOp::Neg => match x {
crate::mir::UnaryOp::Not => VMValue::Bool(!to_bool_vm(&x).map_err(|e| VMError::TypeError(e))?), VMValue::Integer(i) => VMValue::Integer(-i),
crate::mir::UnaryOp::BitNot => match x { VMValue::Integer(i) => VMValue::Integer(!i), _=> return Err(VMError::TypeError(format!("bitnot expects integer, got {:?}", x))) }, VMValue::Float(f) => VMValue::Float(-f),
_ => {
return Err(VMError::TypeError(format!(
"neg expects number, got {:?}",
x
)))
}
},
crate::mir::UnaryOp::Not => {
VMValue::Bool(!to_bool_vm(&x).map_err(|e| VMError::TypeError(e))?)
}
crate::mir::UnaryOp::BitNot => match x {
VMValue::Integer(i) => VMValue::Integer(!i),
_ => {
return Err(VMError::TypeError(format!(
"bitnot expects integer, got {:?}",
x
)))
}
},
}; };
self.regs.insert(*dst, v); self.regs.insert(*dst, v);
} }
MirInstruction::Compare { dst, op, lhs, rhs } => { MirInstruction::Compare { dst, op, lhs, rhs } => {
let a = self.reg_load(*lhs)?; let b = self.reg_load(*rhs)?; let a = self.reg_load(*lhs)?;
let b = self.reg_load(*rhs)?;
let v = self.eval_cmp(*op, a, b)?; let v = self.eval_cmp(*op, a, b)?;
self.regs.insert(*dst, VMValue::Bool(v)); self.regs.insert(*dst, VMValue::Bool(v));
} }
@ -206,7 +382,8 @@ impl MirInterpreter {
self.mem.insert(*ptr, v); self.mem.insert(*ptr, v);
} }
MirInstruction::Copy { dst, src } => { MirInstruction::Copy { dst, src } => {
let v = self.reg_load(*src)?; self.regs.insert(*dst, v); let v = self.reg_load(*src)?;
self.regs.insert(*dst, v);
} }
MirInstruction::Debug { value, message } => { MirInstruction::Debug { value, message } => {
let v = self.reg_load(*value)?; let v = self.reg_load(*value)?;
@ -215,38 +392,75 @@ impl MirInterpreter {
} }
} }
MirInstruction::Print { value, .. } => { MirInstruction::Print { value, .. } => {
let v = self.reg_load(*value)?; println!("{}", v.to_string()); let v = self.reg_load(*value)?;
println!("{}", v.to_string());
} }
// No-ops in the interpreter for now // No-ops in the interpreter for now
MirInstruction::BarrierRead { .. } | MirInstruction::BarrierWrite { .. } | MirInstruction::Barrier { .. } | MirInstruction::Safepoint | MirInstruction::Nop => {} MirInstruction::BarrierRead { .. }
| MirInstruction::BarrierWrite { .. }
| MirInstruction::Barrier { .. }
| MirInstruction::Safepoint
| MirInstruction::Nop => {}
// Unimplemented but recognized — return clear error for visibility // Unimplemented but recognized — return clear error for visibility
other => return Err(VMError::InvalidInstruction(format!("MIR interp: unimplemented instruction: {:?}", other))), other => {
return Err(VMError::InvalidInstruction(format!(
"MIR interp: unimplemented instruction: {:?}",
other
)))
}
} }
} }
// Handle terminator // Handle terminator
match &block.terminator { match &block.terminator {
Some(MirInstruction::Return { value }) => { Some(MirInstruction::Return { value }) => {
if let Some(v) = value { return self.reg_load(*v); } else { return Ok(VMValue::Void); } if let Some(v) = value {
return self.reg_load(*v);
} else {
return Ok(VMValue::Void);
}
} }
Some(MirInstruction::Jump { target }) => { Some(MirInstruction::Jump { target }) => {
last_pred = Some(block.id); cur = *target; continue; last_pred = Some(block.id);
cur = *target;
continue;
} }
Some(MirInstruction::Branch { condition, then_bb, else_bb }) => { Some(MirInstruction::Branch {
let c = self.reg_load(*condition)?; let t = to_bool_vm(&c).map_err(|e| VMError::TypeError(e))?; condition,
last_pred = Some(block.id); cur = if t { *then_bb } else { *else_bb }; continue; then_bb,
else_bb,
}) => {
let c = self.reg_load(*condition)?;
let t = to_bool_vm(&c).map_err(|e| VMError::TypeError(e))?;
last_pred = Some(block.id);
cur = if t { *then_bb } else { *else_bb };
continue;
}
None => {
return Err(VMError::InvalidBasicBlock(format!(
"unterminated block {:?}",
block.id
)))
}
Some(other) => {
return Err(VMError::InvalidInstruction(format!(
"invalid terminator in MIR interp: {:?}",
other
)))
} }
None => return Err(VMError::InvalidBasicBlock(format!("unterminated block {:?}", block.id))),
Some(other) => return Err(VMError::InvalidInstruction(format!("invalid terminator in MIR interp: {:?}", other))),
} }
} }
} }
fn reg_load(&self, id: ValueId) -> Result<VMValue, VMError> { fn reg_load(&self, id: ValueId) -> Result<VMValue, VMError> {
self.regs.get(&id).cloned().ok_or_else(|| VMError::InvalidValue(format!("use of undefined value {:?}", id))) self.regs
.get(&id)
.cloned()
.ok_or_else(|| VMError::InvalidValue(format!("use of undefined value {:?}", id)))
} }
fn eval_binop(&self, op: BinaryOp, a: VMValue, b: VMValue) -> Result<VMValue, VMError> { fn eval_binop(&self, op: BinaryOp, a: VMValue, b: VMValue) -> Result<VMValue, VMError> {
use BinaryOp::*; use VMValue::*; use BinaryOp::*;
use VMValue::*;
Ok(match (op, a, b) { Ok(match (op, a, b) {
(Add, Integer(x), Integer(y)) => Integer(x + y), (Add, Integer(x), Integer(y)) => Integer(x + y),
// String concat: ifいずれかがStringなら文字列連結 // String concat: ifいずれかがStringなら文字列連結
@ -277,12 +491,18 @@ impl MirInterpreter {
(Shl, Integer(x), Integer(y)) => Integer(x.wrapping_shl(y as u32)), (Shl, Integer(x), Integer(y)) => Integer(x.wrapping_shl(y as u32)),
(Shr, Integer(x), Integer(y)) => Integer(x.wrapping_shr(y as u32)), (Shr, Integer(x), Integer(y)) => Integer(x.wrapping_shr(y as u32)),
// Fallbacks not yet supported // Fallbacks not yet supported
(opk, va, vb) => return Err(VMError::TypeError(format!("unsupported binop {:?} on {:?} and {:?}", opk, va, vb))), (opk, va, vb) => {
return Err(VMError::TypeError(format!(
"unsupported binop {:?} on {:?} and {:?}",
opk, va, vb
)))
}
}) })
} }
fn eval_cmp(&self, op: CompareOp, a: VMValue, b: VMValue) -> Result<bool, VMError> { fn eval_cmp(&self, op: CompareOp, a: VMValue, b: VMValue) -> Result<bool, VMError> {
use CompareOp::*; use VMValue::*; use CompareOp::*;
use VMValue::*;
Ok(match (op, &a, &b) { Ok(match (op, &a, &b) {
(Eq, _, _) => eq_vm(&a, &b), (Eq, _, _) => eq_vm(&a, &b),
(Ne, _, _) => !eq_vm(&a, &b), (Ne, _, _) => !eq_vm(&a, &b),
@ -294,7 +514,12 @@ impl MirInterpreter {
(Le, Float(x), Float(y)) => x <= y, (Le, Float(x), Float(y)) => x <= y,
(Gt, Float(x), Float(y)) => x > y, (Gt, Float(x), Float(y)) => x > y,
(Ge, Float(x), Float(y)) => x >= y, (Ge, Float(x), Float(y)) => x >= y,
(opk, va, vb) => return Err(VMError::TypeError(format!("unsupported compare {:?} on {:?} and {:?}", opk, va, vb))), (opk, va, vb) => {
return Err(VMError::TypeError(format!(
"unsupported compare {:?} on {:?} and {:?}",
opk, va, vb
)))
}
}) })
} }
} }

View File

@ -3,49 +3,54 @@
*/ */
pub mod vm; pub mod vm;
pub mod vm_phi;
pub mod vm_instructions;
pub mod vm_values;
pub mod vm_types;
pub mod vm_boxcall; pub mod vm_boxcall;
pub mod vm_instructions;
pub mod vm_phi;
pub mod vm_stats; pub mod vm_stats;
pub mod vm_types;
pub mod vm_values;
// Phase 9.78h: VM split scaffolding (control_flow/dispatch/frame) // Phase 9.78h: VM split scaffolding (control_flow/dispatch/frame)
pub mod abi_util; // Shared ABI/utility helpers
pub mod control_flow; pub mod control_flow;
pub mod dispatch; pub mod dispatch;
pub mod frame; pub mod frame;
pub mod gc_helpers; pub mod gc_helpers;
pub mod mir_interpreter;
pub mod vm_control_flow; pub mod vm_control_flow;
mod vm_gc; // A3: GC roots & diagnostics extracted
mod vm_exec; // A3: execution loop extracted mod vm_exec; // A3: execution loop extracted
mod vm_state; // A3: state & basic helpers extracted mod vm_gc; // A3: GC roots & diagnostics extracted
mod vm_methods; // A3-S1: method dispatch wrappers extracted mod vm_methods; // A3-S1: method dispatch wrappers extracted
pub mod abi_util; // Shared ABI/utility helpers mod vm_state; // A3: state & basic helpers extracted // Lightweight MIR interpreter
pub mod mir_interpreter; // Lightweight MIR interpreter
#[cfg(feature = "wasm-backend")]
pub mod wasm;
#[cfg(feature = "wasm-backend")] #[cfg(feature = "wasm-backend")]
pub mod aot; pub mod aot;
#[cfg(feature = "wasm-backend")] #[cfg(feature = "wasm-backend")]
pub mod wasm;
#[cfg(feature = "wasm-backend")]
pub mod wasm_v2; pub mod wasm_v2;
#[cfg(feature = "llvm-inkwell-legacy")] #[cfg(feature = "llvm-inkwell-legacy")]
pub mod llvm_legacy; pub mod llvm_legacy;
// Back-compat shim so existing paths crate::backend::llvm::* keep working // Back-compat shim so existing paths crate::backend::llvm::* keep working
#[cfg(feature = "llvm-inkwell-legacy")]
pub mod llvm;
#[cfg(feature = "cranelift-jit")] #[cfg(feature = "cranelift-jit")]
pub mod cranelift; pub mod cranelift;
#[cfg(feature = "llvm-inkwell-legacy")]
pub mod llvm;
pub use vm::{VM, VMError, VMValue};
pub use mir_interpreter::MirInterpreter; pub use mir_interpreter::MirInterpreter;
pub use vm::{VMError, VMValue, VM};
#[cfg(feature = "wasm-backend")]
pub use aot::{AotBackend, AotConfig, AotError, AotStats};
#[cfg(feature = "wasm-backend")] #[cfg(feature = "wasm-backend")]
pub use wasm::{WasmBackend, WasmError}; pub use wasm::{WasmBackend, WasmError};
#[cfg(feature = "wasm-backend")]
pub use aot::{AotBackend, AotError, AotConfig, AotStats};
#[cfg(feature = "llvm-inkwell-legacy")]
pub use llvm_legacy::{compile_and_execute as llvm_compile_and_execute, compile_to_object as llvm_compile_to_object};
#[cfg(feature = "cranelift-jit")] #[cfg(feature = "cranelift-jit")]
pub use cranelift::{compile_and_execute as cranelift_compile_and_execute, compile_to_object as cranelift_compile_to_object}; pub use cranelift::{
compile_and_execute as cranelift_compile_and_execute,
compile_to_object as cranelift_compile_to_object,
};
#[cfg(feature = "llvm-inkwell-legacy")]
pub use llvm_legacy::{
compile_and_execute as llvm_compile_and_execute, compile_to_object as llvm_compile_to_object,
};

View File

@ -7,14 +7,14 @@
* Typical Callers: runner (VM backend), instruction handlers (vm_instructions) * Typical Callers: runner (VM backend), instruction handlers (vm_instructions)
*/ */
use crate::mir::{ValueId, BasicBlockId, MirModule}; use crate::mir::{BasicBlockId, MirModule, ValueId};
use std::collections::HashMap;
use crate::runtime::NyashRuntime; use crate::runtime::NyashRuntime;
use crate::scope_tracker::ScopeTracker; use crate::scope_tracker::ScopeTracker;
use std::collections::HashMap;
// MirModule is already imported via crate::mir at top // MirModule is already imported via crate::mir at top
use super::frame::ExecutionFrame;
use super::vm_phi::LoopExecutor; use super::vm_phi::LoopExecutor;
use std::time::Instant; use std::time::Instant;
use super::frame::ExecutionFrame;
// Phase 9.78a: Import necessary components for unified Box handling // Phase 9.78a: Import necessary components for unified Box handling
// TODO: Re-enable when interpreter refactoring is complete // TODO: Re-enable when interpreter refactoring is complete
@ -90,10 +90,9 @@ pub struct VM {
} }
impl VM { impl VM {
pub fn runtime_ref(&self) -> &NyashRuntime { &self.runtime } pub fn runtime_ref(&self) -> &NyashRuntime {
&self.runtime
}
// TODO: Re-enable when interpreter refactoring is complete // TODO: Re-enable when interpreter refactoring is complete
/* /*
@ -118,22 +117,6 @@ impl VM {
} }
*/ */
// Call a method on a Box - moved to vm_methods.rs (wrapper now in vm_methods) // Call a method on a Box - moved to vm_methods.rs (wrapper now in vm_methods)
// removed: old inline implementation // removed: old inline implementation
/* /*
@ -538,7 +521,6 @@ impl VM {
/// RAII guard for GC root regions /// RAII guard for GC root regions
// Root region guard removed in favor of explicit enter/leave to avoid borrow conflicts // Root region guard removed in favor of explicit enter/leave to avoid borrow conflicts
pub(super) use crate::backend::vm_control_flow::ControlFlow; pub(super) use crate::backend::vm_control_flow::ControlFlow;
impl Default for VM { impl Default for VM {
@ -550,14 +532,16 @@ impl Default for VM {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirType, EffectMask, BasicBlock, BinaryOp}; use crate::box_factory::user_defined::UserDefinedBoxFactory;
use crate::parser::NyashParser;
use crate::runtime::NyashRuntime;
use crate::core::model::BoxDeclaration as CoreBoxDecl; use crate::core::model::BoxDeclaration as CoreBoxDecl;
use crate::interpreter::SharedState; use crate::interpreter::SharedState;
use crate::box_factory::user_defined::UserDefinedBoxFactory; use crate::mir::{
use std::sync::Arc; BasicBlock, BinaryOp, EffectMask, FunctionSignature, MirFunction, MirModule, MirType,
};
use crate::parser::NyashParser;
use crate::runtime::NyashRuntime;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
#[test] #[test]
fn test_basic_vm_execution() { fn test_basic_vm_execution() {
@ -603,9 +587,25 @@ mod tests {
fn walk(node: &crate::ast::ASTNode, runtime: &NyashRuntime) { fn walk(node: &crate::ast::ASTNode, runtime: &NyashRuntime) {
match node { match node {
crate::ast::ASTNode::Program { statements, .. } => { crate::ast::ASTNode::Program { statements, .. } => {
for st in statements { walk(st, runtime); } for st in statements {
walk(st, runtime);
} }
crate::ast::ASTNode::BoxDeclaration { name, fields, public_fields, private_fields, methods, constructors, init_fields, weak_fields, is_interface, extends, implements, type_parameters, .. } => { }
crate::ast::ASTNode::BoxDeclaration {
name,
fields,
public_fields,
private_fields,
methods,
constructors,
init_fields,
weak_fields,
is_interface,
extends,
implements,
type_parameters,
..
} => {
let decl = CoreBoxDecl { let decl = CoreBoxDecl {
name: name.clone(), name: name.clone(),
fields: fields.clone(), fields: fields.clone(),
@ -657,7 +657,9 @@ return new Person("Alice").greet()
let mut shared = SharedState::new(); let mut shared = SharedState::new();
shared.box_declarations = rt.box_declarations.clone(); shared.box_declarations = rt.box_declarations.clone();
let udf = Arc::new(UserDefinedBoxFactory::new(shared)); let udf = Arc::new(UserDefinedBoxFactory::new(shared));
if let Ok(mut reg) = rt.box_registry.lock() { reg.register(udf); } if let Ok(mut reg) = rt.box_registry.lock() {
reg.register(udf);
}
rt rt
}; };
@ -673,7 +675,9 @@ return new Person("Alice").greet()
// Execute with VM // Execute with VM
let mut vm = VM::with_runtime(runtime); let mut vm = VM::with_runtime(runtime);
let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); let result = vm
.execute_module(&compile_result.module)
.expect("vm exec failed");
assert_eq!(result.to_string_box().value, "Hello, Alice"); assert_eq!(result.to_string_box().value, "Hello, Alice");
} }
@ -700,13 +704,17 @@ c.get()
let mut shared = SharedState::new(); let mut shared = SharedState::new();
shared.box_declarations = rt.box_declarations.clone(); shared.box_declarations = rt.box_declarations.clone();
let udf = Arc::new(UserDefinedBoxFactory::new(shared)); let udf = Arc::new(UserDefinedBoxFactory::new(shared));
if let Ok(mut reg) = rt.box_registry.lock() { reg.register(udf); } if let Ok(mut reg) = rt.box_registry.lock() {
reg.register(udf);
}
rt rt
}; };
let mut compiler = crate::mir::MirCompiler::new(); let mut compiler = crate::mir::MirCompiler::new();
let compile_result = compiler.compile(ast).expect("mir compile failed"); let compile_result = compiler.compile(ast).expect("mir compile failed");
let mut vm = VM::with_runtime(runtime); let mut vm = VM::with_runtime(runtime);
let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); let result = vm
.execute_module(&compile_result.module)
.expect("vm exec failed");
assert_eq!(result.to_string_box().value, "11"); assert_eq!(result.to_string_box().value, "11");
} }
@ -721,7 +729,9 @@ console.log("ok")
let mut compiler = crate::mir::MirCompiler::new(); let mut compiler = crate::mir::MirCompiler::new();
let compile_result = compiler.compile(ast).expect("mir compile failed"); let compile_result = compiler.compile(ast).expect("mir compile failed");
let mut vm = VM::with_runtime(runtime); let mut vm = VM::with_runtime(runtime);
let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); let result = vm
.execute_module(&compile_result.module)
.expect("vm exec failed");
assert_eq!(result.to_string_box().value, "void"); assert_eq!(result.to_string_box().value, "void");
} }
@ -751,12 +761,33 @@ g.greet()
let runtime = { let runtime = {
let rt = crate::runtime::NyashRuntime::new(); let rt = crate::runtime::NyashRuntime::new();
// Collect box declarations into runtime so that user-defined factory works // Collect box declarations into runtime so that user-defined factory works
fn collect_box_decls(ast: &crate::ast::ASTNode, runtime: &crate::runtime::NyashRuntime) { fn collect_box_decls(
ast: &crate::ast::ASTNode,
runtime: &crate::runtime::NyashRuntime,
) {
use crate::core::model::BoxDeclaration as CoreBoxDecl; use crate::core::model::BoxDeclaration as CoreBoxDecl;
fn walk(node: &crate::ast::ASTNode, runtime: &crate::runtime::NyashRuntime) { fn walk(node: &crate::ast::ASTNode, runtime: &crate::runtime::NyashRuntime) {
match node { match node {
crate::ast::ASTNode::Program { statements, .. } => for st in statements { walk(st, runtime); }, crate::ast::ASTNode::Program { statements, .. } => {
crate::ast::ASTNode::BoxDeclaration { name, fields, public_fields, private_fields, methods, constructors, init_fields, weak_fields, is_interface, extends, implements, type_parameters, .. } => { for st in statements {
walk(st, runtime);
}
}
crate::ast::ASTNode::BoxDeclaration {
name,
fields,
public_fields,
private_fields,
methods,
constructors,
init_fields,
weak_fields,
is_interface,
extends,
implements,
type_parameters,
..
} => {
let decl = CoreBoxDecl { let decl = CoreBoxDecl {
name: name.clone(), name: name.clone(),
fields: fields.clone(), fields: fields.clone(),
@ -771,7 +802,9 @@ g.greet()
implements: implements.clone(), implements: implements.clone(),
type_parameters: type_parameters.clone(), type_parameters: type_parameters.clone(),
}; };
if let Ok(mut map) = runtime.box_declarations.write() { map.insert(name.clone(), decl); } if let Ok(mut map) = runtime.box_declarations.write() {
map.insert(name.clone(), decl);
}
} }
_ => {} _ => {}
} }
@ -782,8 +815,12 @@ g.greet()
// Register user-defined factory // Register user-defined factory
let mut shared = crate::interpreter::SharedState::new(); let mut shared = crate::interpreter::SharedState::new();
shared.box_declarations = rt.box_declarations.clone(); shared.box_declarations = rt.box_declarations.clone();
let udf = std::sync::Arc::new(crate::box_factory::user_defined::UserDefinedBoxFactory::new(shared)); let udf = std::sync::Arc::new(
if let Ok(mut reg) = rt.box_registry.lock() { reg.register(udf); } crate::box_factory::user_defined::UserDefinedBoxFactory::new(shared),
);
if let Ok(mut reg) = rt.box_registry.lock() {
reg.register(udf);
}
rt rt
}; };
@ -791,7 +828,9 @@ g.greet()
let mut compiler = crate::mir::MirCompiler::new(); let mut compiler = crate::mir::MirCompiler::new();
let compile_result = compiler.compile(ast).expect("mir compile failed"); let compile_result = compiler.compile(ast).expect("mir compile failed");
let mut vm = VM::with_runtime(runtime); let mut vm = VM::with_runtime(runtime);
let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); let result = vm
.execute_module(&compile_result.module)
.expect("vm exec failed");
assert_eq!(result.to_string_box().value, "Hi, Bob"); assert_eq!(result.to_string_box().value, "Hi, Bob");
} }
@ -808,7 +847,9 @@ return (new ArrayBox()).type()
let mut compiler = crate::mir::MirCompiler::new(); let mut compiler = crate::mir::MirCompiler::new();
let compile_result = compiler.compile(ast).expect("mir compile failed"); let compile_result = compiler.compile(ast).expect("mir compile failed");
let mut vm = VM::with_runtime(runtime); let mut vm = VM::with_runtime(runtime);
let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); let result = vm
.execute_module(&compile_result.module)
.expect("vm exec failed");
assert_eq!(result.to_string_box().value, "ArrayBox"); assert_eq!(result.to_string_box().value, "ArrayBox");
// 2) equals: (new IntegerBox(5)).equals(5) == true // 2) equals: (new IntegerBox(5)).equals(5) == true
@ -820,7 +861,9 @@ return (new IntegerBox(5)).equals(5)
let mut compiler = crate::mir::MirCompiler::new(); let mut compiler = crate::mir::MirCompiler::new();
let compile_result = compiler.compile(ast).expect("mir compile failed"); let compile_result = compiler.compile(ast).expect("mir compile failed");
let mut vm = VM::with_runtime(runtime); let mut vm = VM::with_runtime(runtime);
let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); let result = vm
.execute_module(&compile_result.module)
.expect("vm exec failed");
assert_eq!(result.to_string_box().value, "true"); assert_eq!(result.to_string_box().value, "true");
} }
} }

View File

@ -7,60 +7,109 @@
* Typical Callers: vm_instructions::execute_boxcall / VM::call_unified_method * Typical Callers: vm_instructions::execute_boxcall / VM::call_unified_method
*/ */
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox}; use super::vm::{VMError, VMValue, VM};
use super::vm::{VM, VMError, VMValue}; use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox};
impl VM { impl VM {
/// Call a method on a Box - simplified version of interpreter method dispatch /// Call a method on a Box - simplified version of interpreter method dispatch
pub(super) fn call_box_method_impl(&self, box_value: Box<dyn NyashBox>, method: &str, _args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, VMError> { pub(super) fn call_box_method_impl(
&self,
box_value: Box<dyn NyashBox>,
method: &str,
_args: Vec<Box<dyn NyashBox>>,
) -> Result<Box<dyn NyashBox>, VMError> {
// PluginBoxV2: delegate to unified plugin host (BID-FFI v1) // PluginBoxV2: delegate to unified plugin host (BID-FFI v1)
if let Some(pbox) = box_value.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() { if let Some(pbox) = box_value
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") { if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
eprintln!("[VM][BoxCall→PluginInvoke] {}.{} inst_id={}", pbox.box_type, method, pbox.inner.instance_id); eprintln!(
"[VM][BoxCall→PluginInvoke] {}.{} inst_id={}",
pbox.box_type, method, pbox.inner.instance_id
);
} }
let host = crate::runtime::get_global_plugin_host(); let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap(); let host = host.read().unwrap();
match host.invoke_instance_method(&pbox.box_type, method, pbox.inner.instance_id, &_args) { match host.invoke_instance_method(
Ok(Some(val)) => { return Ok(val); } &pbox.box_type,
Ok(None) => { return Ok(Box::new(crate::box_trait::VoidBox::new())); } method,
pbox.inner.instance_id,
&_args,
) {
Ok(Some(val)) => {
return Ok(val);
}
Ok(None) => {
return Ok(Box::new(crate::box_trait::VoidBox::new()));
}
Err(e) => { Err(e) => {
return Err(VMError::InvalidInstruction(format!("PluginInvoke failed via BoxCall: {}.{} ({})", pbox.box_type, method, e.message()))); return Err(VMError::InvalidInstruction(format!(
"PluginInvoke failed via BoxCall: {}.{} ({})",
pbox.box_type,
method,
e.message()
)));
} }
} }
} }
// MathBox methods (minimal set used in 10.9) // MathBox methods (minimal set used in 10.9)
if let Some(math) = box_value.as_any().downcast_ref::<crate::boxes::math_box::MathBox>() { if let Some(math) = box_value
.as_any()
.downcast_ref::<crate::boxes::math_box::MathBox>()
{
match method { match method {
"min" => { "min" => {
if _args.len() >= 2 { return Ok(math.min(_args[0].clone_or_share(), _args[1].clone_or_share())); } if _args.len() >= 2 {
return Ok(math.min(_args[0].clone_or_share(), _args[1].clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: min(a, b) requires 2 args"))); return Ok(Box::new(StringBox::new("Error: min(a, b) requires 2 args")));
} }
"max" => { "max" => {
if _args.len() >= 2 { return Ok(math.max(_args[0].clone_or_share(), _args[1].clone_or_share())); } if _args.len() >= 2 {
return Ok(math.max(_args[0].clone_or_share(), _args[1].clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: max(a, b) requires 2 args"))); return Ok(Box::new(StringBox::new("Error: max(a, b) requires 2 args")));
} }
"abs" => { "abs" => {
if let Some(v) = _args.get(0) { return Ok(math.abs(v.clone_or_share())); } if let Some(v) = _args.get(0) {
return Ok(math.abs(v.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: abs(x) requires 1 arg"))); return Ok(Box::new(StringBox::new("Error: abs(x) requires 1 arg")));
} }
"sin" => { "sin" => {
if let Some(v) = _args.get(0) { return Ok(math.sin(v.clone_or_share())); } if let Some(v) = _args.get(0) {
return Ok(math.sin(v.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: sin(x) requires 1 arg"))); return Ok(Box::new(StringBox::new("Error: sin(x) requires 1 arg")));
} }
"cos" => { "cos" => {
if let Some(v) = _args.get(0) { return Ok(math.cos(v.clone_or_share())); } if let Some(v) = _args.get(0) {
return Ok(math.cos(v.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: cos(x) requires 1 arg"))); return Ok(Box::new(StringBox::new("Error: cos(x) requires 1 arg")));
} }
_ => { return Ok(Box::new(VoidBox::new())); } _ => {
return Ok(Box::new(VoidBox::new()));
}
} }
} }
// ResultBox (NyashResultBox - new) // ResultBox (NyashResultBox - new)
if let Some(result_box) = box_value.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() { if let Some(result_box) = box_value
.as_any()
.downcast_ref::<crate::boxes::result::NyashResultBox>()
{
match method { match method {
"is_ok" | "isOk" => { return Ok(result_box.is_ok()); } "is_ok" | "isOk" => {
"get_value" | "getValue" => { return Ok(result_box.get_value()); } return Ok(result_box.is_ok());
"get_error" | "getError" => { return Ok(result_box.get_error()); } }
"get_value" | "getValue" => {
return Ok(result_box.get_value());
}
"get_error" | "getError" => {
return Ok(result_box.get_error());
}
_ => return Ok(Box::new(VoidBox::new())), _ => return Ok(Box::new(VoidBox::new())),
} }
} }
@ -73,10 +122,17 @@ impl VM {
} }
// JitConfigBox methods // JitConfigBox methods
if let Some(jcb) = box_value.as_any().downcast_ref::<crate::boxes::jit_config_box::JitConfigBox>() { if let Some(jcb) = box_value
.as_any()
.downcast_ref::<crate::boxes::jit_config_box::JitConfigBox>()
{
match method { match method {
"get" => { "get" => {
if let Some(k) = _args.get(0) { return Ok(jcb.get_flag(&k.to_string_box().value).unwrap_or_else(|e| Box::new(StringBox::new(e.to_string())))); } if let Some(k) = _args.get(0) {
return Ok(jcb
.get_flag(&k.to_string_box().value)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("get(name) requires 1 arg"))); return Ok(Box::new(StringBox::new("get(name) requires 1 arg")));
} }
"set" => { "set" => {
@ -84,42 +140,73 @@ impl VM {
let k = _args[0].to_string_box().value; let k = _args[0].to_string_box().value;
let v = _args[1].to_string_box().value; let v = _args[1].to_string_box().value;
let on = matches!(v.as_str(), "1" | "true" | "True" | "on" | "ON"); let on = matches!(v.as_str(), "1" | "true" | "True" | "on" | "ON");
return Ok(jcb.set_flag(&k, on).unwrap_or_else(|e| Box::new(StringBox::new(e.to_string())))); return Ok(jcb
.set_flag(&k, on)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
} }
return Ok(Box::new(StringBox::new("set(name, bool) requires 2 args"))); return Ok(Box::new(StringBox::new("set(name, bool) requires 2 args")));
} }
"getThreshold" => { return Ok(jcb.get_threshold()); } "getThreshold" => {
return Ok(jcb.get_threshold());
}
"setThreshold" => { "setThreshold" => {
if let Some(v) = _args.get(0) { if let Some(v) = _args.get(0) {
let iv = v.to_string_box().value.parse::<i64>().unwrap_or(0); let iv = v.to_string_box().value.parse::<i64>().unwrap_or(0);
return Ok(jcb.set_threshold(iv).unwrap_or_else(|e| Box::new(StringBox::new(e.to_string())))); return Ok(jcb
.set_threshold(iv)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
} }
return Ok(Box::new(StringBox::new("setThreshold(n) requires 1 arg"))); return Ok(Box::new(StringBox::new("setThreshold(n) requires 1 arg")));
} }
"apply" => { return Ok(jcb.apply()); } "apply" => {
"toJson" => { return Ok(jcb.to_json()); } return Ok(jcb.apply());
}
"toJson" => {
return Ok(jcb.to_json());
}
"fromJson" => { "fromJson" => {
if let Some(s) = _args.get(0) { return Ok(jcb.from_json(&s.to_string_box().value).unwrap_or_else(|e| Box::new(StringBox::new(e.to_string())))); } if let Some(s) = _args.get(0) {
return Ok(jcb
.from_json(&s.to_string_box().value)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("fromJson(json) requires 1 arg"))); return Ok(Box::new(StringBox::new("fromJson(json) requires 1 arg")));
} }
"enable" => { "enable" => {
if let Some(k) = _args.get(0) { return Ok(jcb.set_flag(&k.to_string_box().value, true).unwrap_or_else(|e| Box::new(StringBox::new(e.to_string())))); } if let Some(k) = _args.get(0) {
return Ok(jcb
.set_flag(&k.to_string_box().value, true)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("enable(name) requires 1 arg"))); return Ok(Box::new(StringBox::new("enable(name) requires 1 arg")));
} }
"disable" => { "disable" => {
if let Some(k) = _args.get(0) { return Ok(jcb.set_flag(&k.to_string_box().value, false).unwrap_or_else(|e| Box::new(StringBox::new(e.to_string())))); } if let Some(k) = _args.get(0) {
return Ok(jcb
.set_flag(&k.to_string_box().value, false)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("disable(name) requires 1 arg"))); return Ok(Box::new(StringBox::new("disable(name) requires 1 arg")));
} }
"summary" => { return Ok(jcb.summary()); } "summary" => {
_ => { return Ok(Box::new(VoidBox::new())); } return Ok(jcb.summary());
}
_ => {
return Ok(Box::new(VoidBox::new()));
}
} }
} }
// JitPolicyBox methods // JitPolicyBox methods
if let Some(jpb) = box_value.as_any().downcast_ref::<crate::boxes::jit_policy_box::JitPolicyBox>() { if let Some(jpb) = box_value
.as_any()
.downcast_ref::<crate::boxes::jit_policy_box::JitPolicyBox>()
{
match method { match method {
"get" => { "get" => {
if let Some(k) = _args.get(0) { return Ok(jpb.get_flag(&k.to_string_box().value)); } if let Some(k) = _args.get(0) {
return Ok(jpb.get_flag(&k.to_string_box().value));
}
return Ok(Box::new(StringBox::new("get(name) requires 1 arg"))); return Ok(Box::new(StringBox::new("get(name) requires 1 arg")));
} }
"set" => { "set" => {
@ -132,37 +219,61 @@ impl VM {
return Ok(Box::new(StringBox::new("set(name, bool) requires 2 args"))); return Ok(Box::new(StringBox::new("set(name, bool) requires 2 args")));
} }
"setWhitelistCsv" | "set_whitelist_csv" => { "setWhitelistCsv" | "set_whitelist_csv" => {
if let Some(s) = _args.get(0) { return Ok(jpb.set_whitelist_csv(&s.to_string_box().value)); } if let Some(s) = _args.get(0) {
return Ok(Box::new(StringBox::new("setWhitelistCsv(csv) requires 1 arg"))); return Ok(jpb.set_whitelist_csv(&s.to_string_box().value));
}
return Ok(Box::new(StringBox::new(
"setWhitelistCsv(csv) requires 1 arg",
)));
} }
"addWhitelist" | "add_whitelist" => { "addWhitelist" | "add_whitelist" => {
if let Some(s) = _args.get(0) { return Ok(jpb.add_whitelist(&s.to_string_box().value)); } if let Some(s) = _args.get(0) {
return Ok(Box::new(StringBox::new("addWhitelist(name) requires 1 arg"))); return Ok(jpb.add_whitelist(&s.to_string_box().value));
}
return Ok(Box::new(StringBox::new(
"addWhitelist(name) requires 1 arg",
)));
}
"clearWhitelist" | "clear_whitelist" => {
return Ok(jpb.clear_whitelist());
} }
"clearWhitelist" | "clear_whitelist" => { return Ok(jpb.clear_whitelist()); }
"enablePreset" | "enable_preset" => { "enablePreset" | "enable_preset" => {
if let Some(s) = _args.get(0) { return Ok(jpb.enable_preset(&s.to_string_box().value)); } if let Some(s) = _args.get(0) {
return Ok(Box::new(StringBox::new("enablePreset(name) requires 1 arg"))); return Ok(jpb.enable_preset(&s.to_string_box().value));
}
return Ok(Box::new(StringBox::new(
"enablePreset(name) requires 1 arg",
)));
}
_ => {
return Ok(Box::new(VoidBox::new()));
} }
_ => { return Ok(Box::new(VoidBox::new())); }
} }
} }
// JitStatsBox methods // JitStatsBox methods
if let Some(jsb) = box_value.as_any().downcast_ref::<crate::boxes::jit_stats_box::JitStatsBox>() { if let Some(jsb) = box_value
.as_any()
.downcast_ref::<crate::boxes::jit_stats_box::JitStatsBox>()
{
match method { match method {
"toJson" => { return Ok(jsb.to_json()); } "toJson" => {
return Ok(jsb.to_json());
}
"top5" => { "top5" => {
if let Some(jm) = &self.jit_manager { if let Some(jm) = &self.jit_manager {
let v = jm.top_hits(5); let v = jm.top_hits(5);
let arr: Vec<serde_json::Value> = v.into_iter().map(|(name, hits, compiled, handle)| { let arr: Vec<serde_json::Value> = v
.into_iter()
.map(|(name, hits, compiled, handle)| {
serde_json::json!({ serde_json::json!({
"name": name, "name": name,
"hits": hits, "hits": hits,
"compiled": compiled, "compiled": compiled,
"handle": handle "handle": handle
}) })
}).collect(); })
.collect();
let s = serde_json::to_string(&arr).unwrap_or_else(|_| "[]".to_string()); let s = serde_json::to_string(&arr).unwrap_or_else(|_| "[]".to_string());
return Ok(Box::new(StringBox::new(s))); return Ok(Box::new(StringBox::new(s)));
} }
@ -171,7 +282,11 @@ impl VM {
"summary" => { "summary" => {
let cfg = crate::jit::config::current(); let cfg = crate::jit::config::current();
let caps = crate::jit::config::probe_capabilities(); let caps = crate::jit::config::probe_capabilities();
let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig { "b1_bool" } else { "i64_bool" }; let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig {
"b1_bool"
} else {
"i64_bool"
};
let b1_norm = crate::jit::rt::b1_norm_get(); let b1_norm = crate::jit::rt::b1_norm_get();
let ret_b1 = crate::jit::rt::ret_bool_hint_get(); let ret_b1 = crate::jit::rt::ret_bool_hint_get();
let mut payload = serde_json::json!({ let mut payload = serde_json::json!({
@ -192,15 +307,25 @@ impl VM {
let per_arr: Vec<serde_json::Value> = perf.into_iter().map(|(name, phi_t, phi_b1, rb, hits, compiled, handle)| serde_json::json!({ let per_arr: Vec<serde_json::Value> = perf.into_iter().map(|(name, phi_t, phi_b1, rb, hits, compiled, handle)| serde_json::json!({
"name": name, "phi_total": phi_t, "phi_b1": phi_b1, "ret_bool_hint": rb, "hits": hits, "compiled": compiled, "handle": handle "name": name, "phi_total": phi_t, "phi_b1": phi_b1, "ret_bool_hint": rb, "hits": hits, "compiled": compiled, "handle": handle
})).collect(); })).collect();
if let Some(obj) = payload.as_object_mut() { obj.insert("top5".to_string(), serde_json::Value::Array(top5)); obj.insert("perFunction".to_string(), serde_json::Value::Array(per_arr)); } if let Some(obj) = payload.as_object_mut() {
obj.insert("top5".to_string(), serde_json::Value::Array(top5));
obj.insert(
"perFunction".to_string(),
serde_json::Value::Array(per_arr),
);
} }
let s = serde_json::to_string_pretty(&payload).unwrap_or_else(|_| "{}".to_string()); }
let s =
serde_json::to_string_pretty(&payload).unwrap_or_else(|_| "{}".to_string());
return Ok(Box::new(StringBox::new(s))); return Ok(Box::new(StringBox::new(s)));
} }
"perFunction" | "per_function" => { "perFunction" | "per_function" => {
if let Some(jm) = &self.jit_manager { if let Some(jm) = &self.jit_manager {
let v = jm.per_function_stats(); let v = jm.per_function_stats();
let arr: Vec<serde_json::Value> = v.into_iter().map(|(name, phi_t, phi_b1, rb, hits, compiled, handle)| serde_json::json!({ let arr: Vec<serde_json::Value> = v
.into_iter()
.map(|(name, phi_t, phi_b1, rb, hits, compiled, handle)| {
serde_json::json!({
"name": name, "name": name,
"phi_total": phi_t, "phi_total": phi_t,
"phi_b1": phi_b1, "phi_b1": phi_b1,
@ -208,25 +333,40 @@ impl VM {
"hits": hits, "hits": hits,
"compiled": compiled, "compiled": compiled,
"handle": handle, "handle": handle,
})).collect(); })
let s = serde_json::to_string_pretty(&arr).unwrap_or_else(|_| "[]".to_string()); })
.collect();
let s =
serde_json::to_string_pretty(&arr).unwrap_or_else(|_| "[]".to_string());
return Ok(Box::new(StringBox::new(s))); return Ok(Box::new(StringBox::new(s)));
} }
return Ok(Box::new(StringBox::new("[]"))); return Ok(Box::new(StringBox::new("[]")));
} }
_ => { return Ok(Box::new(VoidBox::new())); } _ => {
return Ok(Box::new(VoidBox::new()));
}
} }
} }
// StringBox methods // StringBox methods
if let Some(string_box) = box_value.as_any().downcast_ref::<StringBox>() { if let Some(string_box) = box_value.as_any().downcast_ref::<StringBox>() {
match method { match method {
"length" | "len" => { return Ok(Box::new(IntegerBox::new(string_box.value.len() as i64))); } "length" | "len" => {
"toString" => { return Ok(Box::new(StringBox::new(string_box.value.clone()))); } return Ok(Box::new(IntegerBox::new(string_box.value.len() as i64)));
}
"toString" => {
return Ok(Box::new(StringBox::new(string_box.value.clone())));
}
"substring" => { "substring" => {
if _args.len() >= 2 { if _args.len() >= 2 {
let s = match _args[0].to_string_box().value.parse::<i64>() { Ok(v) => v.max(0) as usize, Err(_) => 0 }; let s = match _args[0].to_string_box().value.parse::<i64>() {
let e = match _args[1].to_string_box().value.parse::<i64>() { Ok(v) => v.max(0) as usize, Err(_) => string_box.value.chars().count() }; Ok(v) => v.max(0) as usize,
Err(_) => 0,
};
let e = match _args[1].to_string_box().value.parse::<i64>() {
Ok(v) => v.max(0) as usize,
Err(_) => string_box.value.chars().count(),
};
return Ok(string_box.substring(s, e)); return Ok(string_box.substring(s, e));
} }
return Ok(Box::new(VoidBox::new())); return Ok(Box::new(VoidBox::new()));
@ -243,115 +383,345 @@ impl VM {
} }
// ArrayBox methods (minimal set) // ArrayBox methods (minimal set)
if let Some(array_box) = box_value.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() { if let Some(array_box) = box_value
.as_any()
.downcast_ref::<crate::boxes::array::ArrayBox>()
{
match method { match method {
"push" => { if let Some(v) = _args.get(0) { return Ok(array_box.push(v.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: push(value) requires 1 arg"))); } "push" => {
"pop" => { return Ok(array_box.pop()); }, if let Some(v) = _args.get(0) {
"length" | "len" => { return Ok(array_box.length()); }, return Ok(array_box.push(v.clone_or_share()));
"get" => { if let Some(i) = _args.get(0) { return Ok(array_box.get(i.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: get(index) requires 1 arg"))); } }
"set" => { if _args.len() >= 2 { return Ok(array_box.set(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: set(index, value) requires 2 args"))); } return Ok(Box::new(StringBox::new(
"remove" => { if let Some(i) = _args.get(0) { return Ok(array_box.remove(i.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: remove(index) requires 1 arg"))); } "Error: push(value) requires 1 arg",
"contains" => { if let Some(v) = _args.get(0) { return Ok(array_box.contains(v.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: contains(value) requires 1 arg"))); } )));
"indexOf" => { if let Some(v) = _args.get(0) { return Ok(array_box.indexOf(v.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: indexOf(value) requires 1 arg"))); } }
"clear" => { return Ok(array_box.clear()); }, "pop" => {
"join" => { if let Some(sep) = _args.get(0) { return Ok(array_box.join(sep.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: join(sep) requires 1 arg"))); } return Ok(array_box.pop());
"sort" => { return Ok(array_box.sort()); }, }
"reverse" => { return Ok(array_box.reverse()); }, "length" | "len" => {
"slice" => { if _args.len() >= 2 { return Ok(array_box.slice(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: slice(start, end) requires 2 args"))); } return Ok(array_box.length());
}
"get" => {
if let Some(i) = _args.get(0) {
return Ok(array_box.get(i.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: get(index) requires 1 arg")));
}
"set" => {
if _args.len() >= 2 {
return Ok(
array_box.set(_args[0].clone_or_share(), _args[1].clone_or_share())
);
}
return Ok(Box::new(StringBox::new(
"Error: set(index, value) requires 2 args",
)));
}
"remove" => {
if let Some(i) = _args.get(0) {
return Ok(array_box.remove(i.clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: remove(index) requires 1 arg",
)));
}
"contains" => {
if let Some(v) = _args.get(0) {
return Ok(array_box.contains(v.clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: contains(value) requires 1 arg",
)));
}
"indexOf" => {
if let Some(v) = _args.get(0) {
return Ok(array_box.indexOf(v.clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: indexOf(value) requires 1 arg",
)));
}
"clear" => {
return Ok(array_box.clear());
}
"join" => {
if let Some(sep) = _args.get(0) {
return Ok(array_box.join(sep.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: join(sep) requires 1 arg")));
}
"sort" => {
return Ok(array_box.sort());
}
"reverse" => {
return Ok(array_box.reverse());
}
"slice" => {
if _args.len() >= 2 {
return Ok(
array_box.slice(_args[0].clone_or_share(), _args[1].clone_or_share())
);
}
return Ok(Box::new(StringBox::new(
"Error: slice(start, end) requires 2 args",
)));
}
_ => return Ok(Box::new(VoidBox::new())), _ => return Ok(Box::new(VoidBox::new())),
} }
} }
// MapBox methods (minimal set) // MapBox methods (minimal set)
if let Some(map_box) = box_value.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() { if let Some(map_box) = box_value
.as_any()
.downcast_ref::<crate::boxes::map_box::MapBox>()
{
match method { match method {
"set" => { if _args.len() >= 2 { return Ok(map_box.set(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: set(key, value) requires 2 args"))); } "set" => {
"get" => { if let Some(k) = _args.get(0) { return Ok(map_box.get(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: get(key) requires 1 arg"))); } if _args.len() >= 2 {
"has" => { if let Some(k) = _args.get(0) { return Ok(map_box.has(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: has(key) requires 1 arg"))); } return Ok(
"delete" | "remove" => { if let Some(k) = _args.get(0) { return Ok(map_box.delete(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: delete(key) requires 1 arg"))); } map_box.set(_args[0].clone_or_share(), _args[1].clone_or_share())
"keys" => { return Ok(map_box.keys()); }, );
"values" => { return Ok(map_box.values()); }, }
"size" => { return Ok(map_box.size()); }, return Ok(Box::new(StringBox::new(
"clear" => { return Ok(map_box.clear()); }, "Error: set(key, value) requires 2 args",
)));
}
"get" => {
if let Some(k) = _args.get(0) {
return Ok(map_box.get(k.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: get(key) requires 1 arg")));
}
"has" => {
if let Some(k) = _args.get(0) {
return Ok(map_box.has(k.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: has(key) requires 1 arg")));
}
"delete" | "remove" => {
if let Some(k) = _args.get(0) {
return Ok(map_box.delete(k.clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: delete(key) requires 1 arg",
)));
}
"keys" => {
return Ok(map_box.keys());
}
"values" => {
return Ok(map_box.values());
}
"size" => {
return Ok(map_box.size());
}
"clear" => {
return Ok(map_box.clear());
}
_ => return Ok(Box::new(VoidBox::new())), _ => return Ok(Box::new(VoidBox::new())),
} }
} }
// TaskGroupBox methods (scaffold → instance内の所有Futureに対して実行) // TaskGroupBox methods (scaffold → instance内の所有Futureに対して実行)
if box_value.as_any().downcast_ref::<crate::boxes::task_group_box::TaskGroupBox>().is_some() { if box_value
.as_any()
.downcast_ref::<crate::boxes::task_group_box::TaskGroupBox>()
.is_some()
{
let mut owned = box_value; let mut owned = box_value;
if let Some(tg) = (&mut *owned).as_any_mut().downcast_mut::<crate::boxes::task_group_box::TaskGroupBox>() { if let Some(tg) = (&mut *owned)
.as_any_mut()
.downcast_mut::<crate::boxes::task_group_box::TaskGroupBox>()
{
match method { match method {
"cancelAll" | "cancel_all" => { return Ok(tg.cancelAll()); } "cancelAll" | "cancel_all" => {
return Ok(tg.cancelAll());
}
"joinAll" | "join_all" => { "joinAll" | "join_all" => {
let ms = _args.get(0).map(|a| a.to_string_box().value.parse::<i64>().unwrap_or(2000)); let ms = _args
.get(0)
.map(|a| a.to_string_box().value.parse::<i64>().unwrap_or(2000));
return Ok(tg.joinAll(ms)); return Ok(tg.joinAll(ms));
} }
_ => { return Ok(Box::new(VoidBox::new())); } _ => {
return Ok(Box::new(VoidBox::new()));
}
} }
} }
return Ok(Box::new(VoidBox::new())); return Ok(Box::new(VoidBox::new()));
} }
// P2PBox methods (minimal) // P2PBox methods (minimal)
if let Some(p2p) = box_value.as_any().downcast_ref::<crate::boxes::p2p_box::P2PBox>() { if let Some(p2p) = box_value
.as_any()
.downcast_ref::<crate::boxes::p2p_box::P2PBox>()
{
match method { match method {
"send" => { "send" => {
if _args.len() >= 2 { return Ok(p2p.send(_args[0].clone_or_share(), _args[1].clone_or_share())); } if _args.len() >= 2 {
return Ok(Box::new(StringBox::new("Error: send(to, intent) requires 2 args"))); return Ok(p2p.send(_args[0].clone_or_share(), _args[1].clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: send(to, intent) requires 2 args",
)));
} }
"on" => { "on" => {
if _args.len() >= 2 { return Ok(p2p.on(_args[0].clone_or_share(), _args[1].clone_or_share())); } if _args.len() >= 2 {
return Ok(Box::new(StringBox::new("Error: on(intent, handler) requires 2 args"))); return Ok(p2p.on(_args[0].clone_or_share(), _args[1].clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: on(intent, handler) requires 2 args",
)));
} }
"onOnce" | "on_once" => { "onOnce" | "on_once" => {
if _args.len() >= 2 { return Ok(p2p.on_once(_args[0].clone_or_share(), _args[1].clone_or_share())); } if _args.len() >= 2 {
return Ok(Box::new(StringBox::new("Error: onOnce(intent, handler) requires 2 args"))); return Ok(
p2p.on_once(_args[0].clone_or_share(), _args[1].clone_or_share())
);
}
return Ok(Box::new(StringBox::new(
"Error: onOnce(intent, handler) requires 2 args",
)));
} }
"off" => { "off" => {
if _args.len() >= 1 { return Ok(p2p.off(_args[0].clone_or_share())); } if _args.len() >= 1 {
return Ok(Box::new(StringBox::new("Error: off(intent) requires 1 arg"))); return Ok(p2p.off(_args[0].clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: off(intent) requires 1 arg",
)));
}
"getLastFrom" => {
return Ok(p2p.get_last_from());
}
"getLastIntentName" => {
return Ok(p2p.get_last_intent_name());
}
"debug_nodes" | "debugNodes" => {
return Ok(p2p.debug_nodes());
}
"getNodeId" | "getId" => {
return Ok(p2p.get_node_id());
}
"getTransportType" | "transport" => {
return Ok(p2p.get_transport_type());
}
"isReachable" => {
if let Some(n) = _args.get(0) {
return Ok(p2p.is_reachable(n.clone_or_share()));
}
return Ok(Box::new(BoolBox::new(false)));
} }
"getLastFrom" => { return Ok(p2p.get_last_from()); }
"getLastIntentName" => { return Ok(p2p.get_last_intent_name()); }
"debug_nodes" | "debugNodes" => { return Ok(p2p.debug_nodes()); }
"getNodeId" | "getId" => { return Ok(p2p.get_node_id()); }
"getTransportType" | "transport" => { return Ok(p2p.get_transport_type()); }
"isReachable" => { if let Some(n) = _args.get(0) { return Ok(p2p.is_reachable(n.clone_or_share())); } return Ok(Box::new(BoolBox::new(false))); }
_ => return Ok(Box::new(VoidBox::new())), _ => return Ok(Box::new(VoidBox::new())),
} }
} }
// SocketBox methods (minimal + timeouts) // SocketBox methods (minimal + timeouts)
if let Some(sock) = box_value.as_any().downcast_ref::<crate::boxes::socket_box::SocketBox>() { if let Some(sock) = box_value
.as_any()
.downcast_ref::<crate::boxes::socket_box::SocketBox>()
{
match method { match method {
"bind" => { if _args.len() >= 2 { return Ok(sock.bind(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: bind(address, port) requires 2 args"))); } "bind" => {
"listen" => { if let Some(b) = _args.get(0) { return Ok(sock.listen(b.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: listen(backlog) requires 1 arg"))); } if _args.len() >= 2 {
"accept" => { return Ok(sock.accept()); }, return Ok(sock.bind(_args[0].clone_or_share(), _args[1].clone_or_share()));
"acceptTimeout" | "accept_timeout" => { if let Some(ms) = _args.get(0) { return Ok(sock.accept_timeout(ms.clone_or_share())); } return Ok(Box::new(crate::box_trait::VoidBox::new())); } }
"connect" => { if _args.len() >= 2 { return Ok(sock.connect(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: connect(address, port) requires 2 args"))); } return Ok(Box::new(StringBox::new(
"read" => { return Ok(sock.read()); }, "Error: bind(address, port) requires 2 args",
"recvTimeout" | "recv_timeout" => { if let Some(ms) = _args.get(0) { return Ok(sock.recv_timeout(ms.clone_or_share())); } return Ok(Box::new(StringBox::new(""))); } )));
"write" => { if let Some(d) = _args.get(0) { return Ok(sock.write(d.clone_or_share())); } return Ok(Box::new(crate::box_trait::BoolBox::new(false))); } }
"close" => { return Ok(sock.close()); }, "listen" => {
"isServer" | "is_server" => { return Ok(sock.is_server()); }, if let Some(b) = _args.get(0) {
"isConnected" | "is_connected" => { return Ok(sock.is_connected()); }, return Ok(sock.listen(b.clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: listen(backlog) requires 1 arg",
)));
}
"accept" => {
return Ok(sock.accept());
}
"acceptTimeout" | "accept_timeout" => {
if let Some(ms) = _args.get(0) {
return Ok(sock.accept_timeout(ms.clone_or_share()));
}
return Ok(Box::new(crate::box_trait::VoidBox::new()));
}
"connect" => {
if _args.len() >= 2 {
return Ok(
sock.connect(_args[0].clone_or_share(), _args[1].clone_or_share())
);
}
return Ok(Box::new(StringBox::new(
"Error: connect(address, port) requires 2 args",
)));
}
"read" => {
return Ok(sock.read());
}
"recvTimeout" | "recv_timeout" => {
if let Some(ms) = _args.get(0) {
return Ok(sock.recv_timeout(ms.clone_or_share()));
}
return Ok(Box::new(StringBox::new("")));
}
"write" => {
if let Some(d) = _args.get(0) {
return Ok(sock.write(d.clone_or_share()));
}
return Ok(Box::new(crate::box_trait::BoolBox::new(false)));
}
"close" => {
return Ok(sock.close());
}
"isServer" | "is_server" => {
return Ok(sock.is_server());
}
"isConnected" | "is_connected" => {
return Ok(sock.is_connected());
}
_ => return Ok(Box::new(VoidBox::new())), _ => return Ok(Box::new(VoidBox::new())),
} }
} }
// IntegerBox methods // IntegerBox methods
if let Some(integer_box) = box_value.as_any().downcast_ref::<IntegerBox>() { match method { "toString" => { return Ok(Box::new(StringBox::new(integer_box.value.to_string()))); }, "abs" => { return Ok(Box::new(IntegerBox::new(integer_box.value.abs()))); }, _ => return Ok(Box::new(VoidBox::new())), } } if let Some(integer_box) = box_value.as_any().downcast_ref::<IntegerBox>() {
match method {
"toString" => {
return Ok(Box::new(StringBox::new(integer_box.value.to_string())));
}
"abs" => {
return Ok(Box::new(IntegerBox::new(integer_box.value.abs())));
}
_ => return Ok(Box::new(VoidBox::new())),
}
}
// BoolBox methods // BoolBox methods
if let Some(bool_box) = box_value.as_any().downcast_ref::<BoolBox>() { match method { "toString" => { return Ok(Box::new(StringBox::new(bool_box.value.to_string()))); }, _ => return Ok(Box::new(VoidBox::new())), } } if let Some(bool_box) = box_value.as_any().downcast_ref::<BoolBox>() {
match method {
"toString" => {
return Ok(Box::new(StringBox::new(bool_box.value.to_string())));
}
_ => return Ok(Box::new(VoidBox::new())),
}
}
// Default: return void for any unrecognized box type or method // Default: return void for any unrecognized box type or method
Ok(Box::new(VoidBox::new())) Ok(Box::new(VoidBox::new()))
} }
/// Debug helper for BoxCall tracing (enabled via NYASH_VM_DEBUG_BOXCALL=1) /// Debug helper for BoxCall tracing (enabled via NYASH_VM_DEBUG_BOXCALL=1)
pub(super) fn debug_log_boxcall(&self, recv: &VMValue, method: &str, args: &[Box<dyn NyashBox>], stage: &str, result: Option<&VMValue>) { pub(super) fn debug_log_boxcall(
&self,
recv: &VMValue,
method: &str,
args: &[Box<dyn NyashBox>],
stage: &str,
result: Option<&VMValue>,
) {
if std::env::var("NYASH_VM_DEBUG_BOXCALL").ok().as_deref() == Some("1") { if std::env::var("NYASH_VM_DEBUG_BOXCALL").ok().as_deref() == Some("1") {
let recv_ty = match recv { let recv_ty = match recv {
VMValue::BoxRef(arc) => arc.type_name().to_string(), VMValue::BoxRef(arc) => arc.type_name().to_string(),
@ -373,9 +743,24 @@ impl VM {
VMValue::Future(_) => "Future".to_string(), VMValue::Future(_) => "Future".to_string(),
VMValue::Void => "Void".to_string(), VMValue::Void => "Void".to_string(),
}; };
eprintln!("[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?} => result_ty={}", stage, recv_ty, method, args.len(), args_desc, res_ty); eprintln!(
"[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?} => result_ty={}",
stage,
recv_ty,
method,
args.len(),
args_desc,
res_ty
);
} else { } else {
eprintln!("[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?}", stage, recv_ty, method, args.len(), args_desc); eprintln!(
"[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?}",
stage,
recv_ty,
method,
args.len(),
args_desc
);
} }
} }
} }

View File

@ -1,5 +1,5 @@
use crate::mir::BasicBlockId;
use crate::backend::vm::VMValue; use crate::backend::vm::VMValue;
use crate::mir::BasicBlockId;
/// Control flow result from instruction execution /// Control flow result from instruction execution
pub(crate) enum ControlFlow { pub(crate) enum ControlFlow {

View File

@ -11,10 +11,10 @@
* Behavior and public APIs are preserved. This is a pure move/refactor. * Behavior and public APIs are preserved. This is a pure move/refactor.
*/ */
use crate::mir::{MirModule, MirFunction, MirInstruction, BasicBlockId}; use super::{vm::VMError, vm::VMValue, vm::VM};
use crate::box_trait::NyashBox;
use super::{vm::VM, vm::VMError, vm::VMValue};
use crate::backend::vm_control_flow::ControlFlow; use crate::backend::vm_control_flow::ControlFlow;
use crate::box_trait::NyashBox;
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, MirModule};
impl VM { impl VM {
/// Execute a MIR module /// Execute a MIR module
@ -34,12 +34,17 @@ impl VM {
if crate::config::env::vm_pic_stats() { if crate::config::env::vm_pic_stats() {
self.print_cache_stats_summary(); self.print_cache_stats_summary();
} }
if let Some(jm) = &self.jit_manager { jm.print_summary(); } if let Some(jm) = &self.jit_manager {
jm.print_summary();
}
{ {
let lvl = crate::config::env::gc_trace_level(); let lvl = crate::config::env::gc_trace_level();
if lvl > 0 { if lvl > 0 {
if let Some((sp, rd, wr)) = self.runtime.gc.snapshot_counters() { if let Some((sp, rd, wr)) = self.runtime.gc.snapshot_counters() {
eprintln!("[GC] counters: safepoints={} read_barriers={} write_barriers={}", sp, rd, wr); eprintln!(
"[GC] counters: safepoints={} read_barriers={} write_barriers={}",
sp, rd, wr
);
} }
let roots_total = self.scope_tracker.root_count_total(); let roots_total = self.scope_tracker.root_count_total();
let root_regions = self.scope_tracker.root_regions(); let root_regions = self.scope_tracker.root_regions();
@ -48,8 +53,12 @@ impl VM {
"[GC] mock_mark: roots_total={} regions={} object_field_slots={}", "[GC] mock_mark: roots_total={} regions={} object_field_slots={}",
roots_total, root_regions, field_slots roots_total, root_regions, field_slots
); );
if lvl >= 2 { self.gc_print_roots_breakdown(); } if lvl >= 2 {
if lvl >= 3 { self.gc_print_reachability_depth2(); } self.gc_print_roots_breakdown();
}
if lvl >= 3 {
self.gc_print_reachability_depth2();
}
} }
} }
Ok(result.to_nyash_box()) Ok(result.to_nyash_box())
@ -97,9 +106,9 @@ impl VM {
.module .module
.as_ref() .as_ref()
.ok_or_else(|| VMError::InvalidInstruction("No active module".to_string()))?; .ok_or_else(|| VMError::InvalidInstruction("No active module".to_string()))?;
let function_ref = module_ref let function_ref = module_ref.get_function(func_name).ok_or_else(|| {
.get_function(func_name) VMError::InvalidInstruction(format!("Function '{}' not found", func_name))
.ok_or_else(|| VMError::InvalidInstruction(format!("Function '{}' not found", func_name)))?; })?;
let function = function_ref.clone(); let function = function_ref.clone();
let saved_values = std::mem::take(&mut self.values); let saved_values = std::mem::take(&mut self.values);
@ -143,7 +152,11 @@ impl VM {
self.current_function = Some(function.signature.name.clone()); self.current_function = Some(function.signature.name.clone());
if let Some(jm) = &mut self.jit_manager { if let Some(jm) = &mut self.jit_manager {
if let Ok(s) = std::env::var("NYASH_JIT_THRESHOLD") { if let Ok(s) = std::env::var("NYASH_JIT_THRESHOLD") {
if let Ok(t) = s.parse::<u32>() { if t > 0 { jm.set_threshold(t); } } if let Ok(t) = s.parse::<u32>() {
if t > 0 {
jm.set_threshold(t);
}
}
} }
jm.record_entry(&function.signature.name); jm.record_entry(&function.signature.name);
let _ = jm.maybe_compile(&function.signature.name, function); let _ = jm.maybe_compile(&function.signature.name, function);
@ -197,7 +210,10 @@ impl VM {
} else if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") } else if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1")
|| std::env::var("NYASH_JIT_TRAP_LOG").ok().as_deref() == Some("1") || std::env::var("NYASH_JIT_TRAP_LOG").ok().as_deref() == Some("1")
{ {
eprintln!("[JIT] fallback: VM path taken for {}", function.signature.name); eprintln!(
"[JIT] fallback: VM path taken for {}",
function.signature.name
);
if jit_only { if jit_only {
self.leave_root_region(); self.leave_root_region();
self.scope_tracker.pop_scope(); self.scope_tracker.pop_scope();
@ -285,9 +301,13 @@ impl VM {
if should_return.is_none() && next_block.is_none() { if should_return.is_none() && next_block.is_none() {
if let Some(term) = &block.terminator { if let Some(term) = &block.terminator {
match self.execute_instruction(term)? { match self.execute_instruction(term)? {
ControlFlow::Continue => {}, ControlFlow::Continue => {}
ControlFlow::Jump(target) => { next_block = Some(target); }, ControlFlow::Jump(target) => {
ControlFlow::Return(value) => { should_return = Some(value); }, next_block = Some(target);
}
ControlFlow::Return(value) => {
should_return = Some(value);
}
} }
} }
} }
@ -320,10 +340,16 @@ impl VM {
} }
/// Execute a single instruction /// Execute a single instruction
pub(super) fn execute_instruction(&mut self, instruction: &MirInstruction) -> Result<ControlFlow, VMError> { pub(super) fn execute_instruction(
&mut self,
instruction: &MirInstruction,
) -> Result<ControlFlow, VMError> {
let debug_global = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1"); let debug_global = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1");
let debug_exec = debug_global || std::env::var("NYASH_VM_DEBUG_EXEC").ok().as_deref() == Some("1"); let debug_exec =
if debug_exec { eprintln!("[VM] execute_instruction: {:?}", instruction); } debug_global || std::env::var("NYASH_VM_DEBUG_EXEC").ok().as_deref() == Some("1");
if debug_exec {
eprintln!("[VM] execute_instruction: {:?}", instruction);
}
self.record_instruction(instruction); self.record_instruction(instruction);
super::dispatch::execute_instruction(self, instruction, debug_global) super::dispatch::execute_instruction(self, instruction, debug_global)
} }

View File

@ -6,7 +6,7 @@
* - Debug prints for roots snapshot and shallow reachability * - Debug prints for roots snapshot and shallow reachability
*/ */
use super::vm::{VM, VMValue}; use super::vm::{VMValue, VM};
impl VM { impl VM {
/// Enter a GC root region and return a guard that leaves on drop /// Enter a GC root region and return a guard that leaves on drop
@ -22,11 +22,17 @@ impl VM {
} }
/// Leave current GC root region /// Leave current GC root region
pub(super) fn leave_root_region(&mut self) { self.scope_tracker.leave_root_region(); } pub(super) fn leave_root_region(&mut self) {
self.scope_tracker.leave_root_region();
}
/// Site info for GC logs: (func, bb, pc) /// Site info for GC logs: (func, bb, pc)
pub(super) fn gc_site_info(&self) -> (String, i64, i64) { pub(super) fn gc_site_info(&self) -> (String, i64, i64) {
let func = self.current_function.as_deref().unwrap_or("<none>").to_string(); let func = self
.current_function
.as_deref()
.unwrap_or("<none>")
.to_string();
let bb = self.frame.current_block.map(|b| b.0 as i64).unwrap_or(-1); let bb = self.frame.current_block.map(|b| b.0 as i64).unwrap_or(-1);
let pc = self.frame.pc as i64; let pc = self.frame.pc as i64;
(func, bb, pc) (func, bb, pc)
@ -78,7 +84,10 @@ impl VM {
} }
if let Some(map) = b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() { if let Some(map) = b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
let vals = map.values(); let vals = map.values();
if let Some(arr2) = vals.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() { if let Some(arr2) = vals
.as_any()
.downcast_ref::<crate::boxes::array::ArrayBox>()
{
if let Ok(items) = arr2.items.read() { if let Ok(items) = arr2.items.read() {
for item in items.iter() { for item in items.iter() {
let tn = item.type_name().to_string(); let tn = item.type_name().to_string();

File diff suppressed because it is too large Load Diff

View File

@ -1,41 +1,66 @@
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::box_trait::NyashBox; use crate::box_trait::NyashBox;
use crate::mir::ValueId; use crate::mir::ValueId;
use crate::backend::vm::ControlFlow;
use crate::backend::{VM, VMError, VMValue};
impl VM { impl VM {
/// Execute Call instruction (supports function name or FunctionBox value) /// Execute Call instruction (supports function name or FunctionBox value)
pub(crate) fn execute_call(&mut self, dst: Option<ValueId>, func: ValueId, args: &[ValueId]) -> Result<ControlFlow, VMError> { pub(crate) fn execute_call(
&mut self,
dst: Option<ValueId>,
func: ValueId,
args: &[ValueId],
) -> Result<ControlFlow, VMError> {
// Evaluate function value // Evaluate function value
let fval = self.get_value(func)?; let fval = self.get_value(func)?;
match fval { match fval {
VMValue::String(func_name) => { VMValue::String(func_name) => {
// Legacy: call function by name // Legacy: call function by name
let arg_values: Vec<VMValue> = args.iter().map(|arg| self.get_value(*arg)).collect::<Result<Vec<_>, _>>()?; let arg_values: Vec<VMValue> = args
.iter()
.map(|arg| self.get_value(*arg))
.collect::<Result<Vec<_>, _>>()?;
let result = self.call_function_by_name(&func_name, arg_values)?; let result = self.call_function_by_name(&func_name, arg_values)?;
if let Some(dst_id) = dst { self.set_value(dst_id, result); } if let Some(dst_id) = dst {
self.set_value(dst_id, result);
}
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
VMValue::BoxRef(arc_box) => { VMValue::BoxRef(arc_box) => {
// FunctionBox call path // FunctionBox call path
if let Some(fun) = arc_box.as_any().downcast_ref::<crate::boxes::function_box::FunctionBox>() { if let Some(fun) = arc_box
.as_any()
.downcast_ref::<crate::boxes::function_box::FunctionBox>()
{
// Convert args to NyashBox for interpreter helper // Convert args to NyashBox for interpreter helper
let nyash_args: Vec<Box<dyn NyashBox>> = args.iter() let nyash_args: Vec<Box<dyn NyashBox>> = args
.iter()
.map(|a| self.get_value(*a).map(|v| v.to_nyash_box())) .map(|a| self.get_value(*a).map(|v| v.to_nyash_box()))
.collect::<Result<Vec<_>, VMError>>()?; .collect::<Result<Vec<_>, VMError>>()?;
// Execute via interpreter helper // Execute via interpreter helper
match crate::interpreter::run_function_box(fun, nyash_args) { match crate::interpreter::run_function_box(fun, nyash_args) {
Ok(out) => { Ok(out) => {
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::from_nyash_box(out));
}
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
Err(e) => Err(VMError::InvalidInstruction(format!("FunctionBox call failed: {:?}", e))) Err(e) => Err(VMError::InvalidInstruction(format!(
"FunctionBox call failed: {:?}",
e
))),
} }
} else { } else {
Err(VMError::TypeError(format!("Call target not callable: {}", arc_box.type_name()))) Err(VMError::TypeError(format!(
"Call target not callable: {}",
arc_box.type_name()
)))
} }
} }
other => Err(VMError::TypeError(format!("Call target must be function name or FunctionBox, got {:?}", other))), other => Err(VMError::TypeError(format!(
"Call target must be function name or FunctionBox, got {:?}",
other
))),
} }
} }
} }

View File

@ -1,16 +1,27 @@
use crate::mir::{ConstValue, BinaryOp, CompareOp, UnaryOp, ValueId, BasicBlockId, TypeOpKind, MirType}; use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::box_trait::{BoolBox, VoidBox}; use crate::box_trait::{BoolBox, VoidBox};
use crate::boxes::ArrayBox; use crate::boxes::ArrayBox;
use crate::mir::{
BasicBlockId, BinaryOp, CompareOp, ConstValue, MirType, TypeOpKind, UnaryOp, ValueId,
};
use std::sync::Arc; use std::sync::Arc;
use crate::backend::vm::ControlFlow;
use crate::backend::{VM, VMError, VMValue};
impl VM { impl VM {
// ---- Helpers (PIC/VTable bookkeeping) ---- // ---- Helpers (PIC/VTable bookkeeping) ----
pub(super) fn build_pic_key(&self, recv: &VMValue, method: &str, method_id: Option<u16>) -> String { pub(super) fn build_pic_key(
&self,
recv: &VMValue,
method: &str,
method_id: Option<u16>,
) -> String {
let label = self.cache_label_for_recv(recv); let label = self.cache_label_for_recv(recv);
let ver = self.cache_version_for_label(&label); let ver = self.cache_version_for_label(&label);
if let Some(mid) = method_id { format!("v{}:{}#{}", ver, label, mid) } else { format!("v{}:{}#{}", ver, label, method) } if let Some(mid) = method_id {
format!("v{}:{}#{}", ver, label, mid)
} else {
format!("v{}:{}#{}", ver, label, method)
}
} }
pub(super) fn pic_record_hit(&mut self, key: &str) { pub(super) fn pic_record_hit(&mut self, key: &str) {
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
@ -19,23 +30,43 @@ impl VM {
let v = e.get_mut(); let v = e.get_mut();
*v = v.saturating_add(1); *v = v.saturating_add(1);
if std::env::var("NYASH_VM_PIC_DEBUG").ok().as_deref() == Some("1") { if std::env::var("NYASH_VM_PIC_DEBUG").ok().as_deref() == Some("1") {
if *v == 8 || *v == 32 { eprintln!("[PIC] Hot BoxCall site '{}' hits={} (skeleton)", key, v); } if *v == 8 || *v == 32 {
eprintln!("[PIC] Hot BoxCall site '{}' hits={} (skeleton)", key, v);
} }
} }
Entry::Vacant(v) => { v.insert(1); } }
Entry::Vacant(v) => {
v.insert(1);
} }
} }
pub(super) fn pic_hits(&self, key: &str) -> u32 { *self.boxcall_pic_hits.get(key).unwrap_or(&0) } }
pub(super) fn build_vtable_key(&self, class_name: &str, method_id: u16, arity: usize) -> String { pub(super) fn pic_hits(&self, key: &str) -> u32 {
*self.boxcall_pic_hits.get(key).unwrap_or(&0)
}
pub(super) fn build_vtable_key(
&self,
class_name: &str,
method_id: u16,
arity: usize,
) -> String {
let label = format!("BoxRef:{}", class_name); let label = format!("BoxRef:{}", class_name);
let ver = self.cache_version_for_label(&label); let ver = self.cache_version_for_label(&label);
format!("VT@v{}:{}#{}{}", ver, class_name, method_id, format!("/{}", arity)) format!(
"VT@v{}:{}#{}{}",
ver,
class_name,
method_id,
format!("/{}", arity)
)
} }
pub(super) fn try_poly_pic(&mut self, pic_site_key: &str, recv: &VMValue) -> Option<String> { pub(super) fn try_poly_pic(&mut self, pic_site_key: &str, recv: &VMValue) -> Option<String> {
let label = self.cache_label_for_recv(recv); let label = self.cache_label_for_recv(recv);
let ver = self.cache_version_for_label(&label); let ver = self.cache_version_for_label(&label);
if let Some(entries) = self.boxcall_poly_pic.get_mut(pic_site_key) { if let Some(entries) = self.boxcall_poly_pic.get_mut(pic_site_key) {
if let Some(idx) = entries.iter().position(|(l, v, _)| *l == label && *v == ver) { if let Some(idx) = entries
.iter()
.position(|(l, v, _)| *l == label && *v == ver)
{
let entry = entries.remove(idx); let entry = entries.remove(idx);
entries.push(entry.clone()); entries.push(entry.clone());
return Some(entry.2); return Some(entry.2);
@ -50,15 +81,28 @@ impl VM {
match self.boxcall_poly_pic.entry(pic_site_key.to_string()) { match self.boxcall_poly_pic.entry(pic_site_key.to_string()) {
Entry::Occupied(mut e) => { Entry::Occupied(mut e) => {
let v = e.get_mut(); let v = e.get_mut();
if let Some(idx) = v.iter().position(|(l, vv, _)| *l == label && *vv == ver) { v.remove(idx); } if let Some(idx) = v.iter().position(|(l, vv, _)| *l == label && *vv == ver) {
if v.len() >= 4 { v.remove(0); } v.remove(idx);
}
if v.len() >= 4 {
v.remove(0);
}
v.push((label.clone(), ver, func_name.to_string())); v.push((label.clone(), ver, func_name.to_string()));
} }
Entry::Vacant(v) => { v.insert(vec![(label.clone(), ver, func_name.to_string())]); } Entry::Vacant(v) => {
v.insert(vec![(label.clone(), ver, func_name.to_string())]);
}
} }
if crate::config::env::vm_pic_stats() { if crate::config::env::vm_pic_stats() {
if let Some(v) = self.boxcall_poly_pic.get(pic_site_key) { if let Some(v) = self.boxcall_poly_pic.get(pic_site_key) {
eprintln!("[PIC] site={} size={} last=({}, v{}) -> {}", pic_site_key, v.len(), label, ver, func_name); eprintln!(
"[PIC] site={} size={} last=({}, v{}) -> {}",
pic_site_key,
v.len(),
label,
ver,
func_name
);
} }
} }
} }
@ -73,25 +117,45 @@ impl VM {
VMValue::BoxRef(b) => format!("BoxRef:{}", b.type_name()), VMValue::BoxRef(b) => format!("BoxRef:{}", b.type_name()),
} }
} }
pub(super) fn cache_version_for_label(&self, label: &str) -> u32 { crate::runtime::cache_versions::get_version(label) } pub(super) fn cache_version_for_label(&self, label: &str) -> u32 {
crate::runtime::cache_versions::get_version(label)
}
#[allow(dead_code)] #[allow(dead_code)]
pub fn bump_cache_version(&mut self, label: &str) { crate::runtime::cache_versions::bump_version(label) } pub fn bump_cache_version(&mut self, label: &str) {
crate::runtime::cache_versions::bump_version(label)
}
// ---- Basics ---- // ---- Basics ----
pub(crate) fn execute_const(&mut self, dst: ValueId, value: &ConstValue) -> Result<ControlFlow, VMError> { pub(crate) fn execute_const(
&mut self,
dst: ValueId,
value: &ConstValue,
) -> Result<ControlFlow, VMError> {
let vm_value = VMValue::from(value); let vm_value = VMValue::from(value);
self.set_value(dst, vm_value); self.set_value(dst, vm_value);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
pub(crate) fn execute_binop(&mut self, dst: ValueId, op: &BinaryOp, lhs: ValueId, rhs: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_binop(
&mut self,
dst: ValueId,
op: &BinaryOp,
lhs: ValueId,
rhs: ValueId,
) -> Result<ControlFlow, VMError> {
match *op { match *op {
BinaryOp::And | BinaryOp::Or => { BinaryOp::And | BinaryOp::Or => {
if std::env::var("NYASH_VM_DEBUG_ANDOR").ok().as_deref() == Some("1") { eprintln!("[VM] And/Or short-circuit path"); } if std::env::var("NYASH_VM_DEBUG_ANDOR").ok().as_deref() == Some("1") {
eprintln!("[VM] And/Or short-circuit path");
}
let left = self.get_value(lhs)?; let left = self.get_value(lhs)?;
let right = self.get_value(rhs)?; let right = self.get_value(rhs)?;
let lb = left.as_bool()?; let lb = left.as_bool()?;
let rb = right.as_bool()?; let rb = right.as_bool()?;
let out = match *op { BinaryOp::And => lb && rb, BinaryOp::Or => lb || rb, _ => unreachable!() }; let out = match *op {
BinaryOp::And => lb && rb,
BinaryOp::Or => lb || rb,
_ => unreachable!(),
};
self.set_value(dst, VMValue::Bool(out)); self.set_value(dst, VMValue::Bool(out));
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
@ -104,29 +168,63 @@ impl VM {
} }
} }
} }
pub(crate) fn execute_unaryop(&mut self, dst: ValueId, op: &UnaryOp, operand: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_unaryop(
&mut self,
dst: ValueId,
op: &UnaryOp,
operand: ValueId,
) -> Result<ControlFlow, VMError> {
let operand_val = self.get_value(operand)?; let operand_val = self.get_value(operand)?;
let result = self.execute_unary_op(op, &operand_val)?; let result = self.execute_unary_op(op, &operand_val)?;
self.set_value(dst, result); self.set_value(dst, result);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
pub(crate) fn execute_compare(&mut self, dst: ValueId, op: &CompareOp, lhs: ValueId, rhs: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_compare(
let debug_cmp = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1") || std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1"); &mut self,
if debug_cmp { eprintln!("[VM] execute_compare enter op={:?} lhs={:?} rhs={:?}", op, lhs, rhs); } dst: ValueId,
op: &CompareOp,
lhs: ValueId,
rhs: ValueId,
) -> Result<ControlFlow, VMError> {
let debug_cmp = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1")
|| std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1");
if debug_cmp {
eprintln!(
"[VM] execute_compare enter op={:?} lhs={:?} rhs={:?}",
op, lhs, rhs
);
}
let mut left = self.get_value(lhs)?; let mut left = self.get_value(lhs)?;
let mut right = self.get_value(rhs)?; let mut right = self.get_value(rhs)?;
if debug_cmp { eprintln!("[VM] execute_compare values: left={:?} right={:?}", left, right); } if debug_cmp {
eprintln!(
"[VM] execute_compare values: left={:?} right={:?}",
left, right
);
}
left = match left { left = match left {
VMValue::BoxRef(b) => { VMValue::BoxRef(b) => {
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { VMValue::Integer(ib.value) } if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
else { match b.to_string_box().value.trim().parse::<i64>() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) } } VMValue::Integer(ib.value)
} else {
match b.to_string_box().value.trim().parse::<i64>() {
Ok(n) => VMValue::Integer(n),
Err(_) => VMValue::BoxRef(b),
}
}
} }
other => other, other => other,
}; };
right = match right { right = match right {
VMValue::BoxRef(b) => { VMValue::BoxRef(b) => {
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { VMValue::Integer(ib.value) } if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
else { match b.to_string_box().value.trim().parse::<i64>() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) } } VMValue::Integer(ib.value)
} else {
match b.to_string_box().value.trim().parse::<i64>() {
Ok(n) => VMValue::Integer(n),
Err(_) => VMValue::BoxRef(b),
}
}
} }
other => other, other => other,
}; };
@ -139,33 +237,70 @@ impl VM {
println!("{}", val.to_string()); println!("{}", val.to_string());
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
pub(crate) fn execute_jump(&self, target: BasicBlockId) -> Result<ControlFlow, VMError> { Ok(ControlFlow::Jump(target)) } pub(crate) fn execute_jump(&self, target: BasicBlockId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_branch(&self, condition: ValueId, then_bb: BasicBlockId, else_bb: BasicBlockId) -> Result<ControlFlow, VMError> { Ok(ControlFlow::Jump(target))
}
pub(crate) fn execute_branch(
&self,
condition: ValueId,
then_bb: BasicBlockId,
else_bb: BasicBlockId,
) -> Result<ControlFlow, VMError> {
let cond_val = self.get_value(condition)?; let cond_val = self.get_value(condition)?;
let should_branch = match &cond_val { let should_branch = match &cond_val {
VMValue::Bool(b) => *b, VMValue::Bool(b) => *b,
VMValue::Void => false, VMValue::Void => false,
VMValue::Integer(i) => *i != 0, VMValue::Integer(i) => *i != 0,
VMValue::BoxRef(b) => { VMValue::BoxRef(b) => {
if let Some(bool_box) = b.as_any().downcast_ref::<BoolBox>() { bool_box.value } if let Some(bool_box) = b.as_any().downcast_ref::<BoolBox>() {
else if b.as_any().downcast_ref::<VoidBox>().is_some() { false } bool_box.value
else { return Err(VMError::TypeError(format!("Branch condition must be bool, void, or integer, got BoxRef({})", b.type_name()))); } } else if b.as_any().downcast_ref::<VoidBox>().is_some() {
false
} else {
return Err(VMError::TypeError(format!(
"Branch condition must be bool, void, or integer, got BoxRef({})",
b.type_name()
)));
}
}
_ => {
return Err(VMError::TypeError(format!(
"Branch condition must be bool, void, or integer, got {:?}",
cond_val
)))
} }
_ => return Err(VMError::TypeError(format!("Branch condition must be bool, void, or integer, got {:?}", cond_val))),
}; };
Ok(ControlFlow::Jump(if should_branch { then_bb } else { else_bb })) Ok(ControlFlow::Jump(if should_branch {
then_bb
} else {
else_bb
}))
} }
pub(crate) fn execute_return(&self, value: Option<ValueId>) -> Result<ControlFlow, VMError> { pub(crate) fn execute_return(&self, value: Option<ValueId>) -> Result<ControlFlow, VMError> {
if let Some(val_id) = value { if let Some(val_id) = value {
let return_val = self.get_value(val_id)?; let return_val = self.get_value(val_id)?;
if crate::config::env::vm_vt_trace() { eprintln!("[VT] Return id={} val={}", val_id.to_usize(), return_val.to_string()); } if crate::config::env::vm_vt_trace() {
eprintln!(
"[VT] Return id={} val={}",
val_id.to_usize(),
return_val.to_string()
);
}
Ok(ControlFlow::Return(return_val)) Ok(ControlFlow::Return(return_val))
} else { } else {
if crate::config::env::vm_vt_trace() { eprintln!("[VT] Return void"); } if crate::config::env::vm_vt_trace() {
eprintln!("[VT] Return void");
}
Ok(ControlFlow::Return(VMValue::Void)) Ok(ControlFlow::Return(VMValue::Void))
} }
} }
pub(crate) fn execute_typeop(&mut self, dst: ValueId, op: &TypeOpKind, value: ValueId, ty: &MirType) -> Result<ControlFlow, VMError> { pub(crate) fn execute_typeop(
&mut self,
dst: ValueId,
op: &TypeOpKind,
value: ValueId,
ty: &MirType,
) -> Result<ControlFlow, VMError> {
let val = self.get_value(value)?; let val = self.get_value(value)?;
match op { match op {
TypeOpKind::Check => { TypeOpKind::Check => {
@ -175,7 +310,9 @@ impl VM {
(VMValue::Bool(_), MirType::Bool) => true, (VMValue::Bool(_), MirType::Bool) => true,
(VMValue::String(_), MirType::String) => true, (VMValue::String(_), MirType::String) => true,
(VMValue::Void, MirType::Void) => true, (VMValue::Void, MirType::Void) => true,
(VMValue::BoxRef(arc_box), MirType::Box(box_name)) => arc_box.type_name() == box_name, (VMValue::BoxRef(arc_box), MirType::Box(box_name)) => {
arc_box.type_name() == box_name
}
_ => false, _ => false,
}; };
self.set_value(dst, VMValue::Bool(is_type)); self.set_value(dst, VMValue::Bool(is_type));
@ -185,34 +322,65 @@ impl VM {
let result = match (&val, ty) { let result = match (&val, ty) {
(VMValue::Integer(i), MirType::Float) => VMValue::Float(*i as f64), (VMValue::Integer(i), MirType::Float) => VMValue::Float(*i as f64),
(VMValue::Float(f), MirType::Integer) => VMValue::Integer(*f as i64), (VMValue::Float(f), MirType::Integer) => VMValue::Integer(*f as i64),
(VMValue::Integer(_), MirType::Integer) | (VMValue::Float(_), MirType::Float) | (VMValue::Bool(_), MirType::Bool) | (VMValue::String(_), MirType::String) => val.clone(), (VMValue::Integer(_), MirType::Integer)
(VMValue::BoxRef(arc_box), MirType::Box(box_name)) if arc_box.type_name() == box_name => val.clone(), | (VMValue::Float(_), MirType::Float)
_ => { return Err(VMError::TypeError(format!("Cannot cast {:?} to {:?}", val, ty))); } | (VMValue::Bool(_), MirType::Bool)
| (VMValue::String(_), MirType::String) => val.clone(),
(VMValue::BoxRef(arc_box), MirType::Box(box_name))
if arc_box.type_name() == box_name =>
{
val.clone()
}
_ => {
return Err(VMError::TypeError(format!(
"Cannot cast {:?} to {:?}",
val, ty
)));
}
}; };
self.set_value(dst, result); self.set_value(dst, result);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
} }
} }
pub(crate) fn execute_phi(&mut self, dst: ValueId, inputs: &[(BasicBlockId, ValueId)]) -> Result<ControlFlow, VMError> { pub(crate) fn execute_phi(
&mut self,
dst: ValueId,
inputs: &[(BasicBlockId, ValueId)],
) -> Result<ControlFlow, VMError> {
let selected = self.loop_execute_phi(dst, inputs)?; let selected = self.loop_execute_phi(dst, inputs)?;
self.set_value(dst, selected); self.set_value(dst, selected);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
pub(crate) fn execute_load(&mut self, dst: ValueId, ptr: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_load(
&mut self,
dst: ValueId,
ptr: ValueId,
) -> Result<ControlFlow, VMError> {
let loaded_value = self.get_value(ptr)?; let loaded_value = self.get_value(ptr)?;
self.set_value(dst, loaded_value); self.set_value(dst, loaded_value);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
pub(crate) fn execute_store(&mut self, value: ValueId, ptr: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_store(
&mut self,
value: ValueId,
ptr: ValueId,
) -> Result<ControlFlow, VMError> {
let val = self.get_value(value)?; let val = self.get_value(value)?;
self.set_value(ptr, val); self.set_value(ptr, val);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
pub(crate) fn execute_copy(&mut self, dst: ValueId, src: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_copy(
&mut self,
dst: ValueId,
src: ValueId,
) -> Result<ControlFlow, VMError> {
let value = self.get_value(src)?; let value = self.get_value(src)?;
let cloned = match &value { let cloned = match &value {
VMValue::BoxRef(arc_box) => { let cloned_box = arc_box.clone_or_share(); VMValue::BoxRef(Arc::from(cloned_box)) } VMValue::BoxRef(arc_box) => {
let cloned_box = arc_box.clone_or_share();
VMValue::BoxRef(Arc::from(cloned_box))
}
other => other.clone(), other => other.clone(),
}; };
self.set_value(dst, cloned); self.set_value(dst, cloned);
@ -220,7 +388,12 @@ impl VM {
} }
// ---- Arrays ---- // ---- Arrays ----
pub(crate) fn execute_array_get(&mut self, dst: ValueId, array: ValueId, index: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_array_get(
&mut self,
dst: ValueId,
array: ValueId,
index: ValueId,
) -> Result<ControlFlow, VMError> {
let array_val = self.get_value(array)?; let array_val = self.get_value(array)?;
let index_val = self.get_value(index)?; let index_val = self.get_value(index)?;
if let VMValue::BoxRef(array_box) = &array_val { if let VMValue::BoxRef(array_box) = &array_val {
@ -229,10 +402,23 @@ impl VM {
let result = array.get(index_box); let result = array.get(index_box);
self.set_value(dst, VMValue::BoxRef(Arc::from(result))); self.set_value(dst, VMValue::BoxRef(Arc::from(result)));
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} else { Err(VMError::TypeError("ArrayGet requires an ArrayBox".to_string())) } } else {
} else { Err(VMError::TypeError("ArrayGet requires array and integer index".to_string())) } Err(VMError::TypeError(
"ArrayGet requires an ArrayBox".to_string(),
))
} }
pub(crate) fn execute_array_set(&mut self, array: ValueId, index: ValueId, value: ValueId) -> Result<ControlFlow, VMError> { } else {
Err(VMError::TypeError(
"ArrayGet requires array and integer index".to_string(),
))
}
}
pub(crate) fn execute_array_set(
&mut self,
array: ValueId,
index: ValueId,
value: ValueId,
) -> Result<ControlFlow, VMError> {
let array_val = self.get_value(array)?; let array_val = self.get_value(array)?;
let index_val = self.get_value(index)?; let index_val = self.get_value(index)?;
let value_val = self.get_value(value)?; let value_val = self.get_value(value)?;
@ -243,27 +429,50 @@ impl VM {
let box_value = value_val.to_nyash_box(); let box_value = value_val.to_nyash_box();
array.set(index_box, box_value); array.set(index_box, box_value);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} else { Err(VMError::TypeError("ArraySet requires an ArrayBox".to_string())) } } else {
} else { Err(VMError::TypeError("ArraySet requires array and integer index".to_string())) } Err(VMError::TypeError(
"ArraySet requires an ArrayBox".to_string(),
))
}
} else {
Err(VMError::TypeError(
"ArraySet requires array and integer index".to_string(),
))
}
} }
// ---- Refs/Weak/Barriers ---- // ---- Refs/Weak/Barriers ----
pub(crate) fn execute_ref_new(&mut self, dst: ValueId, box_val: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_ref_new(
&mut self,
dst: ValueId,
box_val: ValueId,
) -> Result<ControlFlow, VMError> {
let box_value = self.get_value(box_val)?; let box_value = self.get_value(box_val)?;
self.set_value(dst, box_value); self.set_value(dst, box_value);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
pub(crate) fn execute_ref_get(&mut self, dst: ValueId, reference: ValueId, field: &str) -> Result<ControlFlow, VMError> { pub(crate) fn execute_ref_get(
&mut self,
dst: ValueId,
reference: ValueId,
field: &str,
) -> Result<ControlFlow, VMError> {
let debug_ref = std::env::var("NYASH_VM_DEBUG_REF").ok().as_deref() == Some("1"); let debug_ref = std::env::var("NYASH_VM_DEBUG_REF").ok().as_deref() == Some("1");
if debug_ref { eprintln!("[VM] RefGet ref={:?} field={}", reference, field); } if debug_ref {
eprintln!("[VM] RefGet ref={:?} field={}", reference, field);
}
let is_internal = self.object_internal.contains(&reference); let is_internal = self.object_internal.contains(&reference);
if !is_internal { if !is_internal {
if let Some(class_name) = self.object_class.get(&reference) { if let Some(class_name) = self.object_class.get(&reference) {
if let Ok(decls) = self.runtime.box_declarations.read() { if let Ok(decls) = self.runtime.box_declarations.read() {
if let Some(decl) = decls.get(class_name) { if let Some(decl) = decls.get(class_name) {
let has_vis = !decl.public_fields.is_empty() || !decl.private_fields.is_empty(); let has_vis =
!decl.public_fields.is_empty() || !decl.private_fields.is_empty();
if has_vis && !decl.public_fields.iter().any(|f| f == field) { if has_vis && !decl.public_fields.iter().any(|f| f == field) {
return Err(VMError::TypeError(format!("Field '{}' is private in {}", field, class_name))); return Err(VMError::TypeError(format!(
"Field '{}' is private in {}",
field, class_name
)));
} }
} }
} }
@ -271,78 +480,140 @@ impl VM {
} }
let mut field_value = if let Some(fields) = self.object_fields.get(&reference) { let mut field_value = if let Some(fields) = self.object_fields.get(&reference) {
if let Some(value) = fields.get(field) { if let Some(value) = fields.get(field) {
if debug_ref { eprintln!("[VM] RefGet hit: {} -> {:?}", field, value); } if debug_ref {
eprintln!("[VM] RefGet hit: {} -> {:?}", field, value);
}
value.clone() value.clone()
} else { } else {
if debug_ref { eprintln!("[VM] RefGet miss: {} -> default 0", field); } if debug_ref {
eprintln!("[VM] RefGet miss: {} -> default 0", field);
}
VMValue::Integer(0) VMValue::Integer(0)
} }
} else { } else {
if debug_ref { eprintln!("[VM] RefGet no fields: -> default 0"); } if debug_ref {
eprintln!("[VM] RefGet no fields: -> default 0");
}
VMValue::Integer(0) VMValue::Integer(0)
}; };
if matches!(field_value, VMValue::Integer(0)) && field == "console" { if matches!(field_value, VMValue::Integer(0)) && field == "console" {
if debug_ref { eprintln!("[VM] RefGet special binding: console -> Plugin ConsoleBox"); } if debug_ref {
eprintln!("[VM] RefGet special binding: console -> Plugin ConsoleBox");
}
let host = crate::runtime::get_global_plugin_host(); let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap(); let host = host.read().unwrap();
if let Ok(pbox) = host.create_box("ConsoleBox", &[]) { if let Ok(pbox) = host.create_box("ConsoleBox", &[]) {
field_value = VMValue::from_nyash_box(pbox); field_value = VMValue::from_nyash_box(pbox);
if !self.object_fields.contains_key(&reference) { self.object_fields.insert(reference, std::collections::HashMap::new()); } if !self.object_fields.contains_key(&reference) {
if let Some(fields) = self.object_fields.get_mut(&reference) { fields.insert(field.to_string(), field_value.clone()); } self.object_fields
.insert(reference, std::collections::HashMap::new());
}
if let Some(fields) = self.object_fields.get_mut(&reference) {
fields.insert(field.to_string(), field_value.clone());
}
} }
} }
self.set_value(dst, field_value); self.set_value(dst, field_value);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
pub(crate) fn execute_ref_set(&mut self, reference: ValueId, field: &str, value: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_ref_set(
&mut self,
reference: ValueId,
field: &str,
value: ValueId,
) -> Result<ControlFlow, VMError> {
let debug_ref = std::env::var("NYASH_VM_DEBUG_REF").ok().as_deref() == Some("1"); let debug_ref = std::env::var("NYASH_VM_DEBUG_REF").ok().as_deref() == Some("1");
let new_value = self.get_value(value)?; let new_value = self.get_value(value)?;
if debug_ref { eprintln!("[VM] RefSet ref={:?} field={} value={:?}", reference, field, new_value); } if debug_ref {
eprintln!(
"[VM] RefSet ref={:?} field={} value={:?}",
reference, field, new_value
);
}
let is_internal = self.object_internal.contains(&reference); let is_internal = self.object_internal.contains(&reference);
if !is_internal { if !is_internal {
if let Some(class_name) = self.object_class.get(&reference) { if let Some(class_name) = self.object_class.get(&reference) {
if let Ok(decls) = self.runtime.box_declarations.read() { if let Ok(decls) = self.runtime.box_declarations.read() {
if let Some(decl) = decls.get(class_name) { if let Some(decl) = decls.get(class_name) {
let has_vis = !decl.public_fields.is_empty() || !decl.private_fields.is_empty(); let has_vis =
!decl.public_fields.is_empty() || !decl.private_fields.is_empty();
if has_vis && !decl.public_fields.iter().any(|f| f == field) { if has_vis && !decl.public_fields.iter().any(|f| f == field) {
return Err(VMError::TypeError(format!("Field '{}' is private in {}", field, class_name))); return Err(VMError::TypeError(format!(
"Field '{}' is private in {}",
field, class_name
)));
} }
} }
} }
} }
} }
if !self.object_fields.contains_key(&reference) { self.object_fields.insert(reference, std::collections::HashMap::new()); } if !self.object_fields.contains_key(&reference) {
self.object_fields
.insert(reference, std::collections::HashMap::new());
}
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "RefSet"); crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "RefSet");
if let Some(fields) = self.object_fields.get_mut(&reference) { fields.insert(field.to_string(), new_value); if debug_ref { eprintln!("[VM] RefSet stored: {}", field); } } if let Some(fields) = self.object_fields.get_mut(&reference) {
fields.insert(field.to_string(), new_value);
if debug_ref {
eprintln!("[VM] RefSet stored: {}", field);
}
}
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
pub(crate) fn execute_weak_new(&mut self, dst: ValueId, box_val: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_weak_new(
&mut self,
dst: ValueId,
box_val: ValueId,
) -> Result<ControlFlow, VMError> {
let box_value = self.get_value(box_val)?; let box_value = self.get_value(box_val)?;
self.set_value(dst, box_value); self.set_value(dst, box_value);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
pub(crate) fn execute_weak_load(&mut self, dst: ValueId, weak_ref: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_weak_load(
&mut self,
dst: ValueId,
weak_ref: ValueId,
) -> Result<ControlFlow, VMError> {
let weak_value = self.get_value(weak_ref)?; let weak_value = self.get_value(weak_ref)?;
self.set_value(dst, weak_value); self.set_value(dst, weak_value);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
pub(crate) fn execute_barrier_read(&mut self, dst: ValueId, value: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_barrier_read(
&mut self,
dst: ValueId,
value: ValueId,
) -> Result<ControlFlow, VMError> {
let val = self.get_value(value)?; let val = self.get_value(value)?;
self.set_value(dst, val); self.set_value(dst, val);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
pub(crate) fn execute_barrier_write(&mut self, _value: ValueId) -> Result<ControlFlow, VMError> { Ok(ControlFlow::Continue) } pub(crate) fn execute_barrier_write(
&mut self,
_value: ValueId,
) -> Result<ControlFlow, VMError> {
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_throw(&mut self, exception: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_throw(&mut self, exception: ValueId) -> Result<ControlFlow, VMError> {
let exc_value = self.get_value(exception)?; let exc_value = self.get_value(exception)?;
Err(VMError::InvalidInstruction(format!("Exception thrown: {:?}", exc_value))) Err(VMError::InvalidInstruction(format!(
"Exception thrown: {:?}",
exc_value
)))
} }
pub(crate) fn execute_catch(&mut self, exception_value: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_catch(
&mut self,
exception_value: ValueId,
) -> Result<ControlFlow, VMError> {
self.set_value(exception_value, VMValue::Void); self.set_value(exception_value, VMValue::Void);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }
// ---- Futures ---- // ---- Futures ----
pub(crate) fn execute_await(&mut self, dst: ValueId, future: ValueId) -> Result<ControlFlow, VMError> { pub(crate) fn execute_await(
&mut self,
dst: ValueId,
future: ValueId,
) -> Result<ControlFlow, VMError> {
let future_val = self.get_value(future)?; let future_val = self.get_value(future)?;
if let VMValue::Future(ref future_box) = future_val { if let VMValue::Future(ref future_box) = future_val {
let max_ms: u64 = crate::config::env::await_max_ms(); let max_ms: u64 = crate::config::env::await_max_ms();
@ -350,10 +621,14 @@ impl VM {
let mut spins = 0usize; let mut spins = 0usize;
while !future_box.ready() { while !future_box.ready() {
self.runtime.gc.safepoint(); self.runtime.gc.safepoint();
if let Some(s) = &self.runtime.scheduler { s.poll(); } if let Some(s) = &self.runtime.scheduler {
s.poll();
}
std::thread::yield_now(); std::thread::yield_now();
spins += 1; spins += 1;
if spins % 1024 == 0 { std::thread::sleep(std::time::Duration::from_millis(1)); } if spins % 1024 == 0 {
std::thread::sleep(std::time::Duration::from_millis(1));
}
if start.elapsed() >= std::time::Duration::from_millis(max_ms) { if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
let err = Box::new(crate::box_trait::StringBox::new("Timeout")); let err = Box::new(crate::box_trait::StringBox::new("Timeout"));
let rb = crate::boxes::result::NyashResultBox::new_err(err); let rb = crate::boxes::result::NyashResultBox::new_err(err);
@ -367,6 +642,11 @@ impl VM {
let vm_value = VMValue::from_nyash_box(Box::new(ok)); let vm_value = VMValue::from_nyash_box(Box::new(ok));
self.set_value(dst, vm_value); self.set_value(dst, vm_value);
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} else { Err(VMError::TypeError(format!("Expected Future, got {:?}", future_val))) } } else {
Err(VMError::TypeError(format!(
"Expected Future, got {:?}",
future_val
)))
}
} }
} }

View File

@ -1,33 +1,60 @@
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::box_trait::NyashBox; use crate::box_trait::NyashBox;
use crate::mir::ValueId; use crate::mir::ValueId;
use crate::backend::vm::ControlFlow;
use crate::backend::{VM, VMError, VMValue};
impl VM { impl VM {
/// Execute ExternCall instruction /// Execute ExternCall instruction
pub(crate) fn execute_extern_call(&mut self, dst: Option<ValueId>, iface_name: &str, method_name: &str, args: &[ValueId]) -> Result<ControlFlow, VMError> { pub(crate) fn execute_extern_call(
&mut self,
dst: Option<ValueId>,
iface_name: &str,
method_name: &str,
args: &[ValueId],
) -> Result<ControlFlow, VMError> {
// Core-13 pure shims: env.local.{get,set}, env.box.new // Core-13 pure shims: env.local.{get,set}, env.box.new
match (iface_name, method_name) { match (iface_name, method_name) {
("env.local", "get") => { ("env.local", "get") => {
if args.len() != 1 { return Err(VMError::InvalidInstruction("env.local.get arity".into())); } if args.len() != 1 {
return Err(VMError::InvalidInstruction("env.local.get arity".into()));
}
let ptr = args[0]; let ptr = args[0];
let v = self.get_value(ptr).unwrap_or(crate::backend::vm::VMValue::Void); let v = self
if let Some(d) = dst { self.set_value(d, v); } .get_value(ptr)
.unwrap_or(crate::backend::vm::VMValue::Void);
if let Some(d) = dst {
self.set_value(d, v);
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
("env.local", "set") => { ("env.local", "set") => {
if args.len() != 2 { return Err(VMError::InvalidInstruction("env.local.set arity".into())); } if args.len() != 2 {
return Err(VMError::InvalidInstruction("env.local.set arity".into()));
}
let ptr = args[0]; let ptr = args[0];
let val = self.get_value(args[1])?; let val = self.get_value(args[1])?;
self.set_value(ptr, val); self.set_value(ptr, val);
if let Some(d) = dst { self.set_value(d, crate::backend::vm::VMValue::Void); } if let Some(d) = dst {
self.set_value(d, crate::backend::vm::VMValue::Void);
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
("env.box", "new") => { ("env.box", "new") => {
if args.is_empty() { return Err(VMError::InvalidInstruction("env.box.new requires type name".into())); } if args.is_empty() {
return Err(VMError::InvalidInstruction(
"env.box.new requires type name".into(),
));
}
// first arg must be Const String type name // first arg must be Const String type name
let ty = self.get_value(args[0])?; let ty = self.get_value(args[0])?;
let ty_name = match ty { crate::backend::vm::VMValue::String(s) => s, _ => return Err(VMError::InvalidInstruction("env.box.new first arg must be string".into())) }; let ty_name = match ty {
crate::backend::vm::VMValue::String(s) => s,
_ => {
return Err(VMError::InvalidInstruction(
"env.box.new first arg must be string".into(),
))
}
};
// remaining args as NyashBox // remaining args as NyashBox
let mut ny_args: Vec<Box<dyn NyashBox>> = Vec::new(); let mut ny_args: Vec<Box<dyn NyashBox>> = Vec::new();
for id in args.iter().skip(1) { for id in args.iter().skip(1) {
@ -36,8 +63,17 @@ impl VM {
} }
let reg = crate::runtime::box_registry::get_global_registry(); let reg = crate::runtime::box_registry::get_global_registry();
match reg.create_box(&ty_name, &ny_args) { match reg.create_box(&ty_name, &ny_args) {
Ok(b) => { if let Some(d) = dst { self.set_value(d, crate::backend::vm::VMValue::from_nyash_box(b)); } } Ok(b) => {
Err(e) => { return Err(VMError::InvalidInstruction(format!("env.box.new failed for {}: {}", ty_name, e))); } if let Some(d) = dst {
self.set_value(d, crate::backend::vm::VMValue::from_nyash_box(b));
}
}
Err(e) => {
return Err(VMError::InvalidInstruction(format!(
"env.box.new failed for {}: {}",
ty_name, e
)));
}
} }
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
@ -45,41 +81,90 @@ impl VM {
} }
// Optional routing to name→slot handlers for stability and diagnostics // Optional routing to name→slot handlers for stability and diagnostics
if crate::config::env::extern_route_slots() { if crate::config::env::extern_route_slots() {
if let Some(slot) = crate::runtime::extern_registry::resolve_slot(iface_name, method_name) { if let Some(slot) =
crate::runtime::extern_registry::resolve_slot(iface_name, method_name)
{
// Decode args to VMValue as needed by handlers below // Decode args to VMValue as needed by handlers below
let vm_args: Vec<VMValue> = args.iter().filter_map(|a| self.get_value(*a).ok()).collect(); let vm_args: Vec<VMValue> = args
.iter()
.filter_map(|a| self.get_value(*a).ok())
.collect();
match (iface_name, method_name, slot) { match (iface_name, method_name, slot) {
("env.local", "get", 40) => { ("env.local", "get", 40) => {
if let Some(d) = dst { if let Some(a0) = args.get(0) { let v = self.get_value(*a0).unwrap_or(VMValue::Void); self.set_value(d, v); } } if let Some(d) = dst {
if let Some(a0) = args.get(0) {
let v = self.get_value(*a0).unwrap_or(VMValue::Void);
self.set_value(d, v);
}
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
("env.local", "set", 41) => { ("env.local", "set", 41) => {
if args.len() >= 2 { let ptr = args[0]; let val = vm_args.get(1).cloned().unwrap_or(VMValue::Void); self.set_value(ptr, val); } if args.len() >= 2 {
if let Some(d) = dst { self.set_value(d, VMValue::Void); } let ptr = args[0];
let val = vm_args.get(1).cloned().unwrap_or(VMValue::Void);
self.set_value(ptr, val);
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
("env.box", "new", 50) => { ("env.box", "new", 50) => {
if vm_args.is_empty() { return Err(VMError::InvalidInstruction("env.box.new requires type".into())); } if vm_args.is_empty() {
let ty = &vm_args[0]; let ty_name = match ty { VMValue::String(s) => s.clone(), _ => return Err(VMError::InvalidInstruction("env.box.new first arg must be string".into())) }; return Err(VMError::InvalidInstruction(
"env.box.new requires type".into(),
));
}
let ty = &vm_args[0];
let ty_name = match ty {
VMValue::String(s) => s.clone(),
_ => {
return Err(VMError::InvalidInstruction(
"env.box.new first arg must be string".into(),
))
}
};
let mut ny_args: Vec<Box<dyn NyashBox>> = Vec::new(); let mut ny_args: Vec<Box<dyn NyashBox>> = Vec::new();
for v in vm_args.iter().skip(1) { ny_args.push(v.to_nyash_box()); } for v in vm_args.iter().skip(1) {
ny_args.push(v.to_nyash_box());
}
let reg = crate::runtime::box_registry::get_global_registry(); let reg = crate::runtime::box_registry::get_global_registry();
match reg.create_box(&ty_name, &ny_args) { match reg.create_box(&ty_name, &ny_args) {
Ok(b) => { if let Some(d) = dst { self.set_value(d, VMValue::from_nyash_box(b)); } } Ok(b) => {
Err(e) => { return Err(VMError::InvalidInstruction(format!("env.box.new failed for {}: {}", ty_name, e))); } if let Some(d) = dst {
self.set_value(d, VMValue::from_nyash_box(b));
}
}
Err(e) => {
return Err(VMError::InvalidInstruction(format!(
"env.box.new failed for {}: {}",
ty_name, e
)));
}
} }
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
("env.console", m @ ("log" | "warn" | "error" | "println"), 10) => { ("env.console", m @ ("log" | "warn" | "error" | "println"), 10) => {
if let Some(a0) = vm_args.get(0) { if let Some(a0) = vm_args.get(0) {
match m { "warn" => eprintln!("[warn] {}", a0.to_string()), "error" => eprintln!("[error] {}", a0.to_string()), _ => println!("{}", a0.to_string()), } match m {
"warn" => eprintln!("[warn] {}", a0.to_string()),
"error" => eprintln!("[error] {}", a0.to_string()),
_ => println!("{}", a0.to_string()),
}
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
} }
if let Some(d) = dst { self.set_value(d, VMValue::Void); }
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
("env.debug", "trace", 11) => { ("env.debug", "trace", 11) => {
if let Some(a0) = vm_args.get(0) { eprintln!("[trace] {}", a0.to_string()); } if let Some(a0) = vm_args.get(0) {
if let Some(d) = dst { self.set_value(d, VMValue::Void); } eprintln!("[trace] {}", a0.to_string());
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
("env.runtime", "checkpoint", 12) => { ("env.runtime", "checkpoint", 12) => {
@ -88,21 +173,35 @@ impl VM {
eprintln!("[rt] checkpoint @{} bb={} pc={}", func, bb, pc); eprintln!("[rt] checkpoint @{} bb={} pc={}", func, bb, pc);
} }
self.runtime.gc.safepoint(); self.runtime.gc.safepoint();
if let Some(s) = &self.runtime.scheduler { s.poll(); } if let Some(s) = &self.runtime.scheduler {
if let Some(d) = dst { self.set_value(d, VMValue::Void); } s.poll();
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
("env.future", "new", 20) | ("env.future", "birth", 20) => { ("env.future", "new", 20) | ("env.future", "birth", 20) => {
// Create a new Future and optionally set initial value from arg0 // Create a new Future and optionally set initial value from arg0
let fut = crate::boxes::future::FutureBox::new(); let fut = crate::boxes::future::FutureBox::new();
if let Some(a0) = vm_args.get(0) { fut.set_result(a0.to_nyash_box()); } if let Some(a0) = vm_args.get(0) {
if let Some(d) = dst { self.set_value(d, VMValue::Future(fut)); } fut.set_result(a0.to_nyash_box());
}
if let Some(d) = dst {
self.set_value(d, VMValue::Future(fut));
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
("env.future", "set", 21) => { ("env.future", "set", 21) => {
// set(future, value) // set(future, value)
if vm_args.len() >= 2 { if let VMValue::Future(f) = &vm_args[0] { f.set_result(vm_args[1].to_nyash_box()); } } if vm_args.len() >= 2 {
if let Some(d) = dst { self.set_value(d, VMValue::Void); } if let VMValue::Future(f) = &vm_args[0] {
f.set_result(vm_args[1].to_nyash_box());
}
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
("env.future", "await", 22) => { ("env.future", "await", 22) => {
@ -112,21 +211,57 @@ impl VM {
let max_ms = crate::config::env::await_max_ms(); let max_ms = crate::config::env::await_max_ms();
while !fb.ready() { while !fb.ready() {
std::thread::yield_now(); std::thread::yield_now();
if start.elapsed() >= std::time::Duration::from_millis(max_ms) { break; } if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
break;
} }
let result = if fb.ready() { fb.get() } else { Box::new(crate::box_trait::StringBox::new("Timeout")) }; }
let result = if fb.ready() {
fb.get()
} else {
Box::new(crate::box_trait::StringBox::new("Timeout"))
};
let ok = crate::boxes::result::NyashResultBox::new_ok(result); let ok = crate::boxes::result::NyashResultBox::new_ok(result);
if let Some(d) = dst { self.set_value(d, VMValue::from_nyash_box(Box::new(ok))); } if let Some(d) = dst {
} else if let Some(d) = dst { self.set_value(d, VMValue::Void); } self.set_value(d, VMValue::from_nyash_box(Box::new(ok)));
}
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.task", "cancelCurrent", 30) => {
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.task", "currentToken", 31) => {
if let Some(d) = dst {
self.set_value(d, VMValue::Integer(0));
}
return Ok(ControlFlow::Continue);
}
("env.task", "yieldNow", 32) => {
std::thread::yield_now();
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
("env.task", "cancelCurrent", 30) => { if let Some(d) = dst { self.set_value(d, VMValue::Void); } return Ok(ControlFlow::Continue); }
("env.task", "currentToken", 31) => { if let Some(d) = dst { self.set_value(d, VMValue::Integer(0)); } return Ok(ControlFlow::Continue); }
("env.task", "yieldNow", 32) => { std::thread::yield_now(); if let Some(d) = dst { self.set_value(d, VMValue::Void); } return Ok(ControlFlow::Continue); }
("env.task", "sleepMs", 33) => { ("env.task", "sleepMs", 33) => {
let ms = vm_args.get(0).map(|v| match v { VMValue::Integer(i) => *i, _ => 0 }).unwrap_or(0); let ms = vm_args
if ms > 0 { std::thread::sleep(std::time::Duration::from_millis(ms as u64)); } .get(0)
if let Some(d) = dst { self.set_value(d, VMValue::Void); } .map(|v| match v {
VMValue::Integer(i) => *i,
_ => 0,
})
.unwrap_or(0);
if ms > 0 {
std::thread::sleep(std::time::Duration::from_millis(ms as u64));
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
_ => { /* fallthrough to host */ } _ => { /* fallthrough to host */ }
@ -137,24 +272,38 @@ impl VM {
match (iface_name, method_name) { match (iface_name, method_name) {
("env.modules", "set") => { ("env.modules", "set") => {
// Expect two args // Expect two args
let vm_args: Vec<VMValue> = args.iter().filter_map(|a| self.get_value(*a).ok()).collect(); let vm_args: Vec<VMValue> = args
.iter()
.filter_map(|a| self.get_value(*a).ok())
.collect();
if vm_args.len() >= 2 { if vm_args.len() >= 2 {
let key = vm_args[0].to_string(); let key = vm_args[0].to_string();
let val_box = vm_args[1].to_nyash_box(); let val_box = vm_args[1].to_nyash_box();
crate::runtime::modules_registry::set(key, val_box); crate::runtime::modules_registry::set(key, val_box);
} }
if let Some(d) = dst { self.set_value(d, VMValue::Void); } if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
("env.modules", "get") => { ("env.modules", "get") => {
let vm_args: Vec<VMValue> = args.iter().filter_map(|a| self.get_value(*a).ok()).collect(); let vm_args: Vec<VMValue> = args
.iter()
.filter_map(|a| self.get_value(*a).ok())
.collect();
if let Some(k) = vm_args.get(0) { if let Some(k) = vm_args.get(0) {
let key = k.to_string(); let key = k.to_string();
if let Some(v) = crate::runtime::modules_registry::get(&key) { if let Some(v) = crate::runtime::modules_registry::get(&key) {
if let Some(d) = dst { self.set_value(d, VMValue::from_nyash_box(v)); } if let Some(d) = dst {
else { /* no dst */ } self.set_value(d, VMValue::from_nyash_box(v));
} else if let Some(d) = dst { self.set_value(d, VMValue::Void); } } else { /* no dst */
} else if let Some(d) = dst { self.set_value(d, VMValue::Void); } }
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
_ => {} _ => {}
@ -164,7 +313,10 @@ impl VM {
// Name-route minimal registry even when slot routing is disabled // Name-route minimal registry even when slot routing is disabled
if iface_name == "env.modules" { if iface_name == "env.modules" {
// Decode args as VMValue for convenience // Decode args as VMValue for convenience
let vm_args: Vec<VMValue> = args.iter().filter_map(|a| self.get_value(*a).ok()).collect(); let vm_args: Vec<VMValue> = args
.iter()
.filter_map(|a| self.get_value(*a).ok())
.collect();
match method_name { match method_name {
"set" => { "set" => {
if vm_args.len() >= 2 { if vm_args.len() >= 2 {
@ -172,16 +324,24 @@ impl VM {
let val_box = vm_args[1].to_nyash_box(); let val_box = vm_args[1].to_nyash_box();
crate::runtime::modules_registry::set(key, val_box); crate::runtime::modules_registry::set(key, val_box);
} }
if let Some(d) = dst { self.set_value(d, VMValue::Void); } if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
"get" => { "get" => {
if let Some(k) = vm_args.get(0) { if let Some(k) = vm_args.get(0) {
let key = k.to_string(); let key = k.to_string();
if let Some(v) = crate::runtime::modules_registry::get(&key) { if let Some(v) = crate::runtime::modules_registry::get(&key) {
if let Some(d) = dst { self.set_value(d, VMValue::from_nyash_box(v)); } if let Some(d) = dst {
} else if let Some(d) = dst { self.set_value(d, VMValue::Void); } self.set_value(d, VMValue::from_nyash_box(v));
} else if let Some(d) = dst { self.set_value(d, VMValue::Void); } }
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
_ => {} _ => {}
@ -190,30 +350,78 @@ impl VM {
// Evaluate arguments as NyashBox for loader // Evaluate arguments as NyashBox for loader
let mut nyash_args: Vec<Box<dyn NyashBox>> = Vec::new(); let mut nyash_args: Vec<Box<dyn NyashBox>> = Vec::new();
for arg_id in args { let arg_value = self.get_value(*arg_id)?; nyash_args.push(arg_value.to_nyash_box()); } for arg_id in args {
let arg_value = self.get_value(*arg_id)?;
nyash_args.push(arg_value.to_nyash_box());
}
if crate::config::env::extern_trace() { if crate::config::env::extern_trace() {
if let Some(slot) = crate::runtime::extern_registry::resolve_slot(iface_name, method_name) { if let Some(slot) =
eprintln!("[EXT] call {}.{} slot={} argc={}", iface_name, method_name, slot, nyash_args.len()); crate::runtime::extern_registry::resolve_slot(iface_name, method_name)
} else { eprintln!("[EXT] call {}.{} argc={}", iface_name, method_name, nyash_args.len()); } {
eprintln!(
"[EXT] call {}.{} slot={} argc={}",
iface_name,
method_name,
slot,
nyash_args.len()
);
} else {
eprintln!(
"[EXT] call {}.{} argc={}",
iface_name,
method_name,
nyash_args.len()
);
}
} }
let host = crate::runtime::get_global_plugin_host(); let host = crate::runtime::get_global_plugin_host();
let host = host.read().map_err(|_| VMError::InvalidInstruction("Plugin host lock poisoned".into()))?; let host = host
.read()
.map_err(|_| VMError::InvalidInstruction("Plugin host lock poisoned".into()))?;
match host.extern_call(iface_name, method_name, &nyash_args) { match host.extern_call(iface_name, method_name, &nyash_args) {
Ok(Some(result_box)) => { if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::from_nyash_box(result_box)); } } Ok(Some(result_box)) => {
Ok(None) => { if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Void); } } if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::from_nyash_box(result_box));
}
}
Ok(None) => {
if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::Void);
}
}
Err(_) => { Err(_) => {
let strict = crate::config::env::extern_strict() || crate::config::env::abi_strict(); let strict =
crate::config::env::extern_strict() || crate::config::env::abi_strict();
let mut msg = String::new(); let mut msg = String::new();
if strict { msg.push_str("ExternCall STRICT: unregistered or unsupported call "); } else { msg.push_str("ExternCall failed: "); } if strict {
msg.push_str("ExternCall STRICT: unregistered or unsupported call ");
} else {
msg.push_str("ExternCall failed: ");
}
msg.push_str(&format!("{}.{}", iface_name, method_name)); msg.push_str(&format!("{}.{}", iface_name, method_name));
if let Err(detail) = crate::runtime::extern_registry::check_arity(iface_name, method_name, nyash_args.len()) { msg.push_str(&format!(" ({})", detail)); } if let Err(detail) = crate::runtime::extern_registry::check_arity(
if let Some(spec) = crate::runtime::extern_registry::resolve(iface_name, method_name) { iface_name,
msg.push_str(&format!(" (expected arity {}..{})", spec.min_arity, spec.max_arity)); method_name,
nyash_args.len(),
) {
msg.push_str(&format!(" ({})", detail));
}
if let Some(spec) =
crate::runtime::extern_registry::resolve(iface_name, method_name)
{
msg.push_str(&format!(
" (expected arity {}..{})",
spec.min_arity, spec.max_arity
));
} else { } else {
let known = crate::runtime::extern_registry::known_for_iface(iface_name); let known = crate::runtime::extern_registry::known_for_iface(iface_name);
if !known.is_empty() { msg.push_str(&format!("; known methods: {}", known.join(", "))); } if !known.is_empty() {
else { let ifaces = crate::runtime::extern_registry::all_ifaces(); msg.push_str(&format!("; known interfaces: {}", ifaces.join(", "))); } msg.push_str(&format!("; known methods: {}", known.join(", ")));
} else {
let ifaces = crate::runtime::extern_registry::all_ifaces();
msg.push_str(&format!("; known interfaces: {}", ifaces.join(", ")));
}
} }
return Err(VMError::InvalidInstruction(msg)); return Err(VMError::InvalidInstruction(msg));
} }

View File

@ -1,7 +1,7 @@
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::mir::ValueId; use crate::mir::ValueId;
use std::sync::Arc; use std::sync::Arc;
use crate::backend::vm::ControlFlow;
use crate::backend::{VM, VMError, VMValue};
impl VM { impl VM {
/// Execute FunctionNew instruction (construct a FunctionBox value) /// Execute FunctionNew instruction (construct a FunctionBox value)
@ -23,11 +23,14 @@ impl VM {
// Capture 'me' weakly if provided and is a BoxRef // Capture 'me' weakly if provided and is a BoxRef
if let Some(m) = me { if let Some(m) = me {
match self.get_value(*m)? { match self.get_value(*m)? {
VMValue::BoxRef(b) => { env.me_value = Some(Arc::downgrade(&b)); } VMValue::BoxRef(b) => {
env.me_value = Some(Arc::downgrade(&b));
}
_ => {} _ => {}
} }
} }
let fun = crate::boxes::function_box::FunctionBox::with_env(params.to_vec(), body.to_vec(), env); let fun =
crate::boxes::function_box::FunctionBox::with_env(params.to_vec(), body.to_vec(), env);
self.set_value(dst, VMValue::BoxRef(Arc::new(fun))); self.set_value(dst, VMValue::BoxRef(Arc::new(fun)));
Ok(ControlFlow::Continue) Ok(ControlFlow::Continue)
} }

View File

@ -5,11 +5,10 @@
* behavior and public APIs. Methods remain as `impl VM` across submodules. * behavior and public APIs. Methods remain as `impl VM` across submodules.
*/ */
pub mod core; // const/binop/unary/compare/print/ctrl/type/phi/mem/array/refs/weak/barriers/exn/await
pub mod call; // execute_call (Function name or FunctionBox)
pub mod newbox; // execute_newbox
pub mod function_new; // execute_function_new
pub mod extern_call; // execute_extern_call
pub mod boxcall; // execute_boxcall + vtable stub pub mod boxcall; // execute_boxcall + vtable stub
pub mod call; // execute_call (Function name or FunctionBox)
pub mod core; // const/binop/unary/compare/print/ctrl/type/phi/mem/array/refs/weak/barriers/exn/await
pub mod extern_call; // execute_extern_call
pub mod function_new; // execute_function_new
pub mod newbox; // execute_newbox
pub mod plugin_invoke; // execute_plugin_invoke + helpers pub mod plugin_invoke; // execute_plugin_invoke + helpers

View File

@ -1,14 +1,20 @@
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::box_trait::NyashBox; use crate::box_trait::NyashBox;
use crate::mir::ValueId; use crate::mir::ValueId;
use std::sync::Arc; use std::sync::Arc;
use crate::backend::vm::ControlFlow;
use crate::backend::{VM, VMError, VMValue};
impl VM { impl VM {
/// Execute NewBox instruction /// Execute NewBox instruction
pub(crate) fn execute_newbox(&mut self, dst: ValueId, box_type: &str, args: &[ValueId]) -> Result<ControlFlow, VMError> { pub(crate) fn execute_newbox(
&mut self,
dst: ValueId,
box_type: &str,
args: &[ValueId],
) -> Result<ControlFlow, VMError> {
// Convert args to NyashBox values // Convert args to NyashBox values
let arg_values: Vec<Box<dyn NyashBox>> = args.iter() let arg_values: Vec<Box<dyn NyashBox>> = args
.iter()
.map(|arg| { .map(|arg| {
let val = self.get_value(*arg)?; let val = self.get_value(*arg)?;
Ok(val.to_nyash_box()) Ok(val.to_nyash_box())
@ -17,15 +23,20 @@ impl VM {
// Create new box using runtime's registry // Create new box using runtime's registry
let new_box = { let new_box = {
let registry = self.runtime.box_registry.lock() let registry = self.runtime.box_registry.lock().map_err(|_| {
.map_err(|_| VMError::InvalidInstruction("Failed to lock box registry".to_string()))?; VMError::InvalidInstruction("Failed to lock box registry".to_string())
registry.create_box(box_type, &arg_values) })?;
.map_err(|e| VMError::InvalidInstruction(format!("Failed to create {}: {}", box_type, e)))? registry.create_box(box_type, &arg_values).map_err(|e| {
VMError::InvalidInstruction(format!("Failed to create {}: {}", box_type, e))
})?
}; };
// 80/20: Basic boxes are stored as primitives in VMValue for simpler ops // 80/20: Basic boxes are stored as primitives in VMValue for simpler ops
if box_type == "IntegerBox" { if box_type == "IntegerBox" {
if let Some(ib) = new_box.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { if let Some(ib) = new_box
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
{
self.set_value(dst, VMValue::Integer(ib.value)); self.set_value(dst, VMValue::Integer(ib.value));
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
@ -35,7 +46,10 @@ impl VM {
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
} else if box_type == "StringBox" { } else if box_type == "StringBox" {
if let Some(sb) = new_box.as_any().downcast_ref::<crate::box_trait::StringBox>() { if let Some(sb) = new_box
.as_any()
.downcast_ref::<crate::box_trait::StringBox>()
{
self.set_value(dst, VMValue::String(sb.value.clone())); self.set_value(dst, VMValue::String(sb.value.clone()));
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }

View File

@ -1,79 +1,186 @@
use crate::mir::ValueId;
use crate::backend::vm::ControlFlow; use crate::backend::vm::ControlFlow;
use crate::backend::{VM, VMError, VMValue}; use crate::backend::{VMError, VMValue, VM};
use crate::mir::ValueId;
impl VM { impl VM {
/// Execute a forced plugin invocation (no builtin fallback) /// Execute a forced plugin invocation (no builtin fallback)
pub(crate) fn execute_plugin_invoke(&mut self, dst: Option<ValueId>, box_val: ValueId, method: &str, args: &[ValueId]) -> Result<ControlFlow, VMError> { pub(crate) fn execute_plugin_invoke(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
method: &str,
args: &[ValueId],
) -> Result<ControlFlow, VMError> {
// Helper: extract UTF-8 string from internal StringBox, Result.Ok(String-like), or plugin StringBox via toUtf8 // Helper: extract UTF-8 string from internal StringBox, Result.Ok(String-like), or plugin StringBox via toUtf8
fn extract_string_from_box(bx: &dyn crate::box_trait::NyashBox) -> Option<String> { fn extract_string_from_box(bx: &dyn crate::box_trait::NyashBox) -> Option<String> {
if let Some(sb) = bx.as_any().downcast_ref::<crate::box_trait::StringBox>() { return Some(sb.value.clone()); } if let Some(sb) = bx.as_any().downcast_ref::<crate::box_trait::StringBox>() {
if let Some(res) = bx.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() { return Some(sb.value.clone());
if let crate::boxes::result::NyashResultBox::Ok(inner) = res { return extract_string_from_box(inner.as_ref()); }
} }
if let Some(p) = bx.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() { if let Some(res) = bx
.as_any()
.downcast_ref::<crate::boxes::result::NyashResultBox>()
{
if let crate::boxes::result::NyashResultBox::Ok(inner) = res {
return extract_string_from_box(inner.as_ref());
}
}
if let Some(p) = bx
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if p.box_type == "StringBox" { if p.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host(); let host = crate::runtime::get_global_plugin_host();
let tmp: Option<String> = if let Ok(ro) = host.read() { let tmp: Option<String> = if let Ok(ro) = host.read() {
if let Ok(val_opt) = ro.invoke_instance_method("StringBox", "toUtf8", p.inner.instance_id, &[]) { if let Ok(val_opt) = ro.invoke_instance_method(
if let Some(vb) = val_opt { if let Some(sb2) = vb.as_any().downcast_ref::<crate::box_trait::StringBox>() { Some(sb2.value.clone()) } else { None } } else { None } "StringBox",
} else { None } "toUtf8",
} else { None }; p.inner.instance_id,
if tmp.is_some() { return tmp; } &[],
) {
if let Some(vb) = val_opt {
if let Some(sb2) =
vb.as_any().downcast_ref::<crate::box_trait::StringBox>()
{
Some(sb2.value.clone())
} else {
None
}
} else {
None
}
} else {
None
}
} else {
None
};
if tmp.is_some() {
return tmp;
}
} }
} }
None None
} }
let recv = self.get_value(box_val)?; let recv = self.get_value(box_val)?;
if method == "birth" && !matches!(recv, VMValue::BoxRef(ref b) if b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some()) { if method == "birth"
&& !matches!(recv, VMValue::BoxRef(ref b) if b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some())
{
eprintln!("[VM PluginInvoke] static birth fallback recv={:?}", recv); eprintln!("[VM PluginInvoke] static birth fallback recv={:?}", recv);
let mut created: Option<VMValue> = None; let mut created: Option<VMValue> = None;
match &recv { match &recv {
VMValue::String(s) => { VMValue::String(s) => {
let host = crate::runtime::get_global_plugin_host(); let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap(); let host = host.read().unwrap();
let sb: Box<dyn crate::box_trait::NyashBox> = Box::new(crate::box_trait::StringBox::new(s.clone())); let sb: Box<dyn crate::box_trait::NyashBox> =
if let Ok(b) = host.create_box("StringBox", &[sb]) { created = Some(VMValue::from_nyash_box(b)); } Box::new(crate::box_trait::StringBox::new(s.clone()));
if let Ok(b) = host.create_box("StringBox", &[sb]) {
created = Some(VMValue::from_nyash_box(b));
}
} }
VMValue::Integer(_n) => { VMValue::Integer(_n) => {
let host = crate::runtime::get_global_plugin_host(); let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap(); let host = host.read().unwrap();
if let Ok(b) = host.create_box("IntegerBox", &[]) { created = Some(VMValue::from_nyash_box(b)); } if let Ok(b) = host.create_box("IntegerBox", &[]) {
created = Some(VMValue::from_nyash_box(b));
}
} }
_ => {} _ => {}
} }
if let Some(val) = created { if let Some(dst_id) = dst { self.set_value(dst_id, val); } return Ok(ControlFlow::Continue); } if let Some(val) = created {
if let Some(dst_id) = dst {
self.set_value(dst_id, val);
}
return Ok(ControlFlow::Continue);
}
} }
if let VMValue::BoxRef(pbox) = &recv { if let VMValue::BoxRef(pbox) = &recv {
if let Some(p) = pbox.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() { if let Some(p) = pbox
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
let host = crate::runtime::get_global_plugin_host(); let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap(); let host = host.read().unwrap();
let mh = host.resolve_method(&p.box_type, method).map_err(|_| VMError::InvalidInstruction(format!("Plugin method not found: {}.{}", p.box_type, method)))?; let mh = host.resolve_method(&p.box_type, method).map_err(|_| {
let mut tlv = crate::runtime::plugin_ffi_common::encode_tlv_header(args.len() as u16); VMError::InvalidInstruction(format!(
"Plugin method not found: {}.{}",
p.box_type, method
))
})?;
let mut tlv =
crate::runtime::plugin_ffi_common::encode_tlv_header(args.len() as u16);
for (idx, a) in args.iter().enumerate() { for (idx, a) in args.iter().enumerate() {
let v = self.get_value(*a)?; let v = self.get_value(*a)?;
match v { match v {
VMValue::Integer(n) => { if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") { eprintln!("[VM→Plugin][vm] arg[{}] encode I64 {}", idx, n); } crate::runtime::plugin_ffi_common::encode::i64(&mut tlv, n) } VMValue::Integer(n) => {
VMValue::Float(x) => { if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") { eprintln!("[VM→Plugin][vm] arg[{}] encode F64 {}", idx, x); } crate::runtime::plugin_ffi_common::encode::f64(&mut tlv, x) } if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
VMValue::Bool(b) => crate::runtime::plugin_ffi_common::encode::bool(&mut tlv, b), eprintln!("[VM→Plugin][vm] arg[{}] encode I64 {}", idx, n);
VMValue::String(ref s) => crate::runtime::plugin_ffi_common::encode::string(&mut tlv, s), }
crate::runtime::plugin_ffi_common::encode::i64(&mut tlv, n)
}
VMValue::Float(x) => {
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
eprintln!("[VM→Plugin][vm] arg[{}] encode F64 {}", idx, x);
}
crate::runtime::plugin_ffi_common::encode::f64(&mut tlv, x)
}
VMValue::Bool(b) => {
crate::runtime::plugin_ffi_common::encode::bool(&mut tlv, b)
}
VMValue::String(ref s) => {
crate::runtime::plugin_ffi_common::encode::string(&mut tlv, s)
}
VMValue::BoxRef(ref b) => { VMValue::BoxRef(ref b) => {
if let Some(h) = b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() { if let Some(h) = b
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if h.box_type == "StringBox" { if h.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host(); let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap(); let host = host.read().unwrap();
if let Ok(val_opt) = host.invoke_instance_method("StringBox", "toUtf8", h.inner.instance_id, &[]) { if let Ok(val_opt) = host.invoke_instance_method(
if let Some(sb) = val_opt.and_then(|bx| bx.as_any().downcast_ref::<crate::box_trait::StringBox>().map(|s| s.value.clone())) { crate::runtime::plugin_ffi_common::encode::string(&mut tlv, &sb); continue; } "StringBox",
"toUtf8",
h.inner.instance_id,
&[],
) {
if let Some(sb) = val_opt.and_then(|bx| {
bx.as_any()
.downcast_ref::<crate::box_trait::StringBox>()
.map(|s| s.value.clone())
}) {
crate::runtime::plugin_ffi_common::encode::string(
&mut tlv, &sb,
);
continue;
}
} }
} else if h.box_type == "IntegerBox" { } else if h.box_type == "IntegerBox" {
let host = crate::runtime::get_global_plugin_host(); let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap(); let host = host.read().unwrap();
if let Ok(val_opt) = host.invoke_instance_method("IntegerBox", "get", h.inner.instance_id, &[]) { if let Ok(val_opt) = host.invoke_instance_method(
if let Some(ib) = val_opt.and_then(|bx| bx.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|i| i.value)) { crate::runtime::plugin_ffi_common::encode::i64(&mut tlv, ib); continue; } "IntegerBox",
"get",
h.inner.instance_id,
&[],
) {
if let Some(ib) = val_opt.and_then(|bx| {
bx.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|i| i.value)
}) {
crate::runtime::plugin_ffi_common::encode::i64(
&mut tlv, ib,
);
continue;
} }
} }
crate::runtime::plugin_ffi_common::encode::plugin_handle(&mut tlv, h.inner.type_id, h.inner.instance_id); }
crate::runtime::plugin_ffi_common::encode::plugin_handle(
&mut tlv,
h.inner.type_id,
h.inner.instance_id,
);
} else { } else {
let h = crate::runtime::host_handles::to_handle_arc(b.clone()); let h = crate::runtime::host_handles::to_handle_arc(b.clone());
crate::runtime::plugin_ffi_common::encode::host_handle(&mut tlv, h); crate::runtime::plugin_ffi_common::encode::host_handle(&mut tlv, h);
@ -84,28 +191,63 @@ impl VM {
} }
let mut out = vec![0u8; 32768]; let mut out = vec![0u8; 32768];
let mut out_len: usize = out.len(); let mut out_len: usize = out.len();
unsafe { (p.inner.invoke_fn)(p.inner.type_id, mh.method_id as u32, p.inner.instance_id, tlv.as_ptr(), tlv.len(), out.as_mut_ptr(), &mut out_len) }; unsafe {
let vm_out_raw: VMValue = if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) { (p.inner.invoke_fn)(
p.inner.type_id,
mh.method_id as u32,
p.inner.instance_id,
tlv.as_ptr(),
tlv.len(),
out.as_mut_ptr(),
&mut out_len,
)
};
let vm_out_raw: VMValue = if let Some((tag, _sz, payload)) =
crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len])
{
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") { if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
eprintln!("[VM←Plugin] tag={} size={} bytes={}", tag, _sz, payload.len()); eprintln!(
"[VM←Plugin] tag={} size={} bytes={}",
tag,
_sz,
payload.len()
);
} }
match tag { match tag {
1 => crate::runtime::plugin_ffi_common::decode::bool(payload).map(VMValue::Bool).unwrap_or(VMValue::Void), 1 => crate::runtime::plugin_ffi_common::decode::bool(payload)
.map(VMValue::Bool)
.unwrap_or(VMValue::Void),
2 => { 2 => {
let v = crate::runtime::plugin_ffi_common::decode::i32(payload).unwrap_or_default(); let v = crate::runtime::plugin_ffi_common::decode::i32(payload)
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") { eprintln!("[VM←Plugin] decode i32={}", v); } .unwrap_or_default();
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
eprintln!("[VM←Plugin] decode i32={}", v);
}
VMValue::Integer(v as i64) VMValue::Integer(v as i64)
}, }
5 => crate::runtime::plugin_ffi_common::decode::f64(payload).map(VMValue::Float).unwrap_or(VMValue::Void), 5 => crate::runtime::plugin_ffi_common::decode::f64(payload)
6 | 7 => VMValue::String(crate::runtime::plugin_ffi_common::decode::string(payload)), .map(VMValue::Float)
.unwrap_or(VMValue::Void),
6 | 7 => VMValue::String(
crate::runtime::plugin_ffi_common::decode::string(payload),
),
8 => { 8 => {
if let Some(u) = crate::runtime::plugin_ffi_common::decode::u64(payload) { if let Some(u) = crate::runtime::plugin_ffi_common::decode::u64(payload)
if let Some(arc) = crate::runtime::host_handles::get(u) { VMValue::BoxRef(arc) } else { VMValue::Void } {
} else { VMValue::Void } if let Some(arc) = crate::runtime::host_handles::get(u) {
VMValue::BoxRef(arc)
} else {
VMValue::Void
}
} else {
VMValue::Void
}
} }
_ => VMValue::Void, _ => VMValue::Void,
} }
} else { VMValue::Void }; } else {
VMValue::Void
};
// Wrap into Result.Ok when method is declared returns_result // Wrap into Result.Ok when method is declared returns_result
let vm_out = { let vm_out = {
let host = crate::runtime::get_global_plugin_host(); let host = crate::runtime::get_global_plugin_host();
@ -119,13 +261,19 @@ impl VM {
VMValue::String(s) => Box::new(crate::box_trait::StringBox::new(s)), VMValue::String(s) => Box::new(crate::box_trait::StringBox::new(s)),
VMValue::BoxRef(b) => b.share_box(), VMValue::BoxRef(b) => b.share_box(),
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()), VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
_ => Box::new(crate::box_trait::StringBox::new(vm_out_raw.to_string())) _ => Box::new(crate::box_trait::StringBox::new(vm_out_raw.to_string())),
}; };
let res = crate::boxes::result::NyashResultBox::new_ok(boxed); let res = crate::boxes::result::NyashResultBox::new_ok(boxed);
VMValue::BoxRef(std::sync::Arc::from(Box::new(res) as Box<dyn crate::box_trait::NyashBox>)) VMValue::BoxRef(std::sync::Arc::from(
} else { vm_out_raw } Box::new(res) as Box<dyn crate::box_trait::NyashBox>
))
} else {
vm_out_raw
}
}; };
if let Some(dst_id) = dst { self.set_value(dst_id, vm_out); } if let Some(dst_id) = dst {
self.set_value(dst_id, vm_out);
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
} }
@ -133,28 +281,66 @@ impl VM {
if let VMValue::BoxRef(ref bx) = recv { if let VMValue::BoxRef(ref bx) = recv {
if let Some(s) = extract_string_from_box(bx.as_ref()) { if let Some(s) = extract_string_from_box(bx.as_ref()) {
match method { match method {
"length" => { if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Integer(s.len() as i64)); } return Ok(ControlFlow::Continue); } "length" => {
"is_empty" | "isEmpty" => { if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Bool(s.is_empty())); } return Ok(ControlFlow::Continue); } if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::Integer(s.len() as i64));
}
return Ok(ControlFlow::Continue);
}
"is_empty" | "isEmpty" => {
if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::Bool(s.is_empty()));
}
return Ok(ControlFlow::Continue);
}
"charCodeAt" => { "charCodeAt" => {
let idx_v = if let Some(a0) = args.get(0) { self.get_value(*a0)? } else { VMValue::Integer(0) }; let idx_v = if let Some(a0) = args.get(0) {
let idx = match idx_v { VMValue::Integer(i) => i.max(0) as usize, _ => 0 }; self.get_value(*a0)?
} else {
VMValue::Integer(0)
};
let idx = match idx_v {
VMValue::Integer(i) => i.max(0) as usize,
_ => 0,
};
let code = s.chars().nth(idx).map(|c| c as u32 as i64).unwrap_or(0); let code = s.chars().nth(idx).map(|c| c as u32 as i64).unwrap_or(0);
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Integer(code)); } if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::Integer(code));
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
"concat" => { "concat" => {
let rhs_v = if let Some(a0) = args.get(0) { self.get_value(*a0)? } else { VMValue::String(String::new()) }; let rhs_v = if let Some(a0) = args.get(0) {
let rhs_s = match rhs_v { VMValue::String(ss) => ss, VMValue::BoxRef(br) => extract_string_from_box(br.as_ref()).unwrap_or_else(|| br.to_string_box().value), _ => rhs_v.to_string(), }; self.get_value(*a0)?
} else {
VMValue::String(String::new())
};
let rhs_s = match rhs_v {
VMValue::String(ss) => ss,
VMValue::BoxRef(br) => extract_string_from_box(br.as_ref())
.unwrap_or_else(|| br.to_string_box().value),
_ => rhs_v.to_string(),
};
let mut new_s = s.clone(); let mut new_s = s.clone();
new_s.push_str(&rhs_s); new_s.push_str(&rhs_s);
let out = Box::new(crate::box_trait::StringBox::new(new_s)); let out = Box::new(crate::box_trait::StringBox::new(new_s));
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::BoxRef(std::sync::Arc::from(out as Box<dyn crate::box_trait::NyashBox>))); } if let Some(dst_id) = dst {
self.set_value(
dst_id,
VMValue::BoxRef(std::sync::Arc::from(
out as Box<dyn crate::box_trait::NyashBox>,
)),
);
}
return Ok(ControlFlow::Continue); return Ok(ControlFlow::Continue);
} }
_ => {} _ => {}
} }
} }
} }
Err(VMError::InvalidInstruction(format!("PluginInvoke requires PluginBox receiver; method={} got {:?}", method, recv))) Err(VMError::InvalidInstruction(format!(
"PluginInvoke requires PluginBox receiver; method={} got {:?}",
method, recv
)))
} }
} }

View File

@ -6,8 +6,8 @@
* the existing VM API surface. * the existing VM API surface.
*/ */
use super::vm::{VMError, VM};
use crate::box_trait::NyashBox; use crate::box_trait::NyashBox;
use super::vm::{VM, VMError};
impl VM { impl VM {
/// Unified method dispatch entry. Currently delegates to `call_box_method_impl`. /// Unified method dispatch entry. Currently delegates to `call_box_method_impl`.
@ -31,4 +31,3 @@ impl VM {
self.call_box_method_impl(box_value, method, args) self.call_box_method_impl(box_value, method, args)
} }
} }

View File

@ -7,7 +7,7 @@
* Typical Callers: VM 実行ループ(ブロック遷移/phi評価 * Typical Callers: VM 実行ループ(ブロック遷移/phi評価
*/ */
use super::vm::{VMValue, VMError}; use super::vm::{VMError, VMValue};
use crate::mir::{BasicBlockId, ValueId}; use crate::mir::{BasicBlockId, ValueId};
use std::collections::HashMap; use std::collections::HashMap;
@ -72,7 +72,9 @@ impl PhiHandler {
get_value_fn: impl Fn(ValueId) -> Result<VMValue, VMError>, get_value_fn: impl Fn(ValueId) -> Result<VMValue, VMError>,
) -> Result<VMValue, VMError> { ) -> Result<VMValue, VMError> {
if inputs.is_empty() { if inputs.is_empty() {
return Err(VMError::InvalidInstruction("Phi node has no inputs".to_string())); return Err(VMError::InvalidInstruction(
"Phi node has no inputs".to_string(),
));
} }
// previous_blockに基づいて入力を選択 // previous_blockに基づいて入力を選択
@ -125,7 +127,8 @@ impl LoopExecutor {
self.phi_handler.record_block_transition(from, to); self.phi_handler.record_block_transition(from, to);
// ループイテレーション数を更新(デバッグ用) // ループイテレーション数を更新(デバッグ用)
if from > to { // 単純なバックエッジ検出 if from > to {
// 単純なバックエッジ検出
*self.iteration_count.entry(to).or_insert(0) += 1; *self.iteration_count.entry(to).or_insert(0) += 1;
} }
} }
@ -184,33 +187,25 @@ mod tests {
// エントリポイントからの実行 // エントリポイントからの実行
handler.record_entry(); handler.record_entry();
let result = handler.execute_phi( let result = handler.execute_phi(ValueId::new(3), &inputs, |id| {
ValueId::new(3),
&inputs,
|id| {
if id == ValueId::new(1) { if id == ValueId::new(1) {
Ok(VMValue::Integer(0)) Ok(VMValue::Integer(0))
} else { } else {
Ok(VMValue::Integer(10)) Ok(VMValue::Integer(10))
} }
} });
);
assert_eq!(result.unwrap(), VMValue::Integer(0)); assert_eq!(result.unwrap(), VMValue::Integer(0));
// ループボディからの実行 // ループボディからの実行
handler.record_block_transition(BasicBlockId::new(2), BasicBlockId::new(1)); handler.record_block_transition(BasicBlockId::new(2), BasicBlockId::new(1));
handler.phi_cache.clear(); // テスト用にキャッシュクリア handler.phi_cache.clear(); // テスト用にキャッシュクリア
let result = handler.execute_phi( let result = handler.execute_phi(ValueId::new(3), &inputs, |id| {
ValueId::new(3),
&inputs,
|id| {
if id == ValueId::new(1) { if id == ValueId::new(1) {
Ok(VMValue::Integer(0)) Ok(VMValue::Integer(0))
} else { } else {
Ok(VMValue::Integer(10)) Ok(VMValue::Integer(10))
} }
} });
);
assert_eq!(result.unwrap(), VMValue::Integer(10)); assert_eq!(result.unwrap(), VMValue::Integer(10));
} }
} }

View File

@ -5,9 +5,9 @@
* phi selection delegation, and small utilities that support the exec loop. * phi selection delegation, and small utilities that support the exec loop.
*/ */
use super::vm::{VM, VMError, VMValue};
use super::vm_phi::LoopExecutor;
use super::frame::ExecutionFrame; use super::frame::ExecutionFrame;
use super::vm::{VMError, VMValue, VM};
use super::vm_phi::LoopExecutor;
use crate::mir::{BasicBlockId, ValueId}; use crate::mir::{BasicBlockId, ValueId};
use crate::runtime::NyashRuntime; use crate::runtime::NyashRuntime;
use crate::scope_tracker::ScopeTracker; use crate::scope_tracker::ScopeTracker;
@ -30,7 +30,9 @@ impl VM {
inputs: &[(BasicBlockId, ValueId)], inputs: &[(BasicBlockId, ValueId)],
) -> Result<VMValue, VMError> { ) -> Result<VMValue, VMError> {
if inputs.is_empty() { if inputs.is_empty() {
return Err(VMError::InvalidInstruction("Phi node has no inputs".to_string())); return Err(VMError::InvalidInstruction(
"Phi node has no inputs".to_string(),
));
} }
let debug_phi = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1") let debug_phi = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1")
|| std::env::var("NYASH_VM_DEBUG_PHI").ok().as_deref() == Some("1"); || std::env::var("NYASH_VM_DEBUG_PHI").ok().as_deref() == Some("1");
@ -50,7 +52,10 @@ impl VM {
Err(VMError::InvalidValue(format!("Value {} not set", val_id))) Err(VMError::InvalidValue(format!("Value {} not set", val_id)))
} }
} else { } else {
Err(VMError::InvalidValue(format!("Value {} out of bounds", val_id))) Err(VMError::InvalidValue(format!(
"Value {} out of bounds",
val_id
)))
} }
}); });
if debug_phi { if debug_phi {
@ -88,7 +93,9 @@ impl VM {
boxcall_vtable_funcname: std::collections::HashMap::new(), boxcall_vtable_funcname: std::collections::HashMap::new(),
type_versions: std::collections::HashMap::new(), type_versions: std::collections::HashMap::new(),
#[cfg(not(feature = "jit-direct-only"))] #[cfg(not(feature = "jit-direct-only"))]
jit_manager: Some(crate::jit::manager::JitManager::new(Self::jit_threshold_from_env())), jit_manager: Some(crate::jit::manager::JitManager::new(
Self::jit_threshold_from_env(),
)),
#[cfg(feature = "jit-direct-only")] #[cfg(feature = "jit-direct-only")]
jit_manager: None, jit_manager: None,
} }
@ -120,7 +127,9 @@ impl VM {
boxcall_vtable_funcname: std::collections::HashMap::new(), boxcall_vtable_funcname: std::collections::HashMap::new(),
type_versions: std::collections::HashMap::new(), type_versions: std::collections::HashMap::new(),
#[cfg(not(feature = "jit-direct-only"))] #[cfg(not(feature = "jit-direct-only"))]
jit_manager: Some(crate::jit::manager::JitManager::new(Self::jit_threshold_from_env())), jit_manager: Some(crate::jit::manager::JitManager::new(
Self::jit_threshold_from_env(),
)),
#[cfg(feature = "jit-direct-only")] #[cfg(feature = "jit-direct-only")]
jit_manager: None, jit_manager: None,
} }
@ -136,7 +145,10 @@ impl VM {
Err(VMError::InvalidValue(format!("Value {} not set", value_id))) Err(VMError::InvalidValue(format!("Value {} not set", value_id)))
} }
} else { } else {
Err(VMError::InvalidValue(format!("Value {} out of bounds", value_id))) Err(VMError::InvalidValue(format!(
"Value {} out of bounds",
value_id
)))
} }
} }

View File

@ -12,25 +12,45 @@ use super::vm::VM;
impl VM { impl VM {
/// Print simple VM execution statistics when enabled via env var /// Print simple VM execution statistics when enabled via env var
pub(super) fn maybe_print_stats(&mut self) { pub(super) fn maybe_print_stats(&mut self) {
let enabled = std::env::var("NYASH_VM_STATS").ok().map(|v| v != "0").unwrap_or(false); let enabled = std::env::var("NYASH_VM_STATS")
if !enabled { return; } .ok()
.map(|v| v != "0")
.unwrap_or(false);
if !enabled {
return;
}
let elapsed_ms = self.exec_start.map(|t| t.elapsed().as_secs_f64() * 1000.0).unwrap_or(0.0); let elapsed_ms = self
let mut items: Vec<(&str, usize)> = self.instr_counter.iter().map(|(k,v)| (*k, *v)).collect(); .exec_start
.map(|t| t.elapsed().as_secs_f64() * 1000.0)
.unwrap_or(0.0);
let mut items: Vec<(&str, usize)> =
self.instr_counter.iter().map(|(k, v)| (*k, *v)).collect();
items.sort_by(|a, b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0))); items.sort_by(|a, b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0)));
let total: usize = items.iter().map(|(_, v)| *v).sum(); let total: usize = items.iter().map(|(_, v)| *v).sum();
let json_enabled = std::env::var("NYASH_VM_STATS_JSON").ok().map(|v| v != "0").unwrap_or(false) let json_enabled = std::env::var("NYASH_VM_STATS_JSON")
|| std::env::var("NYASH_VM_STATS_FORMAT").map(|v| v == "json").unwrap_or(false); .ok()
.map(|v| v != "0")
.unwrap_or(false)
|| std::env::var("NYASH_VM_STATS_FORMAT")
.map(|v| v == "json")
.unwrap_or(false);
if json_enabled { if json_enabled {
let counts_obj: std::collections::BTreeMap<&str, usize> = self.instr_counter.iter().map(|(k,v)| (*k, *v)).collect(); let counts_obj: std::collections::BTreeMap<&str, usize> =
let top20: Vec<_> = items.iter().take(20).map(|(op,cnt)| { self.instr_counter.iter().map(|(k, v)| (*k, *v)).collect();
serde_json::json!({ "op": op, "count": cnt }) let top20: Vec<_> = items
}).collect(); .iter()
.take(20)
.map(|(op, cnt)| serde_json::json!({ "op": op, "count": cnt }))
.collect();
let now_ms = { let now_ms = {
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_millis() as u64).unwrap_or(0) SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0)
}; };
let payload = serde_json::json!({ let payload = serde_json::json!({
"total": total, "total": total,
@ -44,7 +64,10 @@ impl VM {
Err(_) => println!("{{\"total\":{},\"elapsed_ms\":{:.3}}}", total, elapsed_ms), Err(_) => println!("{{\"total\":{},\"elapsed_ms\":{:.3}}}", total, elapsed_ms),
} }
} else { } else {
println!("\n🧮 VM Stats: {} instructions in {:.3} ms", total, elapsed_ms); println!(
"\n🧮 VM Stats: {} instructions in {:.3} ms",
total, elapsed_ms
);
for (k, v) in items.into_iter().take(20) { for (k, v) in items.into_iter().take(20) {
println!(" {:>10}: {:>8}", k, v); println!(" {:>10}: {:>8}", k, v);
} }
@ -55,9 +78,14 @@ impl VM {
pub(super) fn maybe_print_jit_unified_stats(&self) { pub(super) fn maybe_print_jit_unified_stats(&self) {
// Show when either JIT stats requested or VM stats are on // Show when either JIT stats requested or VM stats are on
let jit_enabled = std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1"); let jit_enabled = std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1");
let vm_enabled = std::env::var("NYASH_VM_STATS").ok().map(|v| v != "0").unwrap_or(false); let vm_enabled = std::env::var("NYASH_VM_STATS")
.ok()
.map(|v| v != "0")
.unwrap_or(false);
let jit_json = std::env::var("NYASH_JIT_STATS_JSON").ok().as_deref() == Some("1"); let jit_json = std::env::var("NYASH_JIT_STATS_JSON").ok().as_deref() == Some("1");
if !jit_enabled && !vm_enabled { return; } if !jit_enabled && !vm_enabled {
return;
}
if let Some(jm) = &self.jit_manager { if let Some(jm) = &self.jit_manager {
// Gather basic counters // Gather basic counters
let sites = jm.sites(); let sites = jm.sites();
@ -66,11 +94,19 @@ impl VM {
let ok = jm.exec_ok_count(); let ok = jm.exec_ok_count();
let tr = jm.exec_trap_count(); let tr = jm.exec_trap_count();
let total_exec = ok + tr; let total_exec = ok + tr;
let fb_rate = if total_exec > 0 { (tr as f64) / (total_exec as f64) } else { 0.0 }; let fb_rate = if total_exec > 0 {
(tr as f64) / (total_exec as f64)
} else {
0.0
};
let handles = crate::jit::rt::handles::len(); let handles = crate::jit::rt::handles::len();
let cfg = crate::jit::config::current(); let cfg = crate::jit::config::current();
let caps = crate::jit::config::probe_capabilities(); let caps = crate::jit::config::probe_capabilities();
let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig { "b1_bool" } else { "i64_bool" }; let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig {
"b1_bool"
} else {
"i64_bool"
};
let b1_norm = crate::jit::rt::b1_norm_get(); let b1_norm = crate::jit::rt::b1_norm_get();
let ret_b1_hints = crate::jit::rt::ret_bool_hint_get(); let ret_b1_hints = crate::jit::rt::ret_bool_hint_get();
if jit_json { if jit_json {
@ -99,11 +135,17 @@ impl VM {
}) })
}).collect::<Vec<_>>() }).collect::<Vec<_>>()
}); });
println!("{}", serde_json::to_string_pretty(&payload).unwrap_or_else(|_| String::from("{}"))); println!(
"{}",
serde_json::to_string_pretty(&payload).unwrap_or_else(|_| String::from("{}"))
);
} else { } else {
eprintln!("[JIT] summary: sites={} compiled={} hits={} exec_ok={} trap={} fallback_rate={:.2} handles={}", eprintln!("[JIT] summary: sites={} compiled={} hits={} exec_ok={} trap={} fallback_rate={:.2} handles={}",
sites, compiled, total_hits, ok, tr, fb_rate, handles); sites, compiled, total_hits, ok, tr, fb_rate, handles);
eprintln!(" abi_mode={} b1_norm_count={} ret_bool_hint_count={}", abi_mode, b1_norm, ret_b1_hints); eprintln!(
" abi_mode={} b1_norm_count={} ret_bool_hint_count={}",
abi_mode, b1_norm, ret_b1_hints
);
} }
} }
} }

View File

@ -5,7 +5,7 @@
* Kept separate to thin vm.rs and allow reuse across helpers. * Kept separate to thin vm.rs and allow reuse across helpers.
*/ */
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox}; use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox};
use crate::mir::ConstValue; use crate::mir::ConstValue;
use std::sync::Arc; use std::sync::Arc;
@ -94,7 +94,10 @@ impl VMValue {
pub fn as_integer(&self) -> Result<i64, VMError> { pub fn as_integer(&self) -> Result<i64, VMError> {
match self { match self {
VMValue::Integer(i) => Ok(*i), VMValue::Integer(i) => Ok(*i),
_ => Err(VMError::TypeError(format!("Expected integer, got {:?}", self))), _ => Err(VMError::TypeError(format!(
"Expected integer, got {:?}",
self
))),
} }
} }
@ -105,11 +108,25 @@ impl VMValue {
VMValue::Integer(i) => Ok(*i != 0), VMValue::Integer(i) => Ok(*i != 0),
// Pragmatic coercions for dynamic boxes (preserve legacy semantics) // Pragmatic coercions for dynamic boxes (preserve legacy semantics)
VMValue::BoxRef(b) => { VMValue::BoxRef(b) => {
if let Some(bb) = b.as_any().downcast_ref::<BoolBox>() { return Ok(bb.value); } if let Some(bb) = b.as_any().downcast_ref::<BoolBox>() {
if let Some(ib) = b.as_any().downcast_ref::<IntegerBox>() { return Ok(ib.value != 0); } return Ok(bb.value);
if let Some(ib) = b.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>() { return Ok(ib.value != 0); } }
if b.as_any().downcast_ref::<VoidBox>().is_some() { return Ok(false); } if let Some(ib) = b.as_any().downcast_ref::<IntegerBox>() {
Err(VMError::TypeError(format!("Expected bool, got BoxRef({})", b.type_name()))) return Ok(ib.value != 0);
}
if let Some(ib) = b
.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
{
return Ok(ib.value != 0);
}
if b.as_any().downcast_ref::<VoidBox>().is_some() {
return Ok(false);
}
Err(VMError::TypeError(format!(
"Expected bool, got BoxRef({})",
b.type_name()
)))
} }
VMValue::Void => Ok(false), VMValue::Void => Ok(false),
VMValue::Float(f) => Ok(*f != 0.0), VMValue::Float(f) => Ok(*f != 0.0),
@ -120,7 +137,11 @@ impl VMValue {
/// Convert from NyashBox to VMValue /// Convert from NyashBox to VMValue
pub fn from_nyash_box(nyash_box: Box<dyn crate::box_trait::NyashBox>) -> VMValue { pub fn from_nyash_box(nyash_box: Box<dyn crate::box_trait::NyashBox>) -> VMValue {
if nyash_box.as_any().downcast_ref::<crate::boxes::null_box::NullBox>().is_some() { if nyash_box
.as_any()
.downcast_ref::<crate::boxes::null_box::NullBox>()
.is_some()
{
// Treat NullBox as Void in VMValue to align with `null` literal semantics // Treat NullBox as Void in VMValue to align with `null` literal semantics
VMValue::Void VMValue::Void
} else if let Some(int_box) = nyash_box.as_any().downcast_ref::<IntegerBox>() { } else if let Some(int_box) = nyash_box.as_any().downcast_ref::<IntegerBox>() {
@ -129,7 +150,10 @@ impl VMValue {
VMValue::Bool(bool_box.value) VMValue::Bool(bool_box.value)
} else if let Some(string_box) = nyash_box.as_any().downcast_ref::<StringBox>() { } else if let Some(string_box) = nyash_box.as_any().downcast_ref::<StringBox>() {
VMValue::String(string_box.value.clone()) VMValue::String(string_box.value.clone())
} else if let Some(future_box) = nyash_box.as_any().downcast_ref::<crate::boxes::future::FutureBox>() { } else if let Some(future_box) = nyash_box
.as_any()
.downcast_ref::<crate::boxes::future::FutureBox>()
{
VMValue::Future(future_box.clone()) VMValue::Future(future_box.clone())
} else { } else {
VMValue::BoxRef(Arc::from(nyash_box)) VMValue::BoxRef(Arc::from(nyash_box))

View File

@ -7,53 +7,101 @@
* Typical Callers: vm_instructions::{execute_binop, execute_unaryop, execute_compare} * Typical Callers: vm_instructions::{execute_binop, execute_unaryop, execute_compare}
*/ */
use crate::mir::{BinaryOp, CompareOp, UnaryOp};
use super::vm::VM; use super::vm::VM;
use super::vm_types::{VMError, VMValue}; use super::vm_types::{VMError, VMValue};
use crate::mir::{BinaryOp, CompareOp, UnaryOp};
impl VM { impl VM {
/// Try to view a BoxRef as a UTF-8 string using unified semantics /// Try to view a BoxRef as a UTF-8 string using unified semantics
fn try_boxref_to_string(&self, b: &dyn crate::box_trait::NyashBox) -> Option<String> { fn try_boxref_to_string(&self, b: &dyn crate::box_trait::NyashBox) -> Option<String> {
// Avoid recursion via PluginHost<->Loader for PluginBoxV2 during VM add/string ops // Avoid recursion via PluginHost<->Loader for PluginBoxV2 during VM add/string ops
if b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() { if b.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
return None; return None;
} }
crate::runtime::semantics::coerce_to_string(b) crate::runtime::semantics::coerce_to_string(b)
} }
/// Execute binary operation /// Execute binary operation
pub(super) fn execute_binary_op(&self, op: &BinaryOp, left: &VMValue, right: &VMValue) -> Result<VMValue, VMError> { pub(super) fn execute_binary_op(
&self,
op: &BinaryOp,
left: &VMValue,
right: &VMValue,
) -> Result<VMValue, VMError> {
let debug_bin = std::env::var("NYASH_VM_DEBUG_BIN").ok().as_deref() == Some("1"); let debug_bin = std::env::var("NYASH_VM_DEBUG_BIN").ok().as_deref() == Some("1");
if debug_bin { eprintln!("[VM] binop {:?} {:?} {:?}", op, left, right); } if debug_bin {
eprintln!("[VM] binop {:?} {:?} {:?}", op, left, right);
}
if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") {
let lty = match left { VMValue::String(_) => "String", VMValue::Integer(_) => "Integer", VMValue::Float(_) => "Float", VMValue::Bool(_) => "Bool", _ => "Other" }; let lty = match left {
let rty = match right { VMValue::String(_) => "String", VMValue::Integer(_) => "Integer", VMValue::Float(_) => "Float", VMValue::Bool(_) => "Bool", _ => "Other" }; VMValue::String(_) => "String",
VMValue::Integer(_) => "Integer",
VMValue::Float(_) => "Float",
VMValue::Bool(_) => "Bool",
_ => "Other",
};
let rty = match right {
VMValue::String(_) => "String",
VMValue::Integer(_) => "Integer",
VMValue::Float(_) => "Float",
VMValue::Bool(_) => "Bool",
_ => "Other",
};
match *op { match *op {
BinaryOp::Add => { BinaryOp::Add => {
let strat = crate::grammar::engine::get().add_coercion_strategy(); let strat = crate::grammar::engine::get().add_coercion_strategy();
let rule = crate::grammar::engine::get().decide_add_result(lty, rty); let rule = crate::grammar::engine::get().decide_add_result(lty, rty);
eprintln!("[GRAMMAR-DIFF][VM] add.coercion_strategy={} left={} right={} rule={:?}", strat, lty, rty, rule); eprintln!(
"[GRAMMAR-DIFF][VM] add.coercion_strategy={} left={} right={} rule={:?}",
strat, lty, rty, rule
);
} }
BinaryOp::Sub => { BinaryOp::Sub => {
let strat = crate::grammar::engine::get().sub_coercion_strategy(); let strat = crate::grammar::engine::get().sub_coercion_strategy();
let rule = crate::grammar::engine::get().decide_sub_result(lty, rty); let rule = crate::grammar::engine::get().decide_sub_result(lty, rty);
eprintln!("[GRAMMAR-DIFF][VM] sub.coercion_strategy={} left={} right={} rule={:?}", strat, lty, rty, rule); eprintln!(
"[GRAMMAR-DIFF][VM] sub.coercion_strategy={} left={} right={} rule={:?}",
strat, lty, rty, rule
);
} }
BinaryOp::Mul => { BinaryOp::Mul => {
let strat = crate::grammar::engine::get().mul_coercion_strategy(); let strat = crate::grammar::engine::get().mul_coercion_strategy();
let rule = crate::grammar::engine::get().decide_mul_result(lty, rty); let rule = crate::grammar::engine::get().decide_mul_result(lty, rty);
eprintln!("[GRAMMAR-DIFF][VM] mul.coercion_strategy={} left={} right={} rule={:?}", strat, lty, rty, rule); eprintln!(
"[GRAMMAR-DIFF][VM] mul.coercion_strategy={} left={} right={} rule={:?}",
strat, lty, rty, rule
);
} }
BinaryOp::Div => { BinaryOp::Div => {
let strat = crate::grammar::engine::get().div_coercion_strategy(); let strat = crate::grammar::engine::get().div_coercion_strategy();
let rule = crate::grammar::engine::get().decide_div_result(lty, rty); let rule = crate::grammar::engine::get().decide_div_result(lty, rty);
eprintln!("[GRAMMAR-DIFF][VM] div.coercion_strategy={} left={} right={} rule={:?}", strat, lty, rty, rule); eprintln!(
"[GRAMMAR-DIFF][VM] div.coercion_strategy={} left={} right={} rule={:?}",
strat, lty, rty, rule
);
} }
_ => {} _ => {}
} }
} }
if matches!(*op, BinaryOp::Add) && std::env::var("NYASH_GRAMMAR_ENFORCE_ADD").ok().as_deref() == Some("1") { if matches!(*op, BinaryOp::Add)
let lty = match left { VMValue::String(_) => "String", VMValue::Integer(_) => "Integer", VMValue::Float(_) => "Float", VMValue::Bool(_) => "Bool", _ => "Other" }; && std::env::var("NYASH_GRAMMAR_ENFORCE_ADD").ok().as_deref() == Some("1")
let rty = match right { VMValue::String(_) => "String", VMValue::Integer(_) => "Integer", VMValue::Float(_) => "Float", VMValue::Bool(_) => "Bool", _ => "Other" }; {
let lty = match left {
VMValue::String(_) => "String",
VMValue::Integer(_) => "Integer",
VMValue::Float(_) => "Float",
VMValue::Bool(_) => "Bool",
_ => "Other",
};
let rty = match right {
VMValue::String(_) => "String",
VMValue::Integer(_) => "Integer",
VMValue::Float(_) => "Float",
VMValue::Bool(_) => "Bool",
_ => "Other",
};
if let Some((res, _)) = crate::grammar::engine::get().decide_add_result(lty, rty) { if let Some((res, _)) = crate::grammar::engine::get().decide_add_result(lty, rty) {
match res { match res {
"String" => { "String" => {
@ -86,7 +134,11 @@ impl VM {
if matches!(*op, BinaryOp::And | BinaryOp::Or) { if matches!(*op, BinaryOp::And | BinaryOp::Or) {
let l = left.as_bool()?; let l = left.as_bool()?;
let r = right.as_bool()?; let r = right.as_bool()?;
return Ok(VMValue::Bool(match *op { BinaryOp::And => l && r, BinaryOp::Or => l || r, _ => unreachable!() })); return Ok(VMValue::Bool(match *op {
BinaryOp::And => l && r,
BinaryOp::Or => l || r,
_ => unreachable!(),
}));
} }
match (left, right) { match (left, right) {
@ -96,107 +148,219 @@ impl VM {
BinaryOp::Sub => *l - *r, BinaryOp::Sub => *l - *r,
BinaryOp::Mul => *l * *r, BinaryOp::Mul => *l * *r,
BinaryOp::Div => { BinaryOp::Div => {
if *r == 0 { return Err(VMError::DivisionByZero); } if *r == 0 {
return Err(VMError::DivisionByZero);
}
*l / *r *l / *r
}, }
_ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))), _ => {
return Err(VMError::InvalidInstruction(format!(
"Unsupported integer operation: {:?}",
op
)))
}
}; };
Ok(VMValue::Integer(result)) Ok(VMValue::Integer(result))
},
(VMValue::String(l), VMValue::Integer(r)) => {
match op {
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))),
_ => Err(VMError::TypeError("String-integer operations only support addition".to_string())),
} }
},
(VMValue::String(l), VMValue::Bool(r)) => { (VMValue::String(l), VMValue::Integer(r)) => match op {
match op {
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))),
_ => Err(VMError::TypeError("String-bool operations only support addition".to_string())), _ => Err(VMError::TypeError(
} "String-integer operations only support addition".to_string(),
)),
}, },
(VMValue::String(l), VMValue::String(r)) => { (VMValue::String(l), VMValue::Bool(r)) => match op {
match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), _ => Err(VMError::TypeError("String operations only support addition".to_string())) } BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))),
_ => Err(VMError::TypeError(
"String-bool operations only support addition".to_string(),
)),
},
(VMValue::String(l), VMValue::String(r)) => match op {
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))),
_ => Err(VMError::TypeError(
"String operations only support addition".to_string(),
)),
}, },
// String + BoxRef concatenation // String + BoxRef concatenation
(VMValue::String(l), VMValue::BoxRef(r)) => { (VMValue::String(l), VMValue::BoxRef(r)) => {
let rs = self.try_boxref_to_string(r.as_ref()).unwrap_or_else(|| r.to_string_box().value); let rs = self
match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, rs))), _ => Err(VMError::TypeError("String-BoxRef operations only support addition".to_string())) } .try_boxref_to_string(r.as_ref())
}, .unwrap_or_else(|| r.to_string_box().value);
match op {
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, rs))),
_ => Err(VMError::TypeError(
"String-BoxRef operations only support addition".to_string(),
)),
}
}
// BoxRef + String concatenation // BoxRef + String concatenation
(VMValue::BoxRef(l), VMValue::String(r)) => { (VMValue::BoxRef(l), VMValue::String(r)) => {
let ls = self.try_boxref_to_string(l.as_ref()).unwrap_or_else(|| l.to_string_box().value); let ls = self
match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", ls, r))), _ => Err(VMError::TypeError("BoxRef-String operations only support addition".to_string())) } .try_boxref_to_string(l.as_ref())
}, .unwrap_or_else(|| l.to_string_box().value);
match op {
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", ls, r))),
_ => Err(VMError::TypeError(
"BoxRef-String operations only support addition".to_string(),
)),
}
}
// Arithmetic with BoxRef(IntegerBox) — support both legacy and new IntegerBox // Arithmetic with BoxRef(IntegerBox) — support both legacy and new IntegerBox
(VMValue::BoxRef(li), VMValue::BoxRef(ri)) if { (VMValue::BoxRef(li), VMValue::BoxRef(ri))
li.as_any().downcast_ref::<crate::box_trait::IntegerBox>().is_some() if {
|| li.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().is_some() li.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.is_some()
|| li
.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.is_some()
} && { } && {
ri.as_any().downcast_ref::<crate::box_trait::IntegerBox>().is_some() ri.as_any()
|| ri.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().is_some() .downcast_ref::<crate::box_trait::IntegerBox>()
} => { .is_some()
let l = li.as_any().downcast_ref::<crate::box_trait::IntegerBox>() || ri
.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.is_some()
} =>
{
let l = li
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value) .map(|x| x.value)
.or_else(|| li.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().map(|x| x.value)) .or_else(|| {
li.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.unwrap(); .unwrap();
let r = ri.as_any().downcast_ref::<crate::box_trait::IntegerBox>() let r = ri
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value) .map(|x| x.value)
.or_else(|| ri.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().map(|x| x.value)) .or_else(|| {
ri.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.unwrap(); .unwrap();
let res = match op { let res = match op {
BinaryOp::Add => l + r, BinaryOp::Add => l + r,
BinaryOp::Sub => l - r, BinaryOp::Sub => l - r,
BinaryOp::Mul => l * r, BinaryOp::Mul => l * r,
BinaryOp::Div => { if r == 0 { return Err(VMError::DivisionByZero); } l / r }, BinaryOp::Div => {
_ => return Err(VMError::InvalidInstruction(format!("Unsupported integer BoxRef operation: {:?}", op))), if r == 0 {
return Err(VMError::DivisionByZero);
}
l / r
}
_ => {
return Err(VMError::InvalidInstruction(format!(
"Unsupported integer BoxRef operation: {:?}",
op
)))
}
}; };
Ok(VMValue::Integer(res)) Ok(VMValue::Integer(res))
} }
// BoxRef + BoxRef string-like concatenation // BoxRef + BoxRef string-like concatenation
(VMValue::BoxRef(li), VMValue::BoxRef(ri)) => { (VMValue::BoxRef(li), VMValue::BoxRef(ri)) => {
if matches!(*op, BinaryOp::Add) { if matches!(*op, BinaryOp::Add) {
let ls = self.try_boxref_to_string(li.as_ref()).unwrap_or_else(|| li.to_string_box().value); let ls = self
let rs = self.try_boxref_to_string(ri.as_ref()).unwrap_or_else(|| ri.to_string_box().value); .try_boxref_to_string(li.as_ref())
.unwrap_or_else(|| li.to_string_box().value);
let rs = self
.try_boxref_to_string(ri.as_ref())
.unwrap_or_else(|| ri.to_string_box().value);
return Ok(VMValue::String(format!("{}{}", ls, rs))); return Ok(VMValue::String(format!("{}{}", ls, rs)));
} }
Err(VMError::TypeError("Unsupported BoxRef+BoxRef operation".to_string())) Err(VMError::TypeError(
"Unsupported BoxRef+BoxRef operation".to_string(),
))
} }
// Mixed Integer forms // Mixed Integer forms
(VMValue::BoxRef(li), VMValue::Integer(r)) if li.as_any().downcast_ref::<crate::box_trait::IntegerBox>().is_some() (VMValue::BoxRef(li), VMValue::Integer(r))
|| li.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().is_some() => { if li
let l = li.as_any().downcast_ref::<crate::box_trait::IntegerBox>() .as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.is_some()
|| li
.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.is_some() =>
{
let l = li
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value) .map(|x| x.value)
.or_else(|| li.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().map(|x| x.value)) .or_else(|| {
li.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.unwrap(); .unwrap();
let res = match op { let res = match op {
BinaryOp::Add => l + *r, BinaryOp::Add => l + *r,
BinaryOp::Sub => l - *r, BinaryOp::Sub => l - *r,
BinaryOp::Mul => l * *r, BinaryOp::Mul => l * *r,
BinaryOp::Div => { if *r == 0 { return Err(VMError::DivisionByZero); } l / *r }, BinaryOp::Div => {
_ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))), if *r == 0 {
return Err(VMError::DivisionByZero);
}
l / *r
}
_ => {
return Err(VMError::InvalidInstruction(format!(
"Unsupported integer operation: {:?}",
op
)))
}
}; };
Ok(VMValue::Integer(res)) Ok(VMValue::Integer(res))
} }
(VMValue::Integer(l), VMValue::BoxRef(ri)) if ri.as_any().downcast_ref::<crate::box_trait::IntegerBox>().is_some() (VMValue::Integer(l), VMValue::BoxRef(ri))
|| ri.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().is_some() => { if ri
let r = ri.as_any().downcast_ref::<crate::box_trait::IntegerBox>() .as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.is_some()
|| ri
.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.is_some() =>
{
let r = ri
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value) .map(|x| x.value)
.or_else(|| ri.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().map(|x| x.value)) .or_else(|| {
ri.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.unwrap(); .unwrap();
let res = match op { let res = match op {
BinaryOp::Add => *l + r, BinaryOp::Add => *l + r,
BinaryOp::Sub => *l - r, BinaryOp::Sub => *l - r,
BinaryOp::Mul => *l * r, BinaryOp::Mul => *l * r,
BinaryOp::Div => { if r == 0 { return Err(VMError::DivisionByZero); } *l / r }, BinaryOp::Div => {
_ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))), if r == 0 {
return Err(VMError::DivisionByZero);
}
*l / r
}
_ => {
return Err(VMError::InvalidInstruction(format!(
"Unsupported integer operation: {:?}",
op
)))
}
}; };
Ok(VMValue::Integer(res)) Ok(VMValue::Integer(res))
} }
@ -210,80 +374,211 @@ impl VM {
BinaryOp::Add => l + r, BinaryOp::Add => l + r,
BinaryOp::Sub => l - r, BinaryOp::Sub => l - r,
BinaryOp::Mul => l * r, BinaryOp::Mul => l * r,
BinaryOp::Div => { if r == 0 { return Err(VMError::DivisionByZero); } l / r }, BinaryOp::Div => {
_ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))), if r == 0 {
return Err(VMError::DivisionByZero);
}
l / r
}
_ => {
return Err(VMError::InvalidInstruction(format!(
"Unsupported integer operation: {:?}",
op
)))
}
}; };
if debug_bin { eprintln!("[VM] binop fallback BoxRef-BoxRef -> {}", res); } if debug_bin {
eprintln!("[VM] binop fallback BoxRef-BoxRef -> {}", res);
}
Ok(VMValue::Integer(res)) Ok(VMValue::Integer(res))
} else { } else {
Err(VMError::TypeError(format!("Unsupported binary operation: {:?} on {:?} and {:?}", op, left, right))) Err(VMError::TypeError(format!(
"Unsupported binary operation: {:?} on {:?} and {:?}",
op, left, right
)))
} }
} }
(VMValue::BoxRef(lb), VMValue::Integer(r)) => { (VMValue::BoxRef(lb), VMValue::Integer(r)) => {
if let Ok(l) = lb.to_string_box().value.trim().parse::<i64>() { if let Ok(l) = lb.to_string_box().value.trim().parse::<i64>() {
let res = match op { BinaryOp::Add => l + *r, BinaryOp::Sub => l - *r, BinaryOp::Mul => l * *r, BinaryOp::Div => { if *r == 0 { return Err(VMError::DivisionByZero); } l / *r }, _ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))), }; let res = match op {
if debug_bin { eprintln!("[VM] binop fallback BoxRef-Int -> {}", res); } BinaryOp::Add => l + *r,
BinaryOp::Sub => l - *r,
BinaryOp::Mul => l * *r,
BinaryOp::Div => {
if *r == 0 {
return Err(VMError::DivisionByZero);
}
l / *r
}
_ => {
return Err(VMError::InvalidInstruction(format!(
"Unsupported integer operation: {:?}",
op
)))
}
};
if debug_bin {
eprintln!("[VM] binop fallback BoxRef-Int -> {}", res);
}
Ok(VMValue::Integer(res)) Ok(VMValue::Integer(res))
} else { } else {
Err(VMError::TypeError(format!("Unsupported binary operation: {:?} on {:?} and {:?}", op, left, right))) Err(VMError::TypeError(format!(
"Unsupported binary operation: {:?} on {:?} and {:?}",
op, left, right
)))
} }
} }
(VMValue::Integer(l), VMValue::BoxRef(rb)) => { (VMValue::Integer(l), VMValue::BoxRef(rb)) => {
if let Ok(r) = rb.to_string_box().value.trim().parse::<i64>() { if let Ok(r) = rb.to_string_box().value.trim().parse::<i64>() {
let res = match op { BinaryOp::Add => *l + r, BinaryOp::Sub => *l - r, BinaryOp::Mul => *l * r, BinaryOp::Div => { if r == 0 { return Err(VMError::DivisionByZero); } *l / r }, _ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))), }; let res = match op {
if debug_bin { eprintln!("[VM] binop fallback Int-BoxRef -> {}", res); } BinaryOp::Add => *l + r,
BinaryOp::Sub => *l - r,
BinaryOp::Mul => *l * r,
BinaryOp::Div => {
if r == 0 {
return Err(VMError::DivisionByZero);
}
*l / r
}
_ => {
return Err(VMError::InvalidInstruction(format!(
"Unsupported integer operation: {:?}",
op
)))
}
};
if debug_bin {
eprintln!("[VM] binop fallback Int-BoxRef -> {}", res);
}
Ok(VMValue::Integer(res)) Ok(VMValue::Integer(res))
} else { } else {
Err(VMError::TypeError(format!("Unsupported binary operation: {:?} on {:?} and {:?}", op, left, right))) Err(VMError::TypeError(format!(
"Unsupported binary operation: {:?} on {:?} and {:?}",
op, left, right
)))
} }
} }
_ => Err(VMError::TypeError(format!("Unsupported binary operation: {:?} on {:?} and {:?}", op, left, right))), _ => Err(VMError::TypeError(format!(
"Unsupported binary operation: {:?} on {:?} and {:?}",
op, left, right
))),
} }
} }
/// Execute unary operation /// Execute unary operation
pub(super) fn execute_unary_op(&self, op: &UnaryOp, operand: &VMValue) -> Result<VMValue, VMError> { pub(super) fn execute_unary_op(
&self,
op: &UnaryOp,
operand: &VMValue,
) -> Result<VMValue, VMError> {
match (op, operand) { match (op, operand) {
(UnaryOp::Neg, VMValue::Integer(i)) => Ok(VMValue::Integer(-i)), (UnaryOp::Neg, VMValue::Integer(i)) => Ok(VMValue::Integer(-i)),
(UnaryOp::Not, VMValue::Bool(b)) => Ok(VMValue::Bool(!b)), (UnaryOp::Not, VMValue::Bool(b)) => Ok(VMValue::Bool(!b)),
_ => Err(VMError::TypeError(format!("Unsupported unary operation: {:?} on {:?}", op, operand))), _ => Err(VMError::TypeError(format!(
"Unsupported unary operation: {:?} on {:?}",
op, operand
))),
} }
} }
/// Execute comparison operation /// Execute comparison operation
pub(super) fn execute_compare_op(&self, op: &CompareOp, left: &VMValue, right: &VMValue) -> Result<bool, VMError> { pub(super) fn execute_compare_op(
let debug_cmp = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1") || &self,
std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1"); op: &CompareOp,
if debug_cmp { eprintln!("[VM] execute_compare_op enter: op={:?}, left={:?}, right={:?}", op, left, right); } left: &VMValue,
right: &VMValue,
) -> Result<bool, VMError> {
let debug_cmp = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1")
|| std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1");
if debug_cmp {
eprintln!(
"[VM] execute_compare_op enter: op={:?}, left={:?}, right={:?}",
op, left, right
);
}
match (left, right) { match (left, right) {
// Mixed numeric // Mixed numeric
(VMValue::Integer(l), VMValue::Float(r)) => { (VMValue::Integer(l), VMValue::Float(r)) => {
let l = *l as f64; let r = *r; Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r }) let l = *l as f64;
let r = *r;
Ok(match op {
CompareOp::Eq => l == r,
CompareOp::Ne => l != r,
CompareOp::Lt => l < r,
CompareOp::Le => l <= r,
CompareOp::Gt => l > r,
CompareOp::Ge => l >= r,
})
} }
(VMValue::Float(l), VMValue::Integer(r)) => { (VMValue::Float(l), VMValue::Integer(r)) => {
let l = *l; let r = *r as f64; Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r }) let l = *l;
let r = *r as f64;
Ok(match op {
CompareOp::Eq => l == r,
CompareOp::Ne => l != r,
CompareOp::Lt => l < r,
CompareOp::Le => l <= r,
CompareOp::Gt => l > r,
CompareOp::Ge => l >= r,
})
} }
// Bool // Bool
(VMValue::Bool(l), VMValue::Bool(r)) => { (VMValue::Bool(l), VMValue::Bool(r)) => Ok(match op {
Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, _ => return Err(VMError::TypeError(format!("Unsupported boolean comparison: {:?}", op))) }) CompareOp::Eq => l == r,
CompareOp::Ne => l != r,
_ => {
return Err(VMError::TypeError(format!(
"Unsupported boolean comparison: {:?}",
op
)))
} }
}),
// Void // Void
(VMValue::Void, VMValue::Void) => { (VMValue::Void, VMValue::Void) => Ok(match op {
Ok(match op { CompareOp::Eq => true, CompareOp::Ne => false, _ => return Err(VMError::TypeError("Cannot order Void".to_string())) }) CompareOp::Eq => true,
} CompareOp::Ne => false,
(VMValue::Void, _) | (_, VMValue::Void) => { _ => return Err(VMError::TypeError("Cannot order Void".to_string())),
Ok(match op { CompareOp::Eq => false, CompareOp::Ne => true, _ => return Err(VMError::TypeError("Cannot order Void".to_string())) }) }),
} (VMValue::Void, _) | (_, VMValue::Void) => Ok(match op {
CompareOp::Eq => false,
CompareOp::Ne => true,
_ => return Err(VMError::TypeError("Cannot order Void".to_string())),
}),
// Homogeneous // Homogeneous
(VMValue::Integer(l), VMValue::Integer(r)) => Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r }), (VMValue::Integer(l), VMValue::Integer(r)) => Ok(match op {
(VMValue::Float(l), VMValue::Float(r)) => Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r }), CompareOp::Eq => l == r,
(VMValue::String(l), VMValue::String(r)) => Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r }), CompareOp::Ne => l != r,
CompareOp::Lt => l < r,
CompareOp::Le => l <= r,
CompareOp::Gt => l > r,
CompareOp::Ge => l >= r,
}),
(VMValue::Float(l), VMValue::Float(r)) => Ok(match op {
CompareOp::Eq => l == r,
CompareOp::Ne => l != r,
CompareOp::Lt => l < r,
CompareOp::Le => l <= r,
CompareOp::Gt => l > r,
CompareOp::Ge => l >= r,
}),
(VMValue::String(l), VMValue::String(r)) => Ok(match op {
CompareOp::Eq => l == r,
CompareOp::Ne => l != r,
CompareOp::Lt => l < r,
CompareOp::Le => l <= r,
CompareOp::Gt => l > r,
CompareOp::Ge => l >= r,
}),
// BoxRef(IntegerBox) comparisons (homogeneous) // BoxRef(IntegerBox) comparisons (homogeneous)
(VMValue::BoxRef(li), VMValue::BoxRef(ri)) => { (VMValue::BoxRef(li), VMValue::BoxRef(ri)) => {
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
eprintln!("[VM] arm BoxRef-BoxRef: lt={}, rt={}", li.type_name(), ri.type_name()); eprintln!(
"[VM] arm BoxRef-BoxRef: lt={}, rt={}",
li.type_name(),
ri.type_name()
);
} }
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
eprintln!( eprintln!(
@ -296,91 +591,265 @@ impl VM {
if let Some(sb) = b.as_any().downcast_ref::<crate::box_trait::StringBox>() { if let Some(sb) = b.as_any().downcast_ref::<crate::box_trait::StringBox>() {
return Some(sb.value.clone()); return Some(sb.value.clone());
} }
if let Some(pb) = b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() { if let Some(pb) = b
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if pb.box_type == "StringBox" { if pb.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host(); let host = crate::runtime::get_global_plugin_host();
let s_opt: Option<String> = { let s_opt: Option<String> = {
if let Ok(ro) = host.read() { if let Ok(ro) = host.read() {
if let Ok(val_opt) = ro.invoke_instance_method("StringBox", "toUtf8", pb.inner.instance_id, &[]) { if let Ok(val_opt) = ro.invoke_instance_method(
"StringBox",
"toUtf8",
pb.inner.instance_id,
&[],
) {
if let Some(vb) = val_opt { if let Some(vb) = val_opt {
if let Some(sbb) = vb.as_any().downcast_ref::<crate::box_trait::StringBox>() { if let Some(sbb) =
vb.as_any()
.downcast_ref::<crate::box_trait::StringBox>()
{
Some(sbb.value.clone()) Some(sbb.value.clone())
} else { None } } else {
} else { None } None
} else { None } }
} else { None } } else {
None
}
} else {
None
}
} else {
None
}
}; };
if s_opt.is_some() { return s_opt; } if s_opt.is_some() {
return s_opt;
}
} }
} }
None None
} }
if let (Some(ls), Some(rs)) = (boxref_to_string(li.as_ref()), boxref_to_string(ri.as_ref())) { if let (Some(ls), Some(rs)) =
return Ok(match op { CompareOp::Eq => ls == rs, CompareOp::Ne => ls != rs, CompareOp::Lt => ls < rs, CompareOp::Le => ls <= rs, CompareOp::Gt => ls > rs, CompareOp::Ge => ls >= rs }); (boxref_to_string(li.as_ref()), boxref_to_string(ri.as_ref()))
{
return Ok(match op {
CompareOp::Eq => ls == rs,
CompareOp::Ne => ls != rs,
CompareOp::Lt => ls < rs,
CompareOp::Le => ls <= rs,
CompareOp::Gt => ls > rs,
CompareOp::Ge => ls >= rs,
});
} }
// Try integer comparisons via downcast or parse fallback // Try integer comparisons via downcast or parse fallback
let l_opt = li.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value) let l_opt = li
.or_else(|| li.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().map(|x| x.value)) .as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| {
li.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.or_else(|| li.to_string_box().value.parse::<i64>().ok()); .or_else(|| li.to_string_box().value.parse::<i64>().ok());
let r_opt = ri.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value) let r_opt = ri
.or_else(|| ri.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().map(|x| x.value)) .as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| {
ri.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.or_else(|| ri.to_string_box().value.parse::<i64>().ok()); .or_else(|| ri.to_string_box().value.parse::<i64>().ok());
if let (Some(l), Some(r)) = (l_opt, r_opt) { if let (Some(l), Some(r)) = (l_opt, r_opt) {
return Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r }); return Ok(match op {
CompareOp::Eq => l == r,
CompareOp::Ne => l != r,
CompareOp::Lt => l < r,
CompareOp::Le => l <= r,
CompareOp::Gt => l > r,
CompareOp::Ge => l >= r,
});
} }
Err(VMError::TypeError(format!("[BoxRef-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))) Err(VMError::TypeError(format!(
"[BoxRef-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}",
op, left, right
)))
} }
// Mixed String vs BoxRef (string-like) // Mixed String vs BoxRef (string-like)
(VMValue::String(ls), VMValue::BoxRef(ri)) => { (VMValue::String(ls), VMValue::BoxRef(ri)) => {
let rs_opt = if let Some(sb) = ri.as_any().downcast_ref::<crate::box_trait::StringBox>() { Some(sb.value.clone()) } else { let rs_opt =
if let Some(pb) = ri.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() { if let Some(sb) = ri.as_any().downcast_ref::<crate::box_trait::StringBox>() {
Some(sb.value.clone())
} else {
if let Some(pb) = ri
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if pb.box_type == "StringBox" { if pb.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host(); let host = crate::runtime::get_global_plugin_host();
let tmp = if let Ok(ro) = host.read() { let tmp = if let Ok(ro) = host.read() {
if let Ok(val_opt) = ro.invoke_instance_method("StringBox", "toUtf8", pb.inner.instance_id, &[]) { if let Ok(val_opt) = ro.invoke_instance_method(
"StringBox",
"toUtf8",
pb.inner.instance_id,
&[],
) {
if let Some(vb) = val_opt { if let Some(vb) = val_opt {
if let Some(sbb) = vb.as_any().downcast_ref::<crate::box_trait::StringBox>() { Some(sbb.value.clone()) } else { None } if let Some(sbb) =
} else { None } vb.as_any()
} else { None } .downcast_ref::<crate::box_trait::StringBox>()
} else { None }; {
tmp Some(sbb.value.clone())
} else { None } } else {
} else { None } None
}
} else {
None
}
} else {
None
}
} else {
None
}; };
if let Some(rs) = rs_opt { return Ok(match op { CompareOp::Eq => *ls == rs, CompareOp::Ne => *ls != rs, CompareOp::Lt => *ls < rs, CompareOp::Le => *ls <= rs, CompareOp::Gt => *ls > rs, CompareOp::Ge => *ls >= rs }); } tmp
Err(VMError::TypeError(format!("[String-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))) } else {
None
}
} else {
None
}
};
if let Some(rs) = rs_opt {
return Ok(match op {
CompareOp::Eq => *ls == rs,
CompareOp::Ne => *ls != rs,
CompareOp::Lt => *ls < rs,
CompareOp::Le => *ls <= rs,
CompareOp::Gt => *ls > rs,
CompareOp::Ge => *ls >= rs,
});
}
Err(VMError::TypeError(format!(
"[String-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}",
op, left, right
)))
} }
(VMValue::BoxRef(li), VMValue::String(rs)) => { (VMValue::BoxRef(li), VMValue::String(rs)) => {
let ls_opt = if let Some(sb) = li.as_any().downcast_ref::<crate::box_trait::StringBox>() { Some(sb.value.clone()) } else { let ls_opt =
if let Some(pb) = li.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() { if let Some(sb) = li.as_any().downcast_ref::<crate::box_trait::StringBox>() {
Some(sb.value.clone())
} else {
if let Some(pb) = li
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if pb.box_type == "StringBox" { if pb.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host(); let host = crate::runtime::get_global_plugin_host();
let tmp = if let Ok(ro) = host.read() { let tmp = if let Ok(ro) = host.read() {
if let Ok(val_opt) = ro.invoke_instance_method("StringBox", "toUtf8", pb.inner.instance_id, &[]) { if let Ok(val_opt) = ro.invoke_instance_method(
if let Some(vb) = val_opt { if let Some(sbb) = vb.as_any().downcast_ref::<crate::box_trait::StringBox>() { Some(sbb.value.clone()) } else { None } } else { None } "StringBox",
} else { None } "toUtf8",
} else { None }; pb.inner.instance_id,
tmp &[],
} else { None } ) {
} else { None } if let Some(vb) = val_opt {
if let Some(sbb) =
vb.as_any()
.downcast_ref::<crate::box_trait::StringBox>()
{
Some(sbb.value.clone())
} else {
None
}
} else {
None
}
} else {
None
}
} else {
None
}; };
if let Some(ls) = ls_opt { return Ok(match op { CompareOp::Eq => ls == *rs, CompareOp::Ne => ls != *rs, CompareOp::Lt => ls < *rs, CompareOp::Le => ls <= *rs, CompareOp::Gt => ls > *rs, CompareOp::Ge => ls >= *rs }); } tmp
Err(VMError::TypeError(format!("[BoxRef-String] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))) } else {
None
}
} else {
None
}
};
if let Some(ls) = ls_opt {
return Ok(match op {
CompareOp::Eq => ls == *rs,
CompareOp::Ne => ls != *rs,
CompareOp::Lt => ls < *rs,
CompareOp::Le => ls <= *rs,
CompareOp::Gt => ls > *rs,
CompareOp::Ge => ls >= *rs,
});
}
Err(VMError::TypeError(format!(
"[BoxRef-String] Unsupported comparison: {:?} on {:?} and {:?}",
op, left, right
)))
} }
// Mixed Integer (BoxRef vs Integer) // Mixed Integer (BoxRef vs Integer)
(VMValue::BoxRef(li), VMValue::Integer(r)) => { (VMValue::BoxRef(li), VMValue::Integer(r)) => {
let l_opt = li.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value) let l_opt = li
.or_else(|| li.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().map(|x| x.value)) .as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| {
li.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.or_else(|| li.to_string_box().value.parse::<i64>().ok()); .or_else(|| li.to_string_box().value.parse::<i64>().ok());
if let Some(l) = l_opt { return Ok(match op { CompareOp::Eq => l == *r, CompareOp::Ne => l != *r, CompareOp::Lt => l < *r, CompareOp::Le => l <= *r, CompareOp::Gt => l > *r, CompareOp::Ge => l >= *r }); } if let Some(l) = l_opt {
Err(VMError::TypeError(format!("[BoxRef-Integer] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))) return Ok(match op {
CompareOp::Eq => l == *r,
CompareOp::Ne => l != *r,
CompareOp::Lt => l < *r,
CompareOp::Le => l <= *r,
CompareOp::Gt => l > *r,
CompareOp::Ge => l >= *r,
});
}
Err(VMError::TypeError(format!(
"[BoxRef-Integer] Unsupported comparison: {:?} on {:?} and {:?}",
op, left, right
)))
} }
(VMValue::Integer(l), VMValue::BoxRef(ri)) => { (VMValue::Integer(l), VMValue::BoxRef(ri)) => {
let r_opt = ri.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value) let r_opt = ri
.or_else(|| ri.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().map(|x| x.value)) .as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| {
ri.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.or_else(|| ri.to_string_box().value.parse::<i64>().ok()); .or_else(|| ri.to_string_box().value.parse::<i64>().ok());
if let Some(r) = r_opt { return Ok(match op { CompareOp::Eq => *l == r, CompareOp::Ne => *l != r, CompareOp::Lt => *l < r, CompareOp::Le => *l <= r, CompareOp::Gt => *l > r, CompareOp::Ge => *l >= r }); } if let Some(r) = r_opt {
Err(VMError::TypeError(format!("[Integer-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))) return Ok(match op {
CompareOp::Eq => *l == r,
CompareOp::Ne => *l != r,
CompareOp::Lt => *l < r,
CompareOp::Le => *l <= r,
CompareOp::Gt => *l > r,
CompareOp::Ge => *l >= r,
});
}
Err(VMError::TypeError(format!(
"[Integer-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}",
op, left, right
)))
} }
_ => { _ => {
// 80/20 numeric fallback: coerce via to_string when possible // 80/20 numeric fallback: coerce via to_string when possible
@ -395,15 +864,34 @@ impl VM {
} }
} }
if let (Some(l), Some(r)) = (to_i64(left), to_i64(right)) { if let (Some(l), Some(r)) = (to_i64(left), to_i64(right)) {
return Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r }); return Ok(match op {
CompareOp::Eq => l == r,
CompareOp::Ne => l != r,
CompareOp::Lt => l < r,
CompareOp::Le => l <= r,
CompareOp::Gt => l > r,
CompareOp::Ge => l >= r,
});
} }
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
let lty = match left { VMValue::BoxRef(b) => format!("BoxRef({})", b.type_name()), other => format!("{:?}", other) }; let lty = match left {
let rty = match right { VMValue::BoxRef(b) => format!("BoxRef({})", b.type_name()), other => format!("{:?}", other) }; VMValue::BoxRef(b) => format!("BoxRef({})", b.type_name()),
eprintln!("[VM] compare default arm: op={:?}, left={}, right={}", op, lty, rty); other => format!("{:?}", other),
};
let rty = match right {
VMValue::BoxRef(b) => format!("BoxRef({})", b.type_name()),
other => format!("{:?}", other),
};
eprintln!(
"[VM] compare default arm: op={:?}, left={}, right={}",
op, lty, rty
);
}
Err(VMError::TypeError(format!(
"[Default] Unsupported comparison: {:?} on {:?} and {:?}",
op, left, right
)))
} }
Err(VMError::TypeError(format!("[Default] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
},
} }
} }
} }

View File

@ -5,8 +5,10 @@
* Phase 8.3 PoC2: Reference operations (RefNew/RefGet/RefSet) * Phase 8.3 PoC2: Reference operations (RefNew/RefGet/RefSet)
*/ */
use crate::mir::{MirModule, MirFunction, MirInstruction, ConstValue, BinaryOp, CompareOp, ValueId, BasicBlockId}; use super::{MemoryManager, RuntimeImports, WasmError};
use super::{WasmError, MemoryManager, RuntimeImports}; use crate::mir::{
BasicBlockId, BinaryOp, CompareOp, ConstValue, MirFunction, MirInstruction, MirModule, ValueId,
};
use std::collections::HashMap; use std::collections::HashMap;
/// WASM module representation for WAT generation /// WASM module representation for WAT generation
@ -96,7 +98,7 @@ impl WasmCodegen {
&mut self, &mut self,
mir_module: MirModule, mir_module: MirModule,
memory_manager: &MemoryManager, memory_manager: &MemoryManager,
runtime: &RuntimeImports runtime: &RuntimeImports,
) -> Result<WasmModule, WasmError> { ) -> Result<WasmModule, WasmError> {
let mut wasm_module = WasmModule::new(); let mut wasm_module = WasmModule::new();
@ -110,8 +112,12 @@ impl WasmCodegen {
wasm_module.globals.extend(memory_manager.get_globals()); wasm_module.globals.extend(memory_manager.get_globals());
// Add memory management functions // Add memory management functions
wasm_module.functions.push(memory_manager.get_malloc_function()); wasm_module
wasm_module.functions.push(memory_manager.get_generic_box_alloc_function()); .functions
.push(memory_manager.get_malloc_function());
wasm_module
.functions
.push(memory_manager.get_generic_box_alloc_function());
// Add Box-specific allocation functions for known types // Add Box-specific allocation functions for known types
for box_type in ["StringBox", "IntegerBox", "BoolBox", "DataBox"] { for box_type in ["StringBox", "IntegerBox", "BoolBox", "DataBox"] {
@ -127,18 +133,26 @@ impl WasmCodegen {
} }
// Add string literal data segments // Add string literal data segments
wasm_module.data_segments.extend(self.generate_data_segments()); wasm_module
.data_segments
.extend(self.generate_data_segments());
// Add main function export if it exists // Add main function export if it exists
if mir_module.functions.contains_key("main") { if mir_module.functions.contains_key("main") {
wasm_module.exports.push("(export \"main\" (func $main))".to_string()); wasm_module
.exports
.push("(export \"main\" (func $main))".to_string());
} }
Ok(wasm_module) Ok(wasm_module)
} }
/// Generate WASM function from MIR function /// Generate WASM function from MIR function
fn generate_function(&mut self, name: &str, mir_function: MirFunction) -> Result<String, WasmError> { fn generate_function(
&mut self,
name: &str,
mir_function: MirFunction,
) -> Result<String, WasmError> {
// Reset local variable tracking for this function // Reset local variable tracking for this function
self.current_locals.clear(); self.current_locals.clear();
self.next_local_index = 0; self.next_local_index = 0;
@ -150,10 +164,13 @@ impl WasmCodegen {
match mir_function.signature.return_type { match mir_function.signature.return_type {
crate::mir::MirType::Integer => function_body.push_str(" (result i32)"), crate::mir::MirType::Integer => function_body.push_str(" (result i32)"),
crate::mir::MirType::Bool => function_body.push_str(" (result i32)"), crate::mir::MirType::Bool => function_body.push_str(" (result i32)"),
crate::mir::MirType::Void => {}, // No return type crate::mir::MirType::Void => {} // No return type
_ => return Err(WasmError::UnsupportedInstruction( _ => {
format!("Unsupported return type: {:?}", mir_function.signature.return_type) return Err(WasmError::UnsupportedInstruction(format!(
)), "Unsupported return type: {:?}",
mir_function.signature.return_type
)))
}
} }
// Collect all local variables needed // Collect all local variables needed
@ -168,7 +185,8 @@ impl WasmCodegen {
function_body.push('\n'); function_body.push('\n');
// Generate body from entry block // Generate body from entry block
let entry_instructions = self.generate_basic_block(&mir_function, mir_function.entry_block)?; let entry_instructions =
self.generate_basic_block(&mir_function, mir_function.entry_block)?;
for instruction in entry_instructions { for instruction in entry_instructions {
function_body.push_str(&format!(" {}\n", instruction)); function_body.push_str(&format!(" {}\n", instruction));
} }
@ -203,9 +221,14 @@ impl WasmCodegen {
} }
/// Generate WASM instructions for a basic block /// Generate WASM instructions for a basic block
fn generate_basic_block(&mut self, mir_function: &MirFunction, block_id: BasicBlockId) -> Result<Vec<String>, WasmError> { fn generate_basic_block(
let block = mir_function.blocks.get(&block_id) &mut self,
.ok_or_else(|| WasmError::CodegenError(format!("Basic block {:?} not found", block_id)))?; mir_function: &MirFunction,
block_id: BasicBlockId,
) -> Result<Vec<String>, WasmError> {
let block = mir_function.blocks.get(&block_id).ok_or_else(|| {
WasmError::CodegenError(format!("Basic block {:?} not found", block_id))
})?;
let mut instructions = Vec::new(); let mut instructions = Vec::new();
@ -225,28 +248,25 @@ impl WasmCodegen {
} }
/// Generate WASM instructions for a single MIR instruction /// Generate WASM instructions for a single MIR instruction
fn generate_instruction(&mut self, instruction: &MirInstruction) -> Result<Vec<String>, WasmError> { fn generate_instruction(
&mut self,
instruction: &MirInstruction,
) -> Result<Vec<String>, WasmError> {
match instruction { match instruction {
// Phase 8.2 PoC1: Basic operations // Phase 8.2 PoC1: Basic operations
MirInstruction::Const { dst, value } => { MirInstruction::Const { dst, value } => self.generate_const(*dst, value),
self.generate_const(*dst, value)
},
MirInstruction::BinOp { dst, op, lhs, rhs } => { MirInstruction::BinOp { dst, op, lhs, rhs } => {
self.generate_binop(*dst, *op, *lhs, *rhs) self.generate_binop(*dst, *op, *lhs, *rhs)
}, }
MirInstruction::Compare { dst, op, lhs, rhs } => { MirInstruction::Compare { dst, op, lhs, rhs } => {
self.generate_compare(*dst, *op, *lhs, *rhs) self.generate_compare(*dst, *op, *lhs, *rhs)
}, }
MirInstruction::Return { value } => { MirInstruction::Return { value } => self.generate_return(value.as_ref()),
self.generate_return(value.as_ref())
},
MirInstruction::Print { value, .. } => { MirInstruction::Print { value, .. } => self.generate_print(*value),
self.generate_print(*value)
},
// Phase 8.3 PoC2: Reference operations // Phase 8.3 PoC2: Reference operations
MirInstruction::RefNew { dst, box_val } => { MirInstruction::RefNew { dst, box_val } => {
@ -256,9 +276,13 @@ impl WasmCodegen {
format!("local.get ${}", self.get_local_index(*box_val)?), format!("local.get ${}", self.get_local_index(*box_val)?),
format!("local.set ${}", self.get_local_index(*dst)?), format!("local.set ${}", self.get_local_index(*dst)?),
]) ])
}, }
MirInstruction::RefGet { dst, reference, field: _ } => { MirInstruction::RefGet {
dst,
reference,
field: _,
} => {
// Load field value from Box through reference // Load field value from Box through reference
// reference contains Box pointer, field is the field name // reference contains Box pointer, field is the field name
// For now, assume all fields are at offset 12 (first field after header) // For now, assume all fields are at offset 12 (first field after header)
@ -270,9 +294,13 @@ impl WasmCodegen {
"i32.load".to_string(), "i32.load".to_string(),
format!("local.set ${}", self.get_local_index(*dst)?), format!("local.set ${}", self.get_local_index(*dst)?),
]) ])
}, }
MirInstruction::RefSet { reference, field: _, value } => { MirInstruction::RefSet {
reference,
field: _,
value,
} => {
// Store field value to Box through reference // Store field value to Box through reference
// reference contains Box pointer, field is the field name, value is new value // reference contains Box pointer, field is the field name, value is new value
// For now, assume all fields are at offset 12 (first field after header) // For now, assume all fields are at offset 12 (first field after header)
@ -284,9 +312,13 @@ impl WasmCodegen {
format!("local.get ${}", self.get_local_index(*value)?), format!("local.get ${}", self.get_local_index(*value)?),
"i32.store".to_string(), "i32.store".to_string(),
]) ])
}, }
MirInstruction::NewBox { dst, box_type, args } => { MirInstruction::NewBox {
dst,
box_type,
args,
} => {
// Create a new Box using the generic allocator // Create a new Box using the generic allocator
match box_type.as_str() { match box_type.as_str() {
"DataBox" => { "DataBox" => {
@ -308,7 +340,7 @@ impl WasmCodegen {
} }
Ok(instructions) Ok(instructions)
}, }
_ => { _ => {
// Use generic allocator for unknown types // Use generic allocator for unknown types
// This is a fallback - in a real implementation, all Box types should be known // This is a fallback - in a real implementation, all Box types should be known
@ -320,45 +352,53 @@ impl WasmCodegen {
]) ])
} }
} }
}, }
// Phase 8.4 PoC3: Extension stubs // Phase 8.4 PoC3: Extension stubs
MirInstruction::WeakNew { dst, box_val } | MirInstruction::WeakNew { dst, box_val }
MirInstruction::FutureNew { dst, value: box_val } => { | MirInstruction::FutureNew {
dst,
value: box_val,
} => {
// Treat as regular reference for now // Treat as regular reference for now
Ok(vec![ Ok(vec![
format!("local.get ${}", self.get_local_index(*box_val)?), format!("local.get ${}", self.get_local_index(*box_val)?),
format!("local.set ${}", self.get_local_index(*dst)?), format!("local.set ${}", self.get_local_index(*dst)?),
]) ])
}, }
MirInstruction::WeakLoad { dst, weak_ref } | MirInstruction::WeakLoad { dst, weak_ref }
MirInstruction::Await { dst, future: weak_ref } => { | MirInstruction::Await {
dst,
future: weak_ref,
} => {
// Always succeed for now // Always succeed for now
Ok(vec![ Ok(vec![
format!("local.get ${}", self.get_local_index(*weak_ref)?), format!("local.get ${}", self.get_local_index(*weak_ref)?),
format!("local.set ${}", self.get_local_index(*dst)?), format!("local.set ${}", self.get_local_index(*dst)?),
]) ])
}, }
MirInstruction::BarrierRead { .. } | MirInstruction::BarrierRead { .. }
MirInstruction::BarrierWrite { .. } | | MirInstruction::BarrierWrite { .. }
MirInstruction::FutureSet { .. } | | MirInstruction::FutureSet { .. }
MirInstruction::Safepoint => { | MirInstruction::Safepoint => {
// No-op for now // No-op for now
Ok(vec!["nop".to_string()]) Ok(vec!["nop".to_string()])
}, }
// Control Flow Instructions (Critical for loops and conditions) // Control Flow Instructions (Critical for loops and conditions)
MirInstruction::Jump { target } => { MirInstruction::Jump { target } => {
// Unconditional jump to target basic block // Unconditional jump to target basic block
// Use WASM br instruction to break to the target block // Use WASM br instruction to break to the target block
Ok(vec![ Ok(vec![format!("br $block_{}", target.as_u32())])
format!("br $block_{}", target.as_u32()), }
])
},
MirInstruction::Branch { condition, then_bb, else_bb } => { MirInstruction::Branch {
condition,
then_bb,
else_bb,
} => {
// Conditional branch based on condition value // Conditional branch based on condition value
// Load condition value and branch accordingly // Load condition value and branch accordingly
Ok(vec![ Ok(vec![
@ -369,18 +409,27 @@ impl WasmCodegen {
// Otherwise, fall through to else_bb // Otherwise, fall through to else_bb
format!("br $block_{}", else_bb.as_u32()), format!("br $block_{}", else_bb.as_u32()),
]) ])
}, }
// Phase 9.7: External Function Calls // Phase 9.7: External Function Calls
MirInstruction::ExternCall { dst, iface_name, method_name, args, effects: _ } => { MirInstruction::ExternCall {
dst,
iface_name,
method_name,
args,
effects: _,
} => {
// Generate call to external function import // Generate call to external function import
let call_target = match (iface_name.as_str(), method_name.as_str()) { let call_target = match (iface_name.as_str(), method_name.as_str()) {
("env.console", "log") => "console_log", ("env.console", "log") => "console_log",
("env.canvas", "fillRect") => "canvas_fillRect", ("env.canvas", "fillRect") => "canvas_fillRect",
("env.canvas", "fillText") => "canvas_fillText", ("env.canvas", "fillText") => "canvas_fillText",
_ => return Err(WasmError::UnsupportedInstruction( _ => {
format!("Unsupported extern call: {}.{}", iface_name, method_name) return Err(WasmError::UnsupportedInstruction(format!(
)), "Unsupported extern call: {}.{}",
iface_name, method_name
)))
}
}; };
let mut instructions = Vec::new(); let mut instructions = Vec::new();
@ -401,22 +450,32 @@ impl WasmCodegen {
} }
Ok(instructions) Ok(instructions)
}, }
// BoxCall codegen - critical Box method calls // BoxCall codegen - critical Box method calls
MirInstruction::BoxCall { dst, box_val, method, args, effects: _ , .. } => { MirInstruction::BoxCall {
self.generate_box_call(*dst, *box_val, method, args) dst,
}, box_val,
method,
args,
effects: _,
..
} => self.generate_box_call(*dst, *box_val, method, args),
// Unsupported instructions // Unsupported instructions
_ => Err(WasmError::UnsupportedInstruction( _ => Err(WasmError::UnsupportedInstruction(format!(
format!("Instruction not yet supported: {:?}", instruction) "Instruction not yet supported: {:?}",
)), instruction
))),
} }
} }
/// Generate constant loading /// Generate constant loading
fn generate_const(&mut self, dst: ValueId, value: &ConstValue) -> Result<Vec<String>, WasmError> { fn generate_const(
&mut self,
dst: ValueId,
value: &ConstValue,
) -> Result<Vec<String>, WasmError> {
let const_instruction = match value { let const_instruction = match value {
ConstValue::Integer(n) => format!("i32.const {}", n), ConstValue::Integer(n) => format!("i32.const {}", n),
ConstValue::Bool(b) => format!("i32.const {}", if *b { 1 } else { 0 }), ConstValue::Bool(b) => format!("i32.const {}", if *b { 1 } else { 0 }),
@ -429,10 +488,13 @@ impl WasmCodegen {
// Generate code to allocate a StringBox and return its pointer // Generate code to allocate a StringBox and return its pointer
// This is more complex and will need StringBox allocation // This is more complex and will need StringBox allocation
return self.generate_string_box_const(dst, data_offset, string_len); return self.generate_string_box_const(dst, data_offset, string_len);
}, }
_ => return Err(WasmError::UnsupportedInstruction( _ => {
format!("Unsupported constant type: {:?}", value) return Err(WasmError::UnsupportedInstruction(format!(
)), "Unsupported constant type: {:?}",
value
)))
}
}; };
Ok(vec![ Ok(vec![
@ -442,7 +504,13 @@ impl WasmCodegen {
} }
/// Generate binary operation /// Generate binary operation
fn generate_binop(&self, dst: ValueId, op: BinaryOp, lhs: ValueId, rhs: ValueId) -> Result<Vec<String>, WasmError> { fn generate_binop(
&self,
dst: ValueId,
op: BinaryOp,
lhs: ValueId,
rhs: ValueId,
) -> Result<Vec<String>, WasmError> {
let wasm_op = match op { let wasm_op = match op {
BinaryOp::Add => "i32.add", BinaryOp::Add => "i32.add",
BinaryOp::Sub => "i32.sub", BinaryOp::Sub => "i32.sub",
@ -450,9 +518,12 @@ impl WasmCodegen {
BinaryOp::Div => "i32.div_s", BinaryOp::Div => "i32.div_s",
BinaryOp::And => "i32.and", BinaryOp::And => "i32.and",
BinaryOp::Or => "i32.or", BinaryOp::Or => "i32.or",
_ => return Err(WasmError::UnsupportedInstruction( _ => {
format!("Unsupported binary operation: {:?}", op) return Err(WasmError::UnsupportedInstruction(format!(
)), "Unsupported binary operation: {:?}",
op
)))
}
}; };
Ok(vec![ Ok(vec![
@ -464,7 +535,13 @@ impl WasmCodegen {
} }
/// Generate comparison operation /// Generate comparison operation
fn generate_compare(&self, dst: ValueId, op: CompareOp, lhs: ValueId, rhs: ValueId) -> Result<Vec<String>, WasmError> { fn generate_compare(
&self,
dst: ValueId,
op: CompareOp,
lhs: ValueId,
rhs: ValueId,
) -> Result<Vec<String>, WasmError> {
let wasm_op = match op { let wasm_op = match op {
CompareOp::Eq => "i32.eq", CompareOp::Eq => "i32.eq",
CompareOp::Ne => "i32.ne", CompareOp::Ne => "i32.ne",
@ -495,7 +572,12 @@ impl WasmCodegen {
} }
/// Generate StringBox allocation for a string constant /// Generate StringBox allocation for a string constant
fn generate_string_box_const(&self, dst: ValueId, data_offset: u32, string_len: u32) -> Result<Vec<String>, WasmError> { fn generate_string_box_const(
&self,
dst: ValueId,
data_offset: u32,
string_len: u32,
) -> Result<Vec<String>, WasmError> {
// Allocate a StringBox using the StringBox allocator // Allocate a StringBox using the StringBox allocator
// StringBox layout: [type_id:0x1001][ref_count:1][field_count:2][data_ptr:offset][length:len] // StringBox layout: [type_id:0x1001][ref_count:1][field_count:2][data_ptr:offset][length:len]
Ok(vec![ Ok(vec![
@ -503,7 +585,6 @@ impl WasmCodegen {
"call $alloc_stringbox".to_string(), "call $alloc_stringbox".to_string(),
// Store the result (StringBox pointer) in local variable // Store the result (StringBox pointer) in local variable
format!("local.set ${}", self.get_local_index(dst)?), format!("local.set ${}", self.get_local_index(dst)?),
// Initialize StringBox fields // Initialize StringBox fields
// Get StringBox pointer back // Get StringBox pointer back
format!("local.get ${}", self.get_local_index(dst)?), format!("local.get ${}", self.get_local_index(dst)?),
@ -512,7 +593,6 @@ impl WasmCodegen {
"i32.add".to_string(), "i32.add".to_string(),
format!("i32.const {}", data_offset), format!("i32.const {}", data_offset),
"i32.store".to_string(), "i32.store".to_string(),
// Get StringBox pointer again // Get StringBox pointer again
format!("local.get ${}", self.get_local_index(dst)?), format!("local.get ${}", self.get_local_index(dst)?),
// Set length field (offset 16 from StringBox pointer) // Set length field (offset 16 from StringBox pointer)
@ -553,15 +633,12 @@ impl WasmCodegen {
let string_bytes = string.as_bytes(); let string_bytes = string.as_bytes();
// Convert to hex-escaped string for WAT // Convert to hex-escaped string for WAT
let byte_string = string_bytes.iter() let byte_string = string_bytes
.iter()
.map(|b| format!("\\{:02x}", b)) .map(|b| format!("\\{:02x}", b))
.collect::<String>(); .collect::<String>();
let data_segment = format!( let data_segment = format!("(data (i32.const {}) \"{}\")", offset, byte_string);
"(data (i32.const {}) \"{}\")",
offset,
byte_string
);
segments.push(data_segment); segments.push(data_segment);
} }
@ -571,34 +648,53 @@ impl WasmCodegen {
/// Get WASM local variable index for ValueId /// Get WASM local variable index for ValueId
fn get_local_index(&self, value_id: ValueId) -> Result<u32, WasmError> { fn get_local_index(&self, value_id: ValueId) -> Result<u32, WasmError> {
self.current_locals.get(&value_id) self.current_locals.get(&value_id).copied().ok_or_else(|| {
.copied() WasmError::CodegenError(format!(
.ok_or_else(|| WasmError::CodegenError(format!("Local variable not found for ValueId: {:?}", value_id))) "Local variable not found for ValueId: {:?}",
value_id
))
})
} }
/// Generate BoxCall method invocation /// Generate BoxCall method invocation
/// Implements critical Box methods: toString, print, equals, clone /// Implements critical Box methods: toString, print, equals, clone
fn generate_box_call(&mut self, dst: Option<ValueId>, box_val: ValueId, method: &str, args: &[ValueId]) -> Result<Vec<String>, WasmError> { fn generate_box_call(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
method: &str,
args: &[ValueId],
) -> Result<Vec<String>, WasmError> {
match method { match method {
"toString" => self.generate_to_string_call(dst, box_val), "toString" => self.generate_to_string_call(dst, box_val),
"print" => self.generate_print_call(dst, box_val), "print" => self.generate_print_call(dst, box_val),
"equals" => self.generate_equals_call(dst, box_val, args), "equals" => self.generate_equals_call(dst, box_val, args),
"clone" => self.generate_clone_call(dst, box_val), "clone" => self.generate_clone_call(dst, box_val),
"log" => self.generate_log_call(dst, box_val, args), "log" => self.generate_log_call(dst, box_val, args),
_ => Err(WasmError::UnsupportedInstruction( _ => Err(WasmError::UnsupportedInstruction(format!(
format!("Unsupported BoxCall method: {}", method) "Unsupported BoxCall method: {}",
)) method
))),
} }
} }
/// Generate toString() method call - Box → String conversion /// Generate toString() method call - Box → String conversion
fn generate_to_string_call(&mut self, dst: Option<ValueId>, box_val: ValueId) -> Result<Vec<String>, WasmError> { fn generate_to_string_call(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
) -> Result<Vec<String>, WasmError> {
let Some(dst) = dst else { let Some(dst) = dst else {
return Err(WasmError::CodegenError("toString() requires destination".to_string())); return Err(WasmError::CodegenError(
"toString() requires destination".to_string(),
));
}; };
Ok(vec![ Ok(vec![
format!(";; toString() implementation for ValueId({})", box_val.as_u32()), format!(
";; toString() implementation for ValueId({})",
box_val.as_u32()
),
format!("local.get ${}", self.get_local_index(box_val)?), format!("local.get ${}", self.get_local_index(box_val)?),
"call $box_to_string".to_string(), "call $box_to_string".to_string(),
format!("local.set ${}", self.get_local_index(dst)?), format!("local.set ${}", self.get_local_index(dst)?),
@ -606,9 +702,16 @@ impl WasmCodegen {
} }
/// Generate print() method call - Basic output /// Generate print() method call - Basic output
fn generate_print_call(&mut self, dst: Option<ValueId>, box_val: ValueId) -> Result<Vec<String>, WasmError> { fn generate_print_call(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
) -> Result<Vec<String>, WasmError> {
let mut instructions = vec![ let mut instructions = vec![
format!(";; print() implementation for ValueId({})", box_val.as_u32()), format!(
";; print() implementation for ValueId({})",
box_val.as_u32()
),
format!("local.get ${}", self.get_local_index(box_val)?), format!("local.get ${}", self.get_local_index(box_val)?),
"call $box_print".to_string(), "call $box_print".to_string(),
]; ];
@ -625,19 +728,31 @@ impl WasmCodegen {
} }
/// Generate equals() method call - Box comparison /// Generate equals() method call - Box comparison
fn generate_equals_call(&mut self, dst: Option<ValueId>, box_val: ValueId, args: &[ValueId]) -> Result<Vec<String>, WasmError> { fn generate_equals_call(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
args: &[ValueId],
) -> Result<Vec<String>, WasmError> {
let Some(dst) = dst else { let Some(dst) = dst else {
return Err(WasmError::CodegenError("equals() requires destination".to_string())); return Err(WasmError::CodegenError(
"equals() requires destination".to_string(),
));
}; };
if args.len() != 1 { if args.len() != 1 {
return Err(WasmError::CodegenError( return Err(WasmError::CodegenError(format!(
format!("equals() expects 1 argument, got {}", args.len()) "equals() expects 1 argument, got {}",
)); args.len()
)));
} }
Ok(vec![ Ok(vec![
format!(";; equals() implementation for ValueId({}) == ValueId({})", box_val.as_u32(), args[0].as_u32()), format!(
";; equals() implementation for ValueId({}) == ValueId({})",
box_val.as_u32(),
args[0].as_u32()
),
format!("local.get ${}", self.get_local_index(box_val)?), format!("local.get ${}", self.get_local_index(box_val)?),
format!("local.get ${}", self.get_local_index(args[0])?), format!("local.get ${}", self.get_local_index(args[0])?),
"call $box_equals".to_string(), "call $box_equals".to_string(),
@ -646,13 +761,22 @@ impl WasmCodegen {
} }
/// Generate clone() method call - Box duplication /// Generate clone() method call - Box duplication
fn generate_clone_call(&mut self, dst: Option<ValueId>, box_val: ValueId) -> Result<Vec<String>, WasmError> { fn generate_clone_call(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
) -> Result<Vec<String>, WasmError> {
let Some(dst) = dst else { let Some(dst) = dst else {
return Err(WasmError::CodegenError("clone() requires destination".to_string())); return Err(WasmError::CodegenError(
"clone() requires destination".to_string(),
));
}; };
Ok(vec![ Ok(vec![
format!(";; clone() implementation for ValueId({})", box_val.as_u32()), format!(
";; clone() implementation for ValueId({})",
box_val.as_u32()
),
format!("local.get ${}", self.get_local_index(box_val)?), format!("local.get ${}", self.get_local_index(box_val)?),
"call $box_clone".to_string(), "call $box_clone".to_string(),
format!("local.set ${}", self.get_local_index(dst)?), format!("local.set ${}", self.get_local_index(dst)?),
@ -660,10 +784,16 @@ impl WasmCodegen {
} }
/// Generate log() method call - Console logging (ConsoleBox.log) /// Generate log() method call - Console logging (ConsoleBox.log)
fn generate_log_call(&mut self, dst: Option<ValueId>, box_val: ValueId, args: &[ValueId]) -> Result<Vec<String>, WasmError> { fn generate_log_call(
let mut instructions = vec![ &mut self,
format!(";; log() implementation for ValueId({})", box_val.as_u32()), dst: Option<ValueId>,
]; box_val: ValueId,
args: &[ValueId],
) -> Result<Vec<String>, WasmError> {
let mut instructions = vec![format!(
";; log() implementation for ValueId({})",
box_val.as_u32()
)];
// Load box_val (ConsoleBox instance) // Load box_val (ConsoleBox instance)
instructions.push(format!("local.get ${}", self.get_local_index(box_val)?)); instructions.push(format!("local.get ${}", self.get_local_index(box_val)?));
@ -691,13 +821,18 @@ impl WasmCodegen {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirType, EffectMask, BasicBlock, BasicBlockId, ValueId}; use crate::mir::{
BasicBlock, BasicBlockId, EffectMask, FunctionSignature, MirFunction, MirModule, MirType,
ValueId,
};
#[test] #[test]
fn test_wasm_module_wat_generation() { fn test_wasm_module_wat_generation() {
let mut module = WasmModule::new(); let mut module = WasmModule::new();
module.memory = "(memory (export \"memory\") 1)".to_string(); module.memory = "(memory (export \"memory\") 1)".to_string();
module.imports.push("(import \"env\" \"print\" (func $print (param i32)))".to_string()); module
.imports
.push("(import \"env\" \"print\" (func $print (param i32)))".to_string());
let wat = module.to_wat(); let wat = module.to_wat();
assert!(wat.contains("(module")); assert!(wat.contains("(module"));

View File

@ -72,7 +72,10 @@ impl MemoryManager {
/// Register standard built-in Box types /// Register standard built-in Box types
fn register_standard_box_types(&mut self) { fn register_standard_box_types(&mut self) {
// StringBox: [type_id][ref_count][field_count][ptr_to_chars][length] // StringBox: [type_id][ref_count][field_count][ptr_to_chars][length]
self.register_box_type("StringBox".to_string(), vec!["data_ptr".to_string(), "length".to_string()]); self.register_box_type(
"StringBox".to_string(),
vec!["data_ptr".to_string(), "length".to_string()],
);
// IntegerBox: [type_id][ref_count][field_count][value] // IntegerBox: [type_id][ref_count][field_count][value]
self.register_box_type("IntegerBox".to_string(), vec!["value".to_string()]); self.register_box_type("IntegerBox".to_string(), vec!["value".to_string()]);
@ -102,9 +105,10 @@ impl MemoryManager {
/// Generate WASM globals for heap management /// Generate WASM globals for heap management
pub fn get_globals(&self) -> Vec<String> { pub fn get_globals(&self) -> Vec<String> {
vec![ vec![format!(
format!("(global $heap_ptr (mut i32) (i32.const {}))", self.heap_start), "(global $heap_ptr (mut i32) (i32.const {}))",
] self.heap_start
)]
} }
/// Generate heap allocation function with 4-byte alignment /// Generate heap allocation function with 4-byte alignment
@ -140,7 +144,8 @@ impl MemoryManager {
/// Generate Box allocation function for specific type /// Generate Box allocation function for specific type
pub fn get_box_alloc_function(&self, type_name: &str) -> Result<String, WasmError> { pub fn get_box_alloc_function(&self, type_name: &str) -> Result<String, WasmError> {
let layout = self.get_box_layout(type_name) let layout = self
.get_box_layout(type_name)
.ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?; .ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?;
Ok(format!( Ok(format!(
@ -182,12 +187,18 @@ impl MemoryManager {
} }
/// Generate field getter function /// Generate field getter function
pub fn get_field_get_function(&self, type_name: &str, field_name: &str) -> Result<String, WasmError> { pub fn get_field_get_function(
let layout = self.get_box_layout(type_name) &self,
type_name: &str,
field_name: &str,
) -> Result<String, WasmError> {
let layout = self
.get_box_layout(type_name)
.ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?; .ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?;
let offset = layout.get_field_offset(field_name) let offset = layout.get_field_offset(field_name).ok_or_else(|| {
.ok_or_else(|| WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name)))?; WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name))
})?;
Ok(format!( Ok(format!(
r#"(func $get_{}_{} (param $box_ptr i32) (result i32) r#"(func $get_{}_{} (param $box_ptr i32) (result i32)
@ -215,12 +226,18 @@ impl MemoryManager {
} }
/// Generate field setter function /// Generate field setter function
pub fn get_field_set_function(&self, type_name: &str, field_name: &str) -> Result<String, WasmError> { pub fn get_field_set_function(
let layout = self.get_box_layout(type_name) &self,
type_name: &str,
field_name: &str,
) -> Result<String, WasmError> {
let layout = self
.get_box_layout(type_name)
.ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?; .ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?;
let offset = layout.get_field_offset(field_name) let offset = layout.get_field_offset(field_name).ok_or_else(|| {
.ok_or_else(|| WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name)))?; WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name))
})?;
Ok(format!( Ok(format!(
r#"(func $set_{}_{} (param $box_ptr i32) (param $value i32) r#"(func $set_{}_{} (param $box_ptr i32) (param $value i32)
@ -362,7 +379,10 @@ mod tests {
#[test] #[test]
fn test_memory_manager_registration() { fn test_memory_manager_registration() {
let mut manager = MemoryManager::new(); let mut manager = MemoryManager::new();
manager.register_box_type("CustomBox".to_string(), vec!["x".to_string(), "y".to_string()]); manager.register_box_type(
"CustomBox".to_string(),
vec!["x".to_string(), "y".to_string()],
);
let layout = manager.get_box_layout("CustomBox").unwrap(); let layout = manager.get_box_layout("CustomBox").unwrap();
assert_eq!(layout.field_offsets.len(), 2); assert_eq!(layout.field_offsets.len(), 2);

View File

@ -11,7 +11,7 @@ mod runtime;
// mod executor; // TODO: Fix WASM executor build errors // mod executor; // TODO: Fix WASM executor build errors
pub use codegen::{WasmCodegen, WasmModule}; pub use codegen::{WasmCodegen, WasmModule};
pub use memory::{MemoryManager, BoxLayout}; pub use memory::{BoxLayout, MemoryManager};
pub use runtime::RuntimeImports; pub use runtime::RuntimeImports;
// pub use executor::WasmExecutor; // TODO: Fix WASM executor build errors // pub use executor::WasmExecutor; // TODO: Fix WASM executor build errors
@ -77,7 +77,7 @@ impl WasmBackend {
if !wat_source.is_ascii() { if !wat_source.is_ascii() {
eprintln!("❌ WAT source contains non-ASCII characters"); eprintln!("❌ WAT source contains non-ASCII characters");
return Err(WasmError::WasmValidationError( return Err(WasmError::WasmValidationError(
"WAT source contains non-ASCII characters".to_string() "WAT source contains non-ASCII characters".to_string(),
)); ));
} }
@ -85,27 +85,32 @@ impl WasmBackend {
// Convert to bytes as required by wabt::wat2wasm // Convert to bytes as required by wabt::wat2wasm
eprintln!("🔄 Converting WAT to WASM bytes..."); eprintln!("🔄 Converting WAT to WASM bytes...");
let wasm_bytes = wabt::wat2wasm(wat_source.as_bytes()) let wasm_bytes = wabt::wat2wasm(wat_source.as_bytes()).map_err(|e| {
.map_err(|e| {
eprintln!("❌ wabt::wat2wasm failed: {}", e); eprintln!("❌ wabt::wat2wasm failed: {}", e);
WasmError::WasmValidationError(format!("WAT to WASM conversion failed: {}", e)) WasmError::WasmValidationError(format!("WAT to WASM conversion failed: {}", e))
})?; })?;
eprintln!("✅ WASM conversion successful, {} bytes generated", wasm_bytes.len()); eprintln!(
"✅ WASM conversion successful, {} bytes generated",
wasm_bytes.len()
);
Ok(wasm_bytes) Ok(wasm_bytes)
} }
/// Compile MIR module to WAT text format (for debugging) /// Compile MIR module to WAT text format (for debugging)
pub fn compile_to_wat(&mut self, mir_module: MirModule) -> Result<String, WasmError> { pub fn compile_to_wat(&mut self, mir_module: MirModule) -> Result<String, WasmError> {
let wasm_module = self.codegen.generate_module(mir_module, &self.memory_manager, &self.runtime)?; let wasm_module =
self.codegen
.generate_module(mir_module, &self.memory_manager, &self.runtime)?;
Ok(wasm_module.to_wat()) Ok(wasm_module.to_wat())
} }
/// Execute WASM bytes using wasmtime (for testing) /// Execute WASM bytes using wasmtime (for testing)
pub fn execute_wasm(&self, wasm_bytes: &[u8]) -> Result<i32, WasmError> { pub fn execute_wasm(&self, wasm_bytes: &[u8]) -> Result<i32, WasmError> {
let engine = wasmtime::Engine::default(); let engine = wasmtime::Engine::default();
let module = wasmtime::Module::new(&engine, wasm_bytes) let module = wasmtime::Module::new(&engine, wasm_bytes).map_err(|e| {
.map_err(|e| WasmError::WasmValidationError(format!("Module creation failed: {}", e)))?; WasmError::WasmValidationError(format!("Module creation failed: {}", e))
})?;
let mut store = wasmtime::Store::new(&engine, ()); let mut store = wasmtime::Store::new(&engine, ());
@ -115,8 +120,14 @@ impl WasmBackend {
}); });
// Create print_str function import for string debugging // Create print_str function import for string debugging
let print_str_func = wasmtime::Func::wrap(&mut store, |mut caller: wasmtime::Caller<'_, ()>, ptr: i32, len: i32| -> Result<(), wasmtime::Error> { let print_str_func = wasmtime::Func::wrap(
let memory = caller.get_export("memory") &mut store,
|mut caller: wasmtime::Caller<'_, ()>,
ptr: i32,
len: i32|
-> Result<(), wasmtime::Error> {
let memory = caller
.get_export("memory")
.and_then(|export| export.into_memory()) .and_then(|export| export.into_memory())
.ok_or_else(|| wasmtime::Error::msg("Memory export not found"))?; .ok_or_else(|| wasmtime::Error::msg("Memory export not found"))?;
@ -132,21 +143,32 @@ impl WasmBackend {
println!("Invalid UTF-8 bytes: {:?}", bytes); println!("Invalid UTF-8 bytes: {:?}", bytes);
} }
} else { } else {
println!("String out of bounds: ptr={}, len={}, memory_size={}", ptr, len, data.len()); println!(
"String out of bounds: ptr={}, len={}, memory_size={}",
ptr,
len,
data.len()
);
} }
Ok(()) Ok(())
}); },
);
let imports = [print_func.into(), print_str_func.into()]; let imports = [print_func.into(), print_str_func.into()];
let instance = wasmtime::Instance::new(&mut store, &module, &imports) let instance = wasmtime::Instance::new(&mut store, &module, &imports).map_err(|e| {
.map_err(|e| WasmError::WasmValidationError(format!("Instance creation failed: {}", e)))?; WasmError::WasmValidationError(format!("Instance creation failed: {}", e))
})?;
// Call main function // Call main function
let main_func = instance.get_typed_func::<(), i32>(&mut store, "main") let main_func = instance
.map_err(|e| WasmError::WasmValidationError(format!("Main function not found: {}", e)))?; .get_typed_func::<(), i32>(&mut store, "main")
.map_err(|e| {
WasmError::WasmValidationError(format!("Main function not found: {}", e))
})?;
let result = main_func.call(&mut store, ()) let result = main_func
.call(&mut store, ())
.map_err(|e| WasmError::WasmValidationError(format!("Execution failed: {}", e)))?; .map_err(|e| WasmError::WasmValidationError(format!("Execution failed: {}", e)))?;
Ok(result) Ok(result)

View File

@ -67,9 +67,14 @@ impl RuntimeImports {
module: "env".to_string(), module: "env".to_string(),
name: "canvas_fillRect".to_string(), name: "canvas_fillRect".to_string(),
params: vec![ params: vec![
"i32".to_string(), "i32".to_string(), // canvas_id (ptr, len) "i32".to_string(),
"i32".to_string(), "i32".to_string(), "i32".to_string(), "i32".to_string(), // x, y, w, h "i32".to_string(), // canvas_id (ptr, len)
"i32".to_string(), "i32".to_string(), // color (ptr, len) "i32".to_string(),
"i32".to_string(),
"i32".to_string(),
"i32".to_string(), // x, y, w, h
"i32".to_string(),
"i32".to_string(), // color (ptr, len)
], ],
result: None, result: None,
}); });
@ -80,11 +85,16 @@ impl RuntimeImports {
module: "env".to_string(), module: "env".to_string(),
name: "canvas_fillText".to_string(), name: "canvas_fillText".to_string(),
params: vec![ params: vec![
"i32".to_string(), "i32".to_string(), // canvas_id (ptr, len) "i32".to_string(),
"i32".to_string(), "i32".to_string(), // text (ptr, len) "i32".to_string(), // canvas_id (ptr, len)
"i32".to_string(), "i32".to_string(), // x, y "i32".to_string(),
"i32".to_string(), "i32".to_string(), // font (ptr, len) "i32".to_string(), // text (ptr, len)
"i32".to_string(), "i32".to_string(), // color (ptr, len) "i32".to_string(),
"i32".to_string(), // x, y
"i32".to_string(),
"i32".to_string(), // font (ptr, len)
"i32".to_string(),
"i32".to_string(), // color (ptr, len)
], ],
result: None, result: None,
}); });
@ -129,7 +139,9 @@ impl RuntimeImports {
/// Get all import declarations in WAT format /// Get all import declarations in WAT format
pub fn get_imports(&self) -> Vec<String> { pub fn get_imports(&self) -> Vec<String> {
self.imports.iter().map(|import| { self.imports
.iter()
.map(|import| {
let params = if import.params.is_empty() { let params = if import.params.is_empty() {
String::new() String::new()
} else { } else {
@ -144,17 +156,20 @@ impl RuntimeImports {
format!( format!(
"(import \"{}\" \"{}\" (func ${} {} {}))", "(import \"{}\" \"{}\" (func ${} {} {}))",
import.module, import.module, import.name, import.name, params, result
import.name,
import.name,
params,
result
) )
}).collect() })
.collect()
} }
/// Add custom import function /// Add custom import function
pub fn add_import(&mut self, module: String, name: String, params: Vec<String>, result: Option<String>) { pub fn add_import(
&mut self,
module: String,
name: String,
params: Vec<String>,
result: Option<String>,
) {
self.imports.push(ImportFunction { self.imports.push(ImportFunction {
module, module,
name, name,
@ -179,9 +194,13 @@ impl RuntimeImports {
js.push_str("const importObject = {\n"); js.push_str("const importObject = {\n");
// Group by module // Group by module
let mut modules: std::collections::HashMap<String, Vec<&ImportFunction>> = std::collections::HashMap::new(); let mut modules: std::collections::HashMap<String, Vec<&ImportFunction>> =
std::collections::HashMap::new();
for import in &self.imports { for import in &self.imports {
modules.entry(import.module.clone()).or_default().push(import); modules
.entry(import.module.clone())
.or_default()
.push(import);
} }
for (module_name, functions) in modules { for (module_name, functions) in modules {
@ -191,21 +210,21 @@ impl RuntimeImports {
match function.name.as_str() { match function.name.as_str() {
"print" => { "print" => {
js.push_str(" print: (value) => console.log(value),\n"); js.push_str(" print: (value) => console.log(value),\n");
}, }
"print_str" => { "print_str" => {
js.push_str(" print_str: (ptr, len) => {\n"); js.push_str(" print_str: (ptr, len) => {\n");
js.push_str(" const memory = instance.exports.memory;\n"); js.push_str(" const memory = instance.exports.memory;\n");
js.push_str(" const str = new TextDecoder().decode(new Uint8Array(memory.buffer, ptr, len));\n"); js.push_str(" const str = new TextDecoder().decode(new Uint8Array(memory.buffer, ptr, len));\n");
js.push_str(" console.log(str);\n"); js.push_str(" console.log(str);\n");
js.push_str(" },\n"); js.push_str(" },\n");
}, }
"console_log" => { "console_log" => {
js.push_str(" console_log: (ptr, len) => {\n"); js.push_str(" console_log: (ptr, len) => {\n");
js.push_str(" const memory = instance.exports.memory;\n"); js.push_str(" const memory = instance.exports.memory;\n");
js.push_str(" const str = new TextDecoder().decode(new Uint8Array(memory.buffer, ptr, len));\n"); js.push_str(" const str = new TextDecoder().decode(new Uint8Array(memory.buffer, ptr, len));\n");
js.push_str(" console.log(str);\n"); js.push_str(" console.log(str);\n");
js.push_str(" },\n"); js.push_str(" },\n");
}, }
"canvas_fillRect" => { "canvas_fillRect" => {
js.push_str(" canvas_fillRect: (canvasIdPtr, canvasIdLen, x, y, w, h, colorPtr, colorLen) => {\n"); js.push_str(" canvas_fillRect: (canvasIdPtr, canvasIdLen, x, y, w, h, colorPtr, colorLen) => {\n");
js.push_str(" const memory = instance.exports.memory;\n"); js.push_str(" const memory = instance.exports.memory;\n");
@ -218,7 +237,7 @@ impl RuntimeImports {
js.push_str(" ctx.fillRect(x, y, w, h);\n"); js.push_str(" ctx.fillRect(x, y, w, h);\n");
js.push_str(" }\n"); js.push_str(" }\n");
js.push_str(" },\n"); js.push_str(" },\n");
}, }
"canvas_fillText" => { "canvas_fillText" => {
js.push_str(" canvas_fillText: (canvasIdPtr, canvasIdLen, textPtr, textLen, x, y, fontPtr, fontLen, colorPtr, colorLen) => {\n"); js.push_str(" canvas_fillText: (canvasIdPtr, canvasIdLen, textPtr, textLen, x, y, fontPtr, fontLen, colorPtr, colorLen) => {\n");
js.push_str(" const memory = instance.exports.memory;\n"); js.push_str(" const memory = instance.exports.memory;\n");
@ -234,10 +253,12 @@ impl RuntimeImports {
js.push_str(" ctx.fillText(text, x, y);\n"); js.push_str(" ctx.fillText(text, x, y);\n");
js.push_str(" }\n"); js.push_str(" }\n");
js.push_str(" },\n"); js.push_str(" },\n");
}, }
_ => { _ => {
js.push_str(&format!(" {}: () => {{ throw new Error('Not implemented: {}'); }},\n", js.push_str(&format!(
function.name, function.name)); " {}: () => {{ throw new Error('Not implemented: {}'); }},\n",
function.name, function.name
));
} }
} }
} }
@ -258,21 +279,26 @@ impl RuntimeImports {
for import in &self.imports { for import in &self.imports {
match import.name.as_str() { match import.name.as_str() {
"print" => { "print" => {
rust_code.push_str(r#" rust_code.push_str(
r#"
let print_func = wasmtime::Func::wrap(&mut store, |value: i32| { let print_func = wasmtime::Func::wrap(&mut store, |value: i32| {
println!("{}", value); println!("{}", value);
}); });
imports.push(print_func.into()); imports.push(print_func.into());
"#); "#,
}, );
}
_ => { _ => {
rust_code.push_str(&format!(r#" rust_code.push_str(&format!(
r#"
// TODO: Implement {} import // TODO: Implement {} import
let {}_func = wasmtime::Func::wrap(&mut store, || {{ let {}_func = wasmtime::Func::wrap(&mut store, || {{
panic!("Not implemented: {}") panic!("Not implemented: {}")
}}); }});
imports.push({}_func.into()); imports.push({}_func.into());
"#, import.name, import.name, import.name, import.name)); "#,
import.name, import.name, import.name, import.name
));
} }
} }
} }
@ -310,7 +336,7 @@ mod tests {
"custom".to_string(), "custom".to_string(),
"test_func".to_string(), "test_func".to_string(),
vec!["i32".to_string(), "i32".to_string()], vec!["i32".to_string(), "i32".to_string()],
Some("i32".to_string()) Some("i32".to_string()),
); );
assert!(runtime.has_import("test_func")); assert!(runtime.has_import("test_func"));

View File

@ -13,14 +13,20 @@ use crate::box_trait::{NyashBox, StringBox};
use crate::boxes::ConsoleBox; use crate::boxes::ConsoleBox;
/// WASM v2エントリポイント: 統一vtableディスパッチの最小テスト /// WASM v2エントリポイント: 統一vtableディスパッチの最小テスト
pub fn compile_and_execute_v2(_module: &crate::mir::MirModule, _temp_name: &str) -> Result<Box<dyn crate::box_trait::NyashBox>, String> { pub fn compile_and_execute_v2(
_module: &crate::mir::MirModule,
_temp_name: &str,
) -> Result<Box<dyn crate::box_trait::NyashBox>, String> {
// 1) ConsoleBoxを生成WASM環境ではブラウザコンソールに委譲 // 1) ConsoleBoxを生成WASM環境ではブラウザコンソールに委譲
let console = Box::new(ConsoleBox::new()); let console = Box::new(ConsoleBox::new());
// 2) slot解決→dispatchでlogを呼ぶ最小疎通 // 2) slot解決→dispatchでlogを呼ぶ最小疎通
if let Some(slot_id) = unified_dispatch::resolve_slot(console.as_ref(), "log", 1) { if let Some(slot_id) = unified_dispatch::resolve_slot(console.as_ref(), "log", 1) {
let args = vec![Box::new(StringBox::new("🎉 WASM v2 console.log working!")) as Box<dyn NyashBox>]; let args =
vec![Box::new(StringBox::new("🎉 WASM v2 console.log working!")) as Box<dyn NyashBox>];
let _ = unified_dispatch::dispatch_by_slot(slot_id, console.as_ref(), &args); let _ = unified_dispatch::dispatch_by_slot(slot_id, console.as_ref(), &args);
} }
// 3) 結果を返す // 3) 結果を返す
Ok(Box::new(StringBox::new("WASM v2 unified dispatch test completed"))) Ok(Box::new(StringBox::new(
"WASM v2 unified dispatch test completed",
)))
} }

View File

@ -5,8 +5,8 @@
#![cfg(feature = "wasm-backend")] #![cfg(feature = "wasm-backend")]
use crate::box_trait::{NyashBox, StringBox, VoidBox, BoolBox}; use crate::box_trait::{BoolBox, NyashBox, StringBox, VoidBox};
use crate::boxes::{ConsoleBox, ArrayBox, MapBox}; use crate::boxes::{ArrayBox, ConsoleBox, MapBox};
/// 受信ボックス/メソッド名/アリティからスロットを解決し、識別子を返す。 /// 受信ボックス/メソッド名/アリティからスロットを解決し、識別子を返す。
pub fn resolve_slot(recv: &dyn NyashBox, method: &str, arity: usize) -> Option<u16> { pub fn resolve_slot(recv: &dyn NyashBox, method: &str, arity: usize) -> Option<u16> {
@ -114,6 +114,6 @@ pub fn dispatch_by_slot(
None None
} }
_ => None _ => None,
} }
} }

View File

@ -13,6 +13,8 @@ pub struct GeneratedVTableInfo {
pub fn generate_tables() -> GeneratedVTableInfo { pub fn generate_tables() -> GeneratedVTableInfo {
// 未実装: TypeRegistry::resolve_typebox_by_name()/methods を走査して集計 // 未実装: TypeRegistry::resolve_typebox_by_name()/methods を走査して集計
GeneratedVTableInfo { types: 0, methods: 0 } GeneratedVTableInfo {
types: 0,
methods: 0,
}
} }

View File

@ -7,14 +7,14 @@
* - WASM (MIR -> WASM execution) * - WASM (MIR -> WASM execution)
*/ */
use std::time::Instant;
use std::fs;
use crate::parser::NyashParser;
use crate::interpreter::NyashInterpreter;
use crate::mir::MirCompiler;
use crate::backend::VM;
#[cfg(feature = "wasm-backend")] #[cfg(feature = "wasm-backend")]
use crate::backend::WasmBackend; use crate::backend::WasmBackend;
use crate::backend::VM;
use crate::interpreter::NyashInterpreter;
use crate::mir::MirCompiler;
use crate::parser::NyashParser;
use std::fs;
use std::time::Instant;
#[derive(Debug)] #[derive(Debug)]
pub struct BenchmarkResult { pub struct BenchmarkResult {
@ -71,7 +71,11 @@ impl BenchmarkSuite {
} }
/// Run benchmark on interpreter backend /// Run benchmark on interpreter backend
fn run_interpreter_benchmark(&self, name: &str, source: &str) -> Result<BenchmarkResult, Box<dyn std::error::Error>> { fn run_interpreter_benchmark(
&self,
name: &str,
source: &str,
) -> Result<BenchmarkResult, Box<dyn std::error::Error>> {
let mut total_duration = 0.0; let mut total_duration = 0.0;
for i in 0..self.iterations { for i in 0..self.iterations {
@ -100,7 +104,11 @@ impl BenchmarkSuite {
} }
/// Run benchmark on VM backend /// Run benchmark on VM backend
fn run_vm_benchmark(&self, name: &str, source: &str) -> Result<BenchmarkResult, Box<dyn std::error::Error>> { fn run_vm_benchmark(
&self,
name: &str,
source: &str,
) -> Result<BenchmarkResult, Box<dyn std::error::Error>> {
let mut total_duration = 0.0; let mut total_duration = 0.0;
for i in 0..self.iterations { for i in 0..self.iterations {
@ -132,7 +140,11 @@ impl BenchmarkSuite {
/// Run benchmark on WASM backend /// Run benchmark on WASM backend
#[cfg(feature = "wasm-backend")] #[cfg(feature = "wasm-backend")]
fn run_wasm_benchmark(&self, name: &str, source: &str) -> Result<BenchmarkResult, Box<dyn std::error::Error>> { fn run_wasm_benchmark(
&self,
name: &str,
source: &str,
) -> Result<BenchmarkResult, Box<dyn std::error::Error>> {
let mut total_duration = 0.0; let mut total_duration = 0.0;
for i in 0..self.iterations { for i in 0..self.iterations {
@ -174,9 +186,13 @@ impl BenchmarkSuite {
println!(); println!();
// Group by benchmark name // Group by benchmark name
let mut benchmarks: std::collections::HashMap<String, Vec<&BenchmarkResult>> = std::collections::HashMap::new(); let mut benchmarks: std::collections::HashMap<String, Vec<&BenchmarkResult>> =
std::collections::HashMap::new();
for result in results { for result in results {
benchmarks.entry(result.name.clone()).or_insert_with(Vec::new).push(result); benchmarks
.entry(result.name.clone())
.or_insert_with(Vec::new)
.push(result);
} }
for (bench_name, bench_results) in benchmarks { for (bench_name, bench_results) in benchmarks {
@ -185,15 +201,16 @@ impl BenchmarkSuite {
println!(" --------------|---------------|-----------------|------------"); println!(" --------------|---------------|-----------------|------------");
// Find fastest for ratio calculation // Find fastest for ratio calculation
let fastest = bench_results.iter().min_by(|a, b| a.avg_duration_ms.partial_cmp(&b.avg_duration_ms).unwrap()).unwrap(); let fastest = bench_results
.iter()
.min_by(|a, b| a.avg_duration_ms.partial_cmp(&b.avg_duration_ms).unwrap())
.unwrap();
for result in &bench_results { for result in &bench_results {
let ratio = result.avg_duration_ms / fastest.avg_duration_ms; let ratio = result.avg_duration_ms / fastest.avg_duration_ms;
println!(" {:12} | {:11.3} | {:13.1} | {:8.2}x", println!(
result.backend, " {:12} | {:11.3} | {:13.1} | {:8.2}x",
result.avg_duration_ms, result.backend, result.avg_duration_ms, result.duration_ms, ratio
result.duration_ms,
ratio
); );
} }
println!(); println!();
@ -201,25 +218,42 @@ impl BenchmarkSuite {
// Summary // Summary
println!("💡 Performance Summary:"); println!("💡 Performance Summary:");
let interpreter_avg: f64 = results.iter() let interpreter_avg: f64 = results
.iter()
.filter(|r| r.backend == "Interpreter") .filter(|r| r.backend == "Interpreter")
.map(|r| r.avg_duration_ms) .map(|r| r.avg_duration_ms)
.sum::<f64>() / results.iter().filter(|r| r.backend == "Interpreter").count() as f64; .sum::<f64>()
/ results
.iter()
.filter(|r| r.backend == "Interpreter")
.count() as f64;
let vm_avg: f64 = results.iter() let vm_avg: f64 = results
.iter()
.filter(|r| r.backend == "VM") .filter(|r| r.backend == "VM")
.map(|r| r.avg_duration_ms) .map(|r| r.avg_duration_ms)
.sum::<f64>() / results.iter().filter(|r| r.backend == "VM").count() as f64; .sum::<f64>()
/ results.iter().filter(|r| r.backend == "VM").count() as f64;
let wasm_avg: f64 = results.iter() let wasm_avg: f64 = results
.iter()
.filter(|r| r.backend == "WASM") .filter(|r| r.backend == "WASM")
.map(|r| r.avg_duration_ms) .map(|r| r.avg_duration_ms)
.sum::<f64>() / results.iter().filter(|r| r.backend == "WASM").count() as f64; .sum::<f64>()
/ results.iter().filter(|r| r.backend == "WASM").count() as f64;
println!(" 📈 Average across all benchmarks:"); println!(" 📈 Average across all benchmarks:");
println!(" Interpreter: {:.2} ms", interpreter_avg); println!(" Interpreter: {:.2} ms", interpreter_avg);
println!(" VM: {:.2} ms ({:.1}x faster than interpreter)", vm_avg, interpreter_avg / vm_avg); println!(
println!(" WASM: {:.2} ms ({:.1}x faster than interpreter)", wasm_avg, interpreter_avg / wasm_avg); " VM: {:.2} ms ({:.1}x faster than interpreter)",
vm_avg,
interpreter_avg / vm_avg
);
println!(
" WASM: {:.2} ms ({:.1}x faster than interpreter)",
wasm_avg,
interpreter_avg / wasm_avg
);
} }
} }

View File

@ -1,7 +1,7 @@
use super::{BidError, BidHandle, BidType};
use crate::box_trait::NyashBox; use crate::box_trait::NyashBox;
use super::{BidHandle, BidType, BidError};
use std::sync::Arc;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
/// BID-FFI Bridge for Nyash Box types /// BID-FFI Bridge for Nyash Box types
/// Provides conversion between Nyash runtime values and BID handles /// Provides conversion between Nyash runtime values and BID handles
@ -78,24 +78,51 @@ pub fn box_to_bid_handle(
registry: &mut BoxRegistry, registry: &mut BoxRegistry,
) -> Result<(BidType, BidHandle), BidError> { ) -> Result<(BidType, BidHandle), BidError> {
// Downcast to specific box types // Downcast to specific box types
if let Some(_string_box) = arc_box.as_any().downcast_ref::<crate::boxes::string_box::StringBox>() { if let Some(_string_box) = arc_box
.as_any()
.downcast_ref::<crate::boxes::string_box::StringBox>()
{
let handle = registry.register_box( let handle = registry.register_box(
crate::bid::types::BoxTypeId::StringBox as u32, crate::bid::types::BoxTypeId::StringBox as u32,
arc_box.clone() arc_box.clone(),
); );
Ok((BidType::Handle { type_id: 1, instance_id: handle.instance_id }, handle)) Ok((
} else if let Some(_integer_box) = arc_box.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>() { BidType::Handle {
type_id: 1,
instance_id: handle.instance_id,
},
handle,
))
} else if let Some(_integer_box) = arc_box
.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
{
let handle = registry.register_box( let handle = registry.register_box(
crate::bid::types::BoxTypeId::IntegerBox as u32, crate::bid::types::BoxTypeId::IntegerBox as u32,
arc_box.clone() arc_box.clone(),
); );
Ok((BidType::Handle { type_id: 2, instance_id: handle.instance_id }, handle)) Ok((
} else if let Some(_future_box) = arc_box.as_any().downcast_ref::<crate::boxes::future::NyashFutureBox>() { BidType::Handle {
type_id: 2,
instance_id: handle.instance_id,
},
handle,
))
} else if let Some(_future_box) = arc_box
.as_any()
.downcast_ref::<crate::boxes::future::NyashFutureBox>()
{
let handle = registry.register_box( let handle = registry.register_box(
crate::bid::types::BoxTypeId::FutureBox as u32, crate::bid::types::BoxTypeId::FutureBox as u32,
arc_box.clone() arc_box.clone(),
); );
Ok((BidType::Handle { type_id: 7, instance_id: handle.instance_id }, handle)) Ok((
BidType::Handle {
type_id: 7,
instance_id: handle.instance_id,
},
handle,
))
} else { } else {
Err(BidError::InvalidType) Err(BidError::InvalidType)
} }
@ -106,13 +133,15 @@ pub fn bid_handle_to_box(
handle: BidHandle, handle: BidHandle,
registry: &BoxRegistry, registry: &BoxRegistry,
) -> Result<Arc<dyn NyashBox>, BidError> { ) -> Result<Arc<dyn NyashBox>, BidError> {
registry.get_box(handle) registry.get_box(handle).ok_or(BidError::InvalidHandle)
.ok_or(BidError::InvalidHandle)
} }
/// Extract string value from a Box for TLV encoding /// Extract string value from a Box for TLV encoding
pub fn extract_string_value(arc_box: &Arc<dyn NyashBox>) -> Result<String, BidError> { pub fn extract_string_value(arc_box: &Arc<dyn NyashBox>) -> Result<String, BidError> {
if let Some(string_box) = arc_box.as_any().downcast_ref::<crate::boxes::string_box::StringBox>() { if let Some(string_box) = arc_box
.as_any()
.downcast_ref::<crate::boxes::string_box::StringBox>()
{
Ok(string_box.value.clone()) Ok(string_box.value.clone())
} else { } else {
Err(BidError::InvalidType) Err(BidError::InvalidType)
@ -121,7 +150,10 @@ pub fn extract_string_value(arc_box: &Arc<dyn NyashBox>) -> Result<String, BidEr
/// Extract integer value from a Box for TLV encoding /// Extract integer value from a Box for TLV encoding
pub fn extract_integer_value(arc_box: &Arc<dyn NyashBox>) -> Result<i64, BidError> { pub fn extract_integer_value(arc_box: &Arc<dyn NyashBox>) -> Result<i64, BidError> {
if let Some(integer_box) = arc_box.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>() { if let Some(integer_box) = arc_box
.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
{
Ok(integer_box.value) Ok(integer_box.value)
} else { } else {
Err(BidError::InvalidType) Err(BidError::InvalidType)
@ -226,10 +258,16 @@ mod tests {
let retrieved = bid_handle_to_box(handle, &registry).unwrap(); let retrieved = bid_handle_to_box(handle, &registry).unwrap();
// Verify it's still a FutureBox // Verify it's still a FutureBox
assert!(retrieved.as_any().downcast_ref::<crate::boxes::future::NyashFutureBox>().is_some()); assert!(retrieved
.as_any()
.downcast_ref::<crate::boxes::future::NyashFutureBox>()
.is_some());
// Test with result set // Test with result set
if let Some(future) = arc_box.as_any().downcast_ref::<crate::boxes::future::NyashFutureBox>() { if let Some(future) = arc_box
.as_any()
.downcast_ref::<crate::boxes::future::NyashFutureBox>()
{
let string_result = crate::boxes::string_box::StringBox::new("Future Result"); let string_result = crate::boxes::string_box::StringBox::new("Future Result");
future.set_result(Box::new(string_result)); future.set_result(Box::new(string_result));

View File

@ -2,7 +2,7 @@
mod plugin_impl { mod plugin_impl {
use crate::bid::{BidError, BidResult, LoadedPlugin}; use crate::bid::{BidError, BidResult, LoadedPlugin};
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any; use std::any::Any;
use std::fmt; use std::fmt;
@ -43,7 +43,10 @@ impl GenericPluginBox {
/// 汎用メソッド呼び出し /// 汎用メソッド呼び出し
pub fn call_method(&self, method_name: &str, args: &[u8]) -> BidResult<Vec<u8>> { pub fn call_method(&self, method_name: &str, args: &[u8]) -> BidResult<Vec<u8>> {
eprintln!("🔍 GenericPluginBox::call_method '{}' on {}", method_name, self.box_name); eprintln!(
"🔍 GenericPluginBox::call_method '{}' on {}",
method_name, self.box_name
);
// プラグインからメソッドIDを動的取得 // プラグインからメソッドIDを動的取得
match self.plugin.find_method(method_name) { match self.plugin.find_method(method_name) {
@ -56,7 +59,7 @@ impl GenericPluginBox {
method_id, method_id,
self.instance_id, self.instance_id,
args, args,
&mut out &mut out,
)?; )?;
Ok(out) Ok(out)
@ -65,7 +68,7 @@ impl GenericPluginBox {
eprintln!("❌ Method '{}' not found in {}", method_name, self.box_name); eprintln!("❌ Method '{}' not found in {}", method_name, self.box_name);
Err(BidError::InvalidArgs) Err(BidError::InvalidArgs)
} }
Err(e) => Err(e) Err(e) => Err(e),
} }
} }
} }
@ -84,13 +87,21 @@ impl Drop for GenericPluginBox {
} }
impl BoxCore for GenericPluginBox { impl BoxCore for GenericPluginBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_box(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}(plugin)", self.box_name) write!(f, "{}(plugin)", self.box_name)
} }
fn as_any(&self) -> &dyn Any { self } fn as_any(&self) -> &dyn Any {
fn as_any_mut(&mut self) -> &mut dyn Any { self } self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl NyashBox for GenericPluginBox { impl NyashBox for GenericPluginBox {
@ -101,8 +112,8 @@ impl NyashBox for GenericPluginBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_plugin) = other.as_any().downcast_ref::<GenericPluginBox>() { if let Some(other_plugin) = other.as_any().downcast_ref::<GenericPluginBox>() {
BoolBox::new( BoolBox::new(
self.box_name == other_plugin.box_name && self.box_name == other_plugin.box_name
self.instance_id == other_plugin.instance_id && self.instance_id == other_plugin.instance_id,
) )
} else { } else {
BoolBox::new(false) BoolBox::new(false)
@ -122,7 +133,11 @@ impl NyashBox for GenericPluginBox {
impl fmt::Debug for GenericPluginBox { impl fmt::Debug for GenericPluginBox {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "GenericPluginBox({}, instance={})", self.box_name, self.instance_id) write!(
f,
"GenericPluginBox({}, instance={})",
self.box_name, self.instance_id
)
} }
} }
@ -131,7 +146,6 @@ impl fmt::Display for GenericPluginBox {
self.fmt_box(f) self.fmt_box(f)
} }
} }
} // mod plugin_impl } // mod plugin_impl
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]

View File

@ -1,4 +1,7 @@
use crate::bid::{BidError, BidResult, NyashHostVtable, NyashPluginInfo, PluginHandle, PLUGIN_ABI_SYMBOL, PLUGIN_INIT_SYMBOL, PLUGIN_INVOKE_SYMBOL, PLUGIN_SHUTDOWN_SYMBOL}; use crate::bid::{
BidError, BidResult, NyashHostVtable, NyashPluginInfo, PluginHandle, PLUGIN_ABI_SYMBOL,
PLUGIN_INIT_SYMBOL, PLUGIN_INVOKE_SYMBOL, PLUGIN_SHUTDOWN_SYMBOL,
};
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
use libloading::{Library, Symbol}; use libloading::{Library, Symbol};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -15,18 +18,21 @@ impl LoadedPlugin {
/// Load a plugin dynamic library from file path and initialize it /// Load a plugin dynamic library from file path and initialize it
pub fn load_from_file(path: &Path) -> BidResult<Self> { pub fn load_from_file(path: &Path) -> BidResult<Self> {
// Load library // Load library
let library = unsafe { Library::new(path) } let library = unsafe { Library::new(path) }.map_err(|_| BidError::PluginError)?;
.map_err(|_| BidError::PluginError)?;
// Resolve symbols // Resolve symbols
unsafe { unsafe {
let abi: Symbol<unsafe extern "C" fn() -> u32> = library let abi: Symbol<unsafe extern "C" fn() -> u32> = library
.get(PLUGIN_ABI_SYMBOL.as_bytes()) .get(PLUGIN_ABI_SYMBOL.as_bytes())
.map_err(|_| BidError::PluginError)?; .map_err(|_| BidError::PluginError)?;
let init: Symbol<unsafe extern "C" fn(*const NyashHostVtable, *mut NyashPluginInfo) -> i32> = library let init: Symbol<
unsafe extern "C" fn(*const NyashHostVtable, *mut NyashPluginInfo) -> i32,
> = library
.get(PLUGIN_INIT_SYMBOL.as_bytes()) .get(PLUGIN_INIT_SYMBOL.as_bytes())
.map_err(|_| BidError::PluginError)?; .map_err(|_| BidError::PluginError)?;
let invoke: Symbol<unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32> = library let invoke: Symbol<
unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
> = library
.get(PLUGIN_INVOKE_SYMBOL.as_bytes()) .get(PLUGIN_INVOKE_SYMBOL.as_bytes())
.map_err(|_| BidError::PluginError)?; .map_err(|_| BidError::PluginError)?;
let shutdown: Symbol<unsafe extern "C" fn()> = library let shutdown: Symbol<unsafe extern "C" fn()> = library
@ -49,7 +55,12 @@ impl LoadedPlugin {
handle.initialize(&host, &mut info)?; handle.initialize(&host, &mut info)?;
let type_id = info.type_id; let type_id = info.type_id;
Ok(Self { library, handle, type_id, plugin_info: info }) Ok(Self {
library,
handle,
type_id,
plugin_info: info,
})
} }
} }
@ -111,7 +122,12 @@ static HOST_VTABLE_STORAGE: std::sync::LazyLock<NyashHostVtable> = std::sync::La
} }
} }
NyashHostVtable { alloc: host_alloc, free: host_free, wake: host_wake, log: host_log } NyashHostVtable {
alloc: host_alloc,
free: host_free,
wake: host_wake,
log: host_log,
}
}); });
/// Build a minimal host vtable for plugins /// Build a minimal host vtable for plugins

View File

@ -1,6 +1,6 @@
use super::{BidError, BidResult}; use super::{BidError, BidResult};
use std::os::raw::{c_char};
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::os::raw::c_char;
/// Host function table provided to plugins /// Host function table provided to plugins
#[repr(C)] #[repr(C)]
@ -14,11 +14,18 @@ pub struct NyashHostVtable {
impl NyashHostVtable { impl NyashHostVtable {
/// Create a vtable with no-op stubs (for tests) /// Create a vtable with no-op stubs (for tests)
pub fn empty() -> Self { pub fn empty() -> Self {
unsafe extern "C" fn a(_size: usize) -> *mut u8 { std::ptr::null_mut() } unsafe extern "C" fn a(_size: usize) -> *mut u8 {
std::ptr::null_mut()
}
unsafe extern "C" fn f(_ptr: *mut u8) {} unsafe extern "C" fn f(_ptr: *mut u8) {}
unsafe extern "C" fn w(_h: u64) {} unsafe extern "C" fn w(_h: u64) {}
unsafe extern "C" fn l(_level: i32, _m: *const c_char) {} unsafe extern "C" fn l(_level: i32, _m: *const c_char) {}
Self { alloc: a, free: f, wake: w, log: l } Self {
alloc: a,
free: f,
wake: w,
log: l,
}
} }
} }
@ -42,9 +49,12 @@ unsafe impl Sync for NyashMethodInfo {}
impl NyashMethodInfo { impl NyashMethodInfo {
/// Create method info with safe string handling /// Create method info with safe string handling
pub fn new(method_id: u32, method_name: &str, signature_hash: u32) -> BidResult<(Self, CString)> { pub fn new(
let c_name = CString::new(method_name) method_id: u32,
.map_err(|_| BidError::InvalidUtf8)?; method_name: &str,
signature_hash: u32,
) -> BidResult<(Self, CString)> {
let c_name = CString::new(method_name).map_err(|_| BidError::InvalidUtf8)?;
let info = Self { let info = Self {
method_id, method_id,
@ -159,8 +169,7 @@ impl PluginMetadata {
methods: Vec<(u32, &str, u32)>, // (id, name, hash) methods: Vec<(u32, &str, u32)>, // (id, name, hash)
) -> BidResult<Self> { ) -> BidResult<Self> {
// Create type name // Create type name
let type_name_holder = CString::new(type_name) let type_name_holder = CString::new(type_name).map_err(|_| BidError::InvalidUtf8)?;
.map_err(|_| BidError::InvalidUtf8)?;
// Create method infos // Create method infos
let mut method_holders = Vec::new(); let mut method_holders = Vec::new();
@ -221,10 +230,17 @@ mod tests {
#[test] #[test]
fn test_host_vtable() { fn test_host_vtable() {
unsafe extern "C" fn a(_size: usize) -> *mut u8 { std::ptr::null_mut() } unsafe extern "C" fn a(_size: usize) -> *mut u8 {
std::ptr::null_mut()
}
unsafe extern "C" fn f(_p: *mut u8) {} unsafe extern "C" fn f(_p: *mut u8) {}
unsafe extern "C" fn w(_h: u64) {} unsafe extern "C" fn w(_h: u64) {}
unsafe extern "C" fn l(_level: i32, _m: *const c_char) {} unsafe extern "C" fn l(_level: i32, _m: *const c_char) {}
let _v = NyashHostVtable { alloc: a, free: f, wake: w, log: l }; let _v = NyashHostVtable {
alloc: a,
free: f,
wake: w,
log: l,
};
} }
} }

View File

@ -1,27 +1,27 @@
// BID-FFI: Box Interface Definition with Foreign Function Interface // BID-FFI: Box Interface Definition with Foreign Function Interface
// Everything is Box philosophy meets practical FFI/ABI design! // Everything is Box philosophy meets practical FFI/ABI design!
pub mod types;
pub mod tlv;
pub mod error;
pub mod metadata;
pub mod plugin_api;
pub mod bridge; pub mod bridge;
pub mod plugins; pub mod error;
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
pub mod loader; pub mod loader;
pub mod metadata;
pub mod plugin_api;
pub mod plugins;
pub mod tlv;
pub mod types;
// pub mod registry; // legacy - v2 plugin system uses BoxFactoryRegistry instead // pub mod registry; // legacy - v2 plugin system uses BoxFactoryRegistry instead
// pub mod plugin_box; // legacy - FileBox専用実装 // pub mod plugin_box; // legacy - FileBox専用実装
pub mod generic_plugin_box; pub mod generic_plugin_box;
pub use types::*;
pub use tlv::*;
pub use error::*;
pub use metadata::*;
pub use plugin_api::*;
pub use bridge::*; pub use bridge::*;
pub use error::*;
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
pub use loader::*; pub use loader::*;
pub use metadata::*;
pub use plugin_api::*;
pub use tlv::*;
pub use types::*;
// pub use registry::*; // legacy - v2 plugin system uses BoxFactoryRegistry instead // pub use registry::*; // legacy - v2 plugin system uses BoxFactoryRegistry instead
// pub use plugin_box::*; // legacy // pub use plugin_box::*; // legacy
pub use generic_plugin_box::*; pub use generic_plugin_box::*;

View File

@ -14,10 +14,8 @@ pub type PluginAbiFn = unsafe extern "C" fn() -> u32;
/// - host: Host function table for plugin to use /// - host: Host function table for plugin to use
/// - info: Plugin information to be filled by plugin /// - info: Plugin information to be filled by plugin
/// Returns: 0 on success, negative error code on failure /// Returns: 0 on success, negative error code on failure
pub type PluginInitFn = unsafe extern "C" fn( pub type PluginInitFn =
host: *const NyashHostVtable, unsafe extern "C" fn(host: *const NyashHostVtable, info: *mut NyashPluginInfo) -> i32;
info: *mut NyashPluginInfo,
) -> i32;
/// Invoke a plugin method /// Invoke a plugin method
/// Parameters: /// Parameters:
@ -67,17 +65,9 @@ impl PluginHandle {
} }
/// Initialize plugin with host vtable /// Initialize plugin with host vtable
pub fn initialize( pub fn initialize(&self, host: &NyashHostVtable, info: &mut NyashPluginInfo) -> BidResult<()> {
&self, let result =
host: &NyashHostVtable, unsafe { (self.init)(host as *const NyashHostVtable, info as *mut NyashPluginInfo) };
info: &mut NyashPluginInfo,
) -> BidResult<()> {
let result = unsafe {
(self.init)(
host as *const NyashHostVtable,
info as *mut NyashPluginInfo,
)
};
if result != 0 { if result != 0 {
Err(BidError::from_raw(result)) Err(BidError::from_raw(result))

View File

@ -44,9 +44,17 @@ impl FileBoxRegistry {
pub fn open(&mut self, path: &str, mode: FileMode) -> Result<BidHandle, std::io::Error> { pub fn open(&mut self, path: &str, mode: FileMode) -> Result<BidHandle, std::io::Error> {
let file = match mode { let file = match mode {
FileMode::Read => OpenOptions::new().read(true).open(path)?, FileMode::Read => OpenOptions::new().read(true).open(path)?,
FileMode::Write => OpenOptions::new().write(true).create(true).truncate(true).open(path)?, FileMode::Write => OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(path)?,
FileMode::Append => OpenOptions::new().append(true).create(true).open(path)?, FileMode::Append => OpenOptions::new().append(true).create(true).open(path)?,
FileMode::ReadWrite => OpenOptions::new().read(true).write(true).create(true).open(path)?, FileMode::ReadWrite => OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)?,
}; };
let handle_id = self.next_handle; let handle_id = self.next_handle;
@ -65,8 +73,9 @@ impl FileBoxRegistry {
pub fn read(&self, handle: BidHandle, size: usize) -> Result<Vec<u8>, std::io::Error> { pub fn read(&self, handle: BidHandle, size: usize) -> Result<Vec<u8>, std::io::Error> {
let handle_id = handle.instance_id; let handle_id = handle.instance_id;
let file_state = self.files.get(&handle_id) let file_state = self.files.get(&handle_id).ok_or_else(|| {
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle"))?; std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle")
})?;
let mut state = file_state.lock().unwrap(); let mut state = file_state.lock().unwrap();
let mut buffer = vec![0u8; size]; let mut buffer = vec![0u8; size];
@ -78,8 +87,9 @@ impl FileBoxRegistry {
pub fn write(&self, handle: BidHandle, data: &[u8]) -> Result<usize, std::io::Error> { pub fn write(&self, handle: BidHandle, data: &[u8]) -> Result<usize, std::io::Error> {
let handle_id = handle.instance_id; let handle_id = handle.instance_id;
let file_state = self.files.get(&handle_id) let file_state = self.files.get(&handle_id).ok_or_else(|| {
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle"))?; std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle")
})?;
let mut state = file_state.lock().unwrap(); let mut state = file_state.lock().unwrap();
state.file.write(data) state.file.write(data)
@ -87,8 +97,9 @@ impl FileBoxRegistry {
pub fn close(&mut self, handle: BidHandle) -> Result<(), std::io::Error> { pub fn close(&mut self, handle: BidHandle) -> Result<(), std::io::Error> {
let handle_id = handle.instance_id; let handle_id = handle.instance_id;
self.files.remove(&handle_id) self.files.remove(&handle_id).ok_or_else(|| {
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle"))?; std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle")
})?;
Ok(()) Ok(())
} }
} }
@ -125,28 +136,32 @@ impl FileBoxPlugin {
}; };
let mut registry = self.registry.lock().unwrap(); let mut registry = self.registry.lock().unwrap();
registry.open(path, file_mode) registry
.open(path, file_mode)
.map_err(|e| format!("Failed to open file: {}", e)) .map_err(|e| format!("Failed to open file: {}", e))
} }
/// Read data from file /// Read data from file
pub fn read(&self, handle: BidHandle, size: usize) -> Result<Vec<u8>, String> { pub fn read(&self, handle: BidHandle, size: usize) -> Result<Vec<u8>, String> {
let registry = self.registry.lock().unwrap(); let registry = self.registry.lock().unwrap();
registry.read(handle, size) registry
.read(handle, size)
.map_err(|e| format!("Failed to read file: {}", e)) .map_err(|e| format!("Failed to read file: {}", e))
} }
/// Write data to file /// Write data to file
pub fn write(&self, handle: BidHandle, data: &[u8]) -> Result<usize, String> { pub fn write(&self, handle: BidHandle, data: &[u8]) -> Result<usize, String> {
let registry = self.registry.lock().unwrap(); let registry = self.registry.lock().unwrap();
registry.write(handle, data) registry
.write(handle, data)
.map_err(|e| format!("Failed to write file: {}", e)) .map_err(|e| format!("Failed to write file: {}", e))
} }
/// Close file /// Close file
pub fn close(&self, handle: BidHandle) -> Result<(), String> { pub fn close(&self, handle: BidHandle) -> Result<(), String> {
let mut registry = self.registry.lock().unwrap(); let mut registry = self.registry.lock().unwrap();
registry.close(handle) registry
.close(handle)
.map_err(|e| format!("Failed to close file: {}", e)) .map_err(|e| format!("Failed to close file: {}", e))
} }
} }

View File

@ -1,4 +1,4 @@
use super::{BidError, BidResult, BidHandle, BidTag, BID_VERSION}; use super::{BidError, BidHandle, BidResult, BidTag, BID_VERSION};
use std::mem; use std::mem;
/// BID-1 TLV Header /// BID-1 TLV Header
@ -34,7 +34,9 @@ impl TlvEncoder {
}; };
// Reserve space for header // Reserve space for header
encoder.buffer.extend_from_slice(&[0; mem::size_of::<BidTlvHeader>()]); encoder
.buffer
.extend_from_slice(&[0; mem::size_of::<BidTlvHeader>()]);
encoder encoder
} }
@ -174,11 +176,12 @@ impl<'a> TlvDecoder<'a> {
// Read entry safely // Read entry safely
let tag = self.data[self.position]; let tag = self.data[self.position];
let reserved = self.data[self.position + 1]; let reserved = self.data[self.position + 1];
let size = u16::from_le_bytes([ let size = u16::from_le_bytes([self.data[self.position + 2], self.data[self.position + 3]]);
self.data[self.position + 2], let entry = TlvEntry {
self.data[self.position + 3], tag,
]); reserved,
let entry = TlvEntry { tag, reserved, size }; size,
};
self.position += mem::size_of::<TlvEntry>(); self.position += mem::size_of::<TlvEntry>();
// Read payload // Read payload
@ -223,7 +226,9 @@ impl<'a> TlvDecoder<'a> {
if payload.len() != 4 { if payload.len() != 4 {
return Err(BidError::InvalidArgs); return Err(BidError::InvalidArgs);
} }
Ok(i32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]])) Ok(i32::from_le_bytes([
payload[0], payload[1], payload[2], payload[3],
]))
} }
/// Decode an i64 from payload /// Decode an i64 from payload
@ -251,7 +256,9 @@ impl<'a> TlvDecoder<'a> {
if payload.len() != 4 { if payload.len() != 4 {
return Err(BidError::InvalidArgs); return Err(BidError::InvalidArgs);
} }
Ok(f32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]])) Ok(f32::from_le_bytes([
payload[0], payload[1], payload[2], payload[3],
]))
} }
/// Decode an f64 from payload /// Decode an f64 from payload

View File

@ -41,7 +41,10 @@ pub struct BidHandle {
impl BidHandle { impl BidHandle {
/// Create a new handle /// Create a new handle
pub fn new(type_id: u32, instance_id: u32) -> Self { pub fn new(type_id: u32, instance_id: u32) -> Self {
Self { type_id, instance_id } Self {
type_id,
instance_id,
}
} }
/// Pack into single u64 (type_id << 32 | instance_id) /// Pack into single u64 (type_id << 32 | instance_id)
@ -215,7 +218,14 @@ mod tests {
fn test_type_tags() { fn test_type_tags() {
assert_eq!(BidType::Bool.tag(), BidTag::Bool); assert_eq!(BidType::Bool.tag(), BidTag::Bool);
assert_eq!(BidType::String.tag(), BidTag::String); assert_eq!(BidType::String.tag(), BidTag::String);
assert_eq!(BidType::Handle { type_id: 6, instance_id: 0 }.tag(), BidTag::Handle); assert_eq!(
BidType::Handle {
type_id: 6,
instance_id: 0
}
.tag(),
BidTag::Handle
);
} }
#[test] #[test]
@ -232,7 +242,7 @@ mod tests {
let mapping = ArgTypeMapping::with_name( let mapping = ArgTypeMapping::with_name(
"content".to_string(), "content".to_string(),
"string".to_string(), "string".to_string(),
"string".to_string() "string".to_string(),
); );
assert_eq!(mapping.name, Some("content".to_string())); assert_eq!(mapping.name, Some("content".to_string()));
assert_eq!(mapping.determine_bid_tag(), Some(BidTag::String)); assert_eq!(mapping.determine_bid_tag(), Some(BidTag::String));

View File

@ -5,9 +5,9 @@
* arithmetic, logical, and comparison operations between different Box types. * arithmetic, logical, and comparison operations between different Box types.
*/ */
use crate::box_trait::{NyashBox, BoxCore, StringBox, IntegerBox, BoolBox, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use std::fmt::{Debug, Display};
use std::any::Any; use std::any::Any;
use std::fmt::{Debug, Display};
// ===== Binary Operation Boxes ===== // ===== Binary Operation Boxes =====
@ -34,7 +34,7 @@ impl AddBox {
// 1. Integer + Integer // 1. Integer + Integer
if let (Some(left_int), Some(right_int)) = ( if let (Some(left_int), Some(right_int)) = (
self.left.as_any().downcast_ref::<IntegerBox>(), self.left.as_any().downcast_ref::<IntegerBox>(),
self.right.as_any().downcast_ref::<IntegerBox>() self.right.as_any().downcast_ref::<IntegerBox>(),
) { ) {
let result = left_int.value + right_int.value; let result = left_int.value + right_int.value;
return Box::new(IntegerBox::new(result)); return Box::new(IntegerBox::new(result));
@ -43,7 +43,7 @@ impl AddBox {
// 2. Float + Float (or mixed with Integer) // 2. Float + Float (or mixed with Integer)
if let (Some(left_float), Some(right_float)) = ( if let (Some(left_float), Some(right_float)) = (
self.left.as_any().downcast_ref::<FloatBox>(), self.left.as_any().downcast_ref::<FloatBox>(),
self.right.as_any().downcast_ref::<FloatBox>() self.right.as_any().downcast_ref::<FloatBox>(),
) { ) {
let result = left_float.value + right_float.value; let result = left_float.value + right_float.value;
return Box::new(FloatBox::new(result)); return Box::new(FloatBox::new(result));
@ -52,7 +52,7 @@ impl AddBox {
// 3. Integer + Float // 3. Integer + Float
if let (Some(left_int), Some(right_float)) = ( if let (Some(left_int), Some(right_float)) = (
self.left.as_any().downcast_ref::<IntegerBox>(), self.left.as_any().downcast_ref::<IntegerBox>(),
self.right.as_any().downcast_ref::<FloatBox>() self.right.as_any().downcast_ref::<FloatBox>(),
) { ) {
let result = left_int.value as f64 + right_float.value; let result = left_int.value as f64 + right_float.value;
return Box::new(FloatBox::new(result)); return Box::new(FloatBox::new(result));
@ -61,7 +61,7 @@ impl AddBox {
// 4. Float + Integer // 4. Float + Integer
if let (Some(left_float), Some(right_int)) = ( if let (Some(left_float), Some(right_int)) = (
self.left.as_any().downcast_ref::<FloatBox>(), self.left.as_any().downcast_ref::<FloatBox>(),
self.right.as_any().downcast_ref::<IntegerBox>() self.right.as_any().downcast_ref::<IntegerBox>(),
) { ) {
let result = left_float.value + right_int.value as f64; let result = left_float.value + right_int.value as f64;
return Box::new(FloatBox::new(result)); return Box::new(FloatBox::new(result));
@ -94,8 +94,8 @@ impl NyashBox for AddBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_add) = other.as_any().downcast_ref::<AddBox>() { if let Some(other_add) = other.as_any().downcast_ref::<AddBox>() {
BoolBox::new( BoolBox::new(
self.left.equals(other_add.left.as_ref()).value && self.left.equals(other_add.left.as_ref()).value
self.right.equals(other_add.right.as_ref()).value && self.right.equals(other_add.right.as_ref()).value,
) )
} else { } else {
BoolBox::new(false) BoolBox::new(false)
@ -107,10 +107,7 @@ impl NyashBox for AddBox {
} }
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(AddBox::new( Box::new(AddBox::new(self.left.clone_box(), self.right.clone_box()))
self.left.clone_box(),
self.right.clone_box()
))
} }
/// 仮実装: clone_boxと同じ後で修正 /// 仮実装: clone_boxと同じ後で修正
@ -168,7 +165,7 @@ impl SubtractBox {
// For now, only handle integer subtraction // For now, only handle integer subtraction
if let (Some(left_int), Some(right_int)) = ( if let (Some(left_int), Some(right_int)) = (
self.left.as_any().downcast_ref::<IntegerBox>(), self.left.as_any().downcast_ref::<IntegerBox>(),
self.right.as_any().downcast_ref::<IntegerBox>() self.right.as_any().downcast_ref::<IntegerBox>(),
) { ) {
let result = left_int.value - right_int.value; let result = left_int.value - right_int.value;
Box::new(IntegerBox::new(result)) Box::new(IntegerBox::new(result))
@ -180,7 +177,8 @@ impl SubtractBox {
} else { } else {
0 0
}; };
let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::<IntegerBox>() { let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::<IntegerBox>()
{
int_box.value int_box.value
} else { } else {
0 0
@ -210,17 +208,22 @@ impl NyashBox for SubtractBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_sub) = other.as_any().downcast_ref::<SubtractBox>() { if let Some(other_sub) = other.as_any().downcast_ref::<SubtractBox>() {
BoolBox::new( BoolBox::new(
self.left.equals(other_sub.left.as_ref()).value && self.left.equals(other_sub.left.as_ref()).value
self.right.equals(other_sub.right.as_ref()).value && self.right.equals(other_sub.right.as_ref()).value,
) )
} else { } else {
BoolBox::new(false) BoolBox::new(false)
} }
} }
fn type_name(&self) -> &'static str { "SubtractBox" } fn type_name(&self) -> &'static str {
"SubtractBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(SubtractBox::new(self.left.clone_box(), self.right.clone_box())) Box::new(SubtractBox::new(
self.left.clone_box(),
self.right.clone_box(),
))
} }
/// 仮実装: clone_boxと同じ後で修正 /// 仮実装: clone_boxと同じ後で修正
@ -230,13 +233,21 @@ impl NyashBox for SubtractBox {
} }
impl BoxCore for SubtractBox { impl BoxCore for SubtractBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.to_string_box().value) write!(f, "{}", self.to_string_box().value)
} }
fn as_any(&self) -> &dyn Any { self } fn as_any(&self) -> &dyn Any {
fn as_any_mut(&mut self) -> &mut dyn Any { self } self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl Display for SubtractBox { impl Display for SubtractBox {
@ -266,7 +277,7 @@ impl MultiplyBox {
// For now, only handle integer multiplication // For now, only handle integer multiplication
if let (Some(left_int), Some(right_int)) = ( if let (Some(left_int), Some(right_int)) = (
self.left.as_any().downcast_ref::<IntegerBox>(), self.left.as_any().downcast_ref::<IntegerBox>(),
self.right.as_any().downcast_ref::<IntegerBox>() self.right.as_any().downcast_ref::<IntegerBox>(),
) { ) {
let result = left_int.value * right_int.value; let result = left_int.value * right_int.value;
Box::new(IntegerBox::new(result)) Box::new(IntegerBox::new(result))
@ -277,7 +288,8 @@ impl MultiplyBox {
} else { } else {
0 0
}; };
let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::<IntegerBox>() { let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::<IntegerBox>()
{
int_box.value int_box.value
} else { } else {
0 0
@ -307,17 +319,22 @@ impl NyashBox for MultiplyBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_mul) = other.as_any().downcast_ref::<MultiplyBox>() { if let Some(other_mul) = other.as_any().downcast_ref::<MultiplyBox>() {
BoolBox::new( BoolBox::new(
self.left.equals(other_mul.left.as_ref()).value && self.left.equals(other_mul.left.as_ref()).value
self.right.equals(other_mul.right.as_ref()).value && self.right.equals(other_mul.right.as_ref()).value,
) )
} else { } else {
BoolBox::new(false) BoolBox::new(false)
} }
} }
fn type_name(&self) -> &'static str { "MultiplyBox" } fn type_name(&self) -> &'static str {
"MultiplyBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(MultiplyBox::new(self.left.clone_box(), self.right.clone_box())) Box::new(MultiplyBox::new(
self.left.clone_box(),
self.right.clone_box(),
))
} }
/// 仮実装: clone_boxと同じ後で修正 /// 仮実装: clone_boxと同じ後で修正
@ -327,13 +344,21 @@ impl NyashBox for MultiplyBox {
} }
impl BoxCore for MultiplyBox { impl BoxCore for MultiplyBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.to_string_box().value) write!(f, "{}", self.to_string_box().value)
} }
fn as_any(&self) -> &dyn Any { self } fn as_any(&self) -> &dyn Any {
fn as_any_mut(&mut self) -> &mut dyn Any { self } self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl Display for MultiplyBox { impl Display for MultiplyBox {
@ -365,7 +390,7 @@ impl DivideBox {
// Handle integer division, but return float result // Handle integer division, but return float result
if let (Some(left_int), Some(right_int)) = ( if let (Some(left_int), Some(right_int)) = (
self.left.as_any().downcast_ref::<IntegerBox>(), self.left.as_any().downcast_ref::<IntegerBox>(),
self.right.as_any().downcast_ref::<IntegerBox>() self.right.as_any().downcast_ref::<IntegerBox>(),
) { ) {
if right_int.value == 0 { if right_int.value == 0 {
// Return error for division by zero // Return error for division by zero
@ -380,7 +405,8 @@ impl DivideBox {
} else { } else {
0 0
}; };
let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::<IntegerBox>() { let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::<IntegerBox>()
{
int_box.value int_box.value
} else { } else {
1 // Avoid division by zero 1 // Avoid division by zero
@ -413,17 +439,22 @@ impl NyashBox for DivideBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_div) = other.as_any().downcast_ref::<DivideBox>() { if let Some(other_div) = other.as_any().downcast_ref::<DivideBox>() {
BoolBox::new( BoolBox::new(
self.left.equals(other_div.left.as_ref()).value && self.left.equals(other_div.left.as_ref()).value
self.right.equals(other_div.right.as_ref()).value && self.right.equals(other_div.right.as_ref()).value,
) )
} else { } else {
BoolBox::new(false) BoolBox::new(false)
} }
} }
fn type_name(&self) -> &'static str { "DivideBox" } fn type_name(&self) -> &'static str {
"DivideBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(DivideBox::new(self.left.clone_box(), self.right.clone_box())) Box::new(DivideBox::new(
self.left.clone_box(),
self.right.clone_box(),
))
} }
/// 仮実装: clone_boxと同じ後で修正 /// 仮実装: clone_boxと同じ後で修正
@ -433,13 +464,21 @@ impl NyashBox for DivideBox {
} }
impl BoxCore for DivideBox { impl BoxCore for DivideBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.to_string_box().value) write!(f, "{}", self.to_string_box().value)
} }
fn as_any(&self) -> &dyn Any { self } fn as_any(&self) -> &dyn Any {
fn as_any_mut(&mut self) -> &mut dyn Any { self } self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl Display for DivideBox { impl Display for DivideBox {
@ -469,7 +508,7 @@ impl ModuloBox {
// Handle integer modulo operation // Handle integer modulo operation
if let (Some(left_int), Some(right_int)) = ( if let (Some(left_int), Some(right_int)) = (
self.left.as_any().downcast_ref::<IntegerBox>(), self.left.as_any().downcast_ref::<IntegerBox>(),
self.right.as_any().downcast_ref::<IntegerBox>() self.right.as_any().downcast_ref::<IntegerBox>(),
) { ) {
if right_int.value == 0 { if right_int.value == 0 {
// Return error for modulo by zero // Return error for modulo by zero
@ -484,7 +523,8 @@ impl ModuloBox {
} else { } else {
0 0
}; };
let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::<IntegerBox>() { let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::<IntegerBox>()
{
int_box.value int_box.value
} else { } else {
1 // Avoid modulo by zero 1 // Avoid modulo by zero
@ -508,8 +548,12 @@ impl Debug for ModuloBox {
} }
impl BoxCore for ModuloBox { impl BoxCore for ModuloBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "ModuloBox[{}]", self.box_id()) write!(f, "ModuloBox[{}]", self.box_id())
} }
@ -532,8 +576,8 @@ impl NyashBox for ModuloBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_modulo) = other.as_any().downcast_ref::<ModuloBox>() { if let Some(other_modulo) = other.as_any().downcast_ref::<ModuloBox>() {
BoolBox::new( BoolBox::new(
self.left.equals(other_modulo.left.as_ref()).value && self.left.equals(other_modulo.left.as_ref()).value
self.right.equals(other_modulo.right.as_ref()).value && self.right.equals(other_modulo.right.as_ref()).value,
) )
} else { } else {
BoolBox::new(false) BoolBox::new(false)
@ -545,7 +589,10 @@ impl NyashBox for ModuloBox {
} }
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(ModuloBox::new(self.left.clone_box(), self.right.clone_box())) Box::new(ModuloBox::new(
self.left.clone_box(),
self.right.clone_box(),
))
} }
/// 仮実装: clone_boxと同じ後で修正 /// 仮実装: clone_boxと同じ後で修正
@ -574,7 +621,7 @@ impl CompareBox {
// Try integer comparison first // Try integer comparison first
if let (Some(left_int), Some(right_int)) = ( if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(), left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>() right.as_any().downcast_ref::<IntegerBox>(),
) { ) {
return BoolBox::new(left_int.value < right_int.value); return BoolBox::new(left_int.value < right_int.value);
} }
@ -590,7 +637,7 @@ impl CompareBox {
// Try integer comparison first // Try integer comparison first
if let (Some(left_int), Some(right_int)) = ( if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(), left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>() right.as_any().downcast_ref::<IntegerBox>(),
) { ) {
return BoolBox::new(left_int.value > right_int.value); return BoolBox::new(left_int.value > right_int.value);
} }
@ -606,7 +653,7 @@ impl CompareBox {
// Try integer comparison first // Try integer comparison first
if let (Some(left_int), Some(right_int)) = ( if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(), left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>() right.as_any().downcast_ref::<IntegerBox>(),
) { ) {
return BoolBox::new(left_int.value <= right_int.value); return BoolBox::new(left_int.value <= right_int.value);
} }
@ -622,7 +669,7 @@ impl CompareBox {
// Try integer comparison first // Try integer comparison first
if let (Some(left_int), Some(right_int)) = ( if let (Some(left_int), Some(right_int)) = (
left.as_any().downcast_ref::<IntegerBox>(), left.as_any().downcast_ref::<IntegerBox>(),
right.as_any().downcast_ref::<IntegerBox>() right.as_any().downcast_ref::<IntegerBox>(),
) { ) {
return BoolBox::new(left_int.value >= right_int.value); return BoolBox::new(left_int.value >= right_int.value);
} }

View File

@ -7,14 +7,16 @@
*/ */
use super::BoxFactory; use super::BoxFactory;
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox}; use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox};
use crate::interpreter::RuntimeError; use crate::interpreter::RuntimeError;
/// Factory for builtin Box types /// Factory for builtin Box types
pub struct BuiltinBoxFactory; pub struct BuiltinBoxFactory;
impl BuiltinBoxFactory { impl BuiltinBoxFactory {
pub fn new() -> Self { Self } pub fn new() -> Self {
Self
}
} }
impl BoxFactory for BuiltinBoxFactory { impl BoxFactory for BuiltinBoxFactory {
@ -57,18 +59,27 @@ impl BoxFactory for BuiltinBoxFactory {
"NullBox" => Ok(Box::new(crate::boxes::null_box::NullBox::new())), "NullBox" => Ok(Box::new(crate::boxes::null_box::NullBox::new())),
// Leave other types to other factories (user/plugin) // Leave other types to other factories (user/plugin)
_ => Err(RuntimeError::InvalidOperation { message: format!("Unknown Box type: {}", name) }), _ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown Box type: {}", name),
}),
} }
} }
fn box_types(&self) -> Vec<&str> { fn box_types(&self) -> Vec<&str> {
vec![ vec![
// Primitive wrappers // Primitive wrappers
"StringBox", "IntegerBox", "BoolBox", "StringBox",
"IntegerBox",
"BoolBox",
// Collections/common // Collections/common
"ArrayBox", "MapBox", "ConsoleBox", "NullBox", "ArrayBox",
"MapBox",
"ConsoleBox",
"NullBox",
] ]
} }
fn is_builtin_factory(&self) -> bool { true } fn is_builtin_factory(&self) -> bool {
true
}
} }

View File

@ -119,8 +119,12 @@ impl UnifiedBoxRegistry {
if std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1") { if std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1") {
use crate::runtime::{get_global_registry, BoxProvider}; use crate::runtime::{get_global_registry, BoxProvider};
// Allowlist types for override: env NYASH_PLUGIN_OVERRIDE_TYPES="ArrayBox,MapBox" (default: none) // Allowlist types for override: env NYASH_PLUGIN_OVERRIDE_TYPES="ArrayBox,MapBox" (default: none)
let allow: Vec<String> = if let Ok(list) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES") { let allow: Vec<String> = if let Ok(list) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES")
list.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect() {
list.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect()
} else { } else {
vec![] vec![]
}; };
@ -128,8 +132,11 @@ impl UnifiedBoxRegistry {
let v2 = get_global_registry(); let v2 = get_global_registry();
if let Some(provider) = v2.get_provider(name) { if let Some(provider) = v2.get_provider(name) {
if let BoxProvider::Plugin(_lib) = provider { if let BoxProvider::Plugin(_lib) = provider {
return v2.create_box(name, args) return v2.create_box(name, args).map_err(|e| {
.map_err(|e| RuntimeError::InvalidOperation { message: format!("Plugin Box creation failed: {}", e) }); RuntimeError::InvalidOperation {
message: format!("Plugin Box creation failed: {}", e),
}
});
} }
} }
} }
@ -185,15 +192,21 @@ impl UnifiedBoxRegistry {
let cache = self.type_cache.read().unwrap(); let cache = self.type_cache.read().unwrap();
if let Some(&idx) = cache.get(name) { if let Some(&idx) = cache.get(name) {
if let Some(factory) = self.factories.get(idx) { if let Some(factory) = self.factories.get(idx) {
if factory.is_available() { return true; } if factory.is_available() {
return true;
}
} }
} }
} }
// Fallback: scan factories that can enumerate types // Fallback: scan factories that can enumerate types
for factory in &self.factories { for factory in &self.factories {
if !factory.is_available() { continue; } if !factory.is_available() {
continue;
}
let types = factory.box_types(); let types = factory.box_types();
if !types.is_empty() && types.contains(&name) { return true; } if !types.is_empty() && types.contains(&name) {
return true;
}
} }
false false
} }
@ -214,10 +227,10 @@ impl UnifiedBoxRegistry {
} }
} }
pub mod builtin;
pub mod plugin;
/// Re-export submodules /// Re-export submodules
pub mod user_defined; pub mod user_defined;
pub mod plugin;
pub mod builtin;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -31,7 +31,8 @@ impl BoxFactory for PluginBoxFactory {
let registry = get_global_registry(); let registry = get_global_registry();
if let Some(_provider) = registry.get_provider(name) { if let Some(_provider) = registry.get_provider(name) {
registry.create_box(name, args) registry
.create_box(name, args)
.map_err(|e| RuntimeError::InvalidOperation { .map_err(|e| RuntimeError::InvalidOperation {
message: format!("Plugin Box creation failed: {}", e), message: format!("Plugin Box creation failed: {}", e),
}) })

View File

@ -7,8 +7,8 @@
use super::BoxFactory; use super::BoxFactory;
use crate::box_trait::NyashBox; use crate::box_trait::NyashBox;
use crate::interpreter::{RuntimeError, SharedState};
use crate::instance_v2::InstanceBox; use crate::instance_v2::InstanceBox;
use crate::interpreter::{RuntimeError, SharedState};
/// Factory for user-defined Box types /// Factory for user-defined Box types
pub struct UserDefinedBoxFactory { pub struct UserDefinedBoxFactory {
@ -17,9 +17,7 @@ pub struct UserDefinedBoxFactory {
impl UserDefinedBoxFactory { impl UserDefinedBoxFactory {
pub fn new(shared_state: SharedState) -> Self { pub fn new(shared_state: SharedState) -> Self {
Self { Self { shared_state }
shared_state,
}
} }
} }

View File

@ -9,12 +9,11 @@
* static/dynamic hybrid dispatch for optimal performance. * static/dynamic hybrid dispatch for optimal performance.
*/ */
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox}; use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox};
use crate::boxes::FloatBox; use crate::boxes::FloatBox;
use crate::operator_traits::{ use crate::operator_traits::{
NyashAdd, NyashSub, NyashMul, NyashDiv, DynamicAdd, DynamicDiv, DynamicMul, DynamicSub, NyashAdd, NyashDiv, NyashMul, NyashSub,
DynamicAdd, DynamicSub, DynamicMul, DynamicDiv, OperatorError,
OperatorError
}; };
// ===== IntegerBox Operator Implementations ===== // ===== IntegerBox Operator Implementations =====
@ -69,14 +68,19 @@ impl DynamicAdd for IntegerBox {
// IntegerBox + FloatBox -> FloatBox // IntegerBox + FloatBox -> FloatBox
if let Some(other_float) = other.as_any().downcast_ref::<FloatBox>() { if let Some(other_float) = other.as_any().downcast_ref::<FloatBox>() {
return Some(Box::new(FloatBox::new(self.value as f64 + other_float.value))); return Some(Box::new(FloatBox::new(
self.value as f64 + other_float.value,
)));
} }
// Fallback: Convert both to strings and concatenate // Fallback: Convert both to strings and concatenate
// This preserves the existing AddBox behavior // This preserves the existing AddBox behavior
let left_str = self.to_string_box(); let left_str = self.to_string_box();
let right_str = other.to_string_box(); let right_str = other.to_string_box();
Some(Box::new(StringBox::new(format!("{}{}", left_str.value, right_str.value)))) Some(Box::new(StringBox::new(format!(
"{}{}",
left_str.value, right_str.value
))))
} }
fn can_add_with(&self, other_type: &str) -> bool { fn can_add_with(&self, other_type: &str) -> bool {
@ -93,7 +97,9 @@ impl DynamicSub for IntegerBox {
// IntegerBox - FloatBox -> FloatBox // IntegerBox - FloatBox -> FloatBox
if let Some(other_float) = other.as_any().downcast_ref::<FloatBox>() { if let Some(other_float) = other.as_any().downcast_ref::<FloatBox>() {
return Some(Box::new(FloatBox::new(self.value as f64 - other_float.value))); return Some(Box::new(FloatBox::new(
self.value as f64 - other_float.value,
)));
} }
None // Subtraction not supported for other types None // Subtraction not supported for other types
@ -113,12 +119,15 @@ impl DynamicMul for IntegerBox {
// IntegerBox * FloatBox -> FloatBox // IntegerBox * FloatBox -> FloatBox
if let Some(other_float) = other.as_any().downcast_ref::<FloatBox>() { if let Some(other_float) = other.as_any().downcast_ref::<FloatBox>() {
return Some(Box::new(FloatBox::new(self.value as f64 * other_float.value))); return Some(Box::new(FloatBox::new(
self.value as f64 * other_float.value,
)));
} }
// IntegerBox * StringBox -> Repeated string // IntegerBox * StringBox -> Repeated string
if let Some(other_str) = other.as_any().downcast_ref::<StringBox>() { if let Some(other_str) = other.as_any().downcast_ref::<StringBox>() {
if self.value >= 0 && self.value <= 10000 { // Safety limit if self.value >= 0 && self.value <= 10000 {
// Safety limit
let repeated = other_str.value.repeat(self.value as usize); let repeated = other_str.value.repeat(self.value as usize);
return Some(Box::new(StringBox::new(repeated))); return Some(Box::new(StringBox::new(repeated)));
} }
@ -139,7 +148,9 @@ impl DynamicDiv for IntegerBox {
if other_int.value == 0 { if other_int.value == 0 {
return None; // Division by zero return None; // Division by zero
} }
return Some(Box::new(FloatBox::new(self.value as f64 / other_int.value as f64))); return Some(Box::new(FloatBox::new(
self.value as f64 / other_int.value as f64,
)));
} }
// IntegerBox / FloatBox -> FloatBox // IntegerBox / FloatBox -> FloatBox
@ -147,7 +158,9 @@ impl DynamicDiv for IntegerBox {
if other_float.value == 0.0 { if other_float.value == 0.0 {
return None; // Division by zero return None; // Division by zero
} }
return Some(Box::new(FloatBox::new(self.value as f64 / other_float.value))); return Some(Box::new(FloatBox::new(
self.value as f64 / other_float.value,
)));
} }
None None
@ -217,7 +230,10 @@ impl DynamicAdd for FloatBox {
// Fallback: Convert both to strings and concatenate // Fallback: Convert both to strings and concatenate
let left_str = self.to_string_box(); let left_str = self.to_string_box();
let right_str = other.to_string_box(); let right_str = other.to_string_box();
Some(Box::new(StringBox::new(format!("{}{}", left_str.value, right_str.value)))) Some(Box::new(StringBox::new(format!(
"{}{}",
left_str.value, right_str.value
))))
} }
fn can_add_with(&self, other_type: &str) -> bool { fn can_add_with(&self, other_type: &str) -> bool {
@ -307,7 +323,8 @@ impl NyashMul<IntegerBox> for StringBox {
type Output = StringBox; type Output = StringBox;
fn mul(self, rhs: IntegerBox) -> Self::Output { fn mul(self, rhs: IntegerBox) -> Self::Output {
if rhs.value >= 0 && rhs.value <= 10000 { // Safety limit if rhs.value >= 0 && rhs.value <= 10000 {
// Safety limit
StringBox::new(self.value.repeat(rhs.value as usize)) StringBox::new(self.value.repeat(rhs.value as usize))
} else { } else {
StringBox::new(String::new()) // Empty string for invalid repetition StringBox::new(String::new()) // Empty string for invalid repetition
@ -320,12 +337,18 @@ impl DynamicAdd for StringBox {
fn try_add(&self, other: &dyn NyashBox) -> Option<Box<dyn NyashBox>> { fn try_add(&self, other: &dyn NyashBox) -> Option<Box<dyn NyashBox>> {
// StringBox + StringBox // StringBox + StringBox
if let Some(other_str) = other.as_any().downcast_ref::<StringBox>() { if let Some(other_str) = other.as_any().downcast_ref::<StringBox>() {
return Some(Box::new(StringBox::new(format!("{}{}", self.value, other_str.value)))); return Some(Box::new(StringBox::new(format!(
"{}{}",
self.value, other_str.value
))));
} }
// StringBox + any other type -> Convert to string and concatenate // StringBox + any other type -> Convert to string and concatenate
let other_str = other.to_string_box(); let other_str = other.to_string_box();
Some(Box::new(StringBox::new(format!("{}{}", self.value, other_str.value)))) Some(Box::new(StringBox::new(format!(
"{}{}",
self.value, other_str.value
))))
} }
fn can_add_with(&self, _other_type: &str) -> bool { fn can_add_with(&self, _other_type: &str) -> bool {
@ -347,7 +370,8 @@ impl DynamicMul for StringBox {
fn try_mul(&self, other: &dyn NyashBox) -> Option<Box<dyn NyashBox>> { fn try_mul(&self, other: &dyn NyashBox) -> Option<Box<dyn NyashBox>> {
// StringBox * IntegerBox -> Repeated string // StringBox * IntegerBox -> Repeated string
if let Some(other_int) = other.as_any().downcast_ref::<IntegerBox>() { if let Some(other_int) = other.as_any().downcast_ref::<IntegerBox>() {
if other_int.value >= 0 && other_int.value <= 10000 { // Safety limit if other_int.value >= 0 && other_int.value <= 10000 {
// Safety limit
let repeated = self.value.repeat(other_int.value as usize); let repeated = self.value.repeat(other_int.value as usize);
return Some(Box::new(StringBox::new(repeated))); return Some(Box::new(StringBox::new(repeated)));
} }
@ -400,7 +424,10 @@ impl DynamicAdd for BoolBox {
// Fallback to string concatenation // Fallback to string concatenation
let left_str = self.to_string_box(); let left_str = self.to_string_box();
let right_str = other.to_string_box(); let right_str = other.to_string_box();
Some(Box::new(StringBox::new(format!("{}{}", left_str.value, right_str.value)))) Some(Box::new(StringBox::new(format!(
"{}{}",
left_str.value, right_str.value
))))
} }
fn can_add_with(&self, other_type: &str) -> bool { fn can_add_with(&self, other_type: &str) -> bool {
@ -499,7 +526,10 @@ impl OperatorResolver {
} }
} }
if let Some(float_box) = right.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>() { if let Some(float_box) = right
.as_any()
.downcast_ref::<crate::boxes::math_box::FloatBox>()
{
if let Some(result) = float_box.try_add(right) { if let Some(result) = float_box.try_add(right) {
return Ok(result); return Ok(result);
} }
@ -530,7 +560,10 @@ impl OperatorResolver {
} }
} }
if let Some(float_box) = left.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>() { if let Some(float_box) = left
.as_any()
.downcast_ref::<crate::boxes::math_box::FloatBox>()
{
if let Some(result) = float_box.try_sub(right) { if let Some(result) = float_box.try_sub(right) {
return Ok(result); return Ok(result);
} }
@ -561,7 +594,10 @@ impl OperatorResolver {
} }
} }
if let Some(float_box) = left.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>() { if let Some(float_box) = left
.as_any()
.downcast_ref::<crate::boxes::math_box::FloatBox>()
{
if let Some(result) = float_box.try_mul(right) { if let Some(result) = float_box.try_mul(right) {
return Ok(result); return Ok(result);
} }
@ -595,7 +631,10 @@ impl OperatorResolver {
} }
} }
if let Some(float_box) = left.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>() { if let Some(float_box) = left
.as_any()
.downcast_ref::<crate::boxes::math_box::FloatBox>()
{
if let Some(result) = float_box.try_div(right) { if let Some(result) = float_box.try_div(right) {
return Ok(result); return Ok(result);
} else { } else {

View File

@ -6,12 +6,12 @@
* implements the NyashBox trait. * implements the NyashBox trait.
*/ */
use std::fmt::{Debug, Display};
use std::any::Any; use std::any::Any;
use std::sync::Arc; use std::fmt::{Debug, Display};
use std::sync::atomic::{AtomicU64, Ordering};
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
// 🔥 新しい型エイリアス - 将来的にBox<dyn NyashBox>を全て置き換える // 🔥 新しい型エイリアス - 将来的にBox<dyn NyashBox>を全て置き換える
pub type SharedNyashBox = Arc<dyn NyashBox>; pub type SharedNyashBox = Arc<dyn NyashBox>;
@ -26,14 +26,38 @@ pub fn next_box_id() -> u64 {
/// 🔥 Phase 8.8: pack透明化システム - ビルトインBox判定リスト /// 🔥 Phase 8.8: pack透明化システム - ビルトインBox判定リスト
/// ユーザーは`pack`を一切意識せず、`from BuiltinBox()`で自動的に内部のpack機能が呼ばれる /// ユーザーは`pack`を一切意識せず、`from BuiltinBox()`で自動的に内部のpack機能が呼ばれる
pub const BUILTIN_BOXES: &[&str] = &[ pub const BUILTIN_BOXES: &[&str] = &[
"StringBox", "IntegerBox", "BoolBox", "NullBox", "StringBox",
"ArrayBox", "MapBox", "FileBox", "ResultBox", "IntegerBox",
"FutureBox", "ChannelBox", "MathBox", "FloatBox", "BoolBox",
"TimeBox", "DateTimeBox", "TimerBox", "RandomBox", "NullBox",
"SoundBox", "DebugBox", "MethodBox", "ConsoleBox", "ArrayBox",
"BufferBox", "RegexBox", "JSONBox", "StreamBox", "MapBox",
"HTTPClientBox", "IntentBox", "P2PBox", "SocketBox", "FileBox",
"HTTPServerBox", "HTTPRequestBox", "HTTPResponseBox", "JitConfigBox" "ResultBox",
"FutureBox",
"ChannelBox",
"MathBox",
"FloatBox",
"TimeBox",
"DateTimeBox",
"TimerBox",
"RandomBox",
"SoundBox",
"DebugBox",
"MethodBox",
"ConsoleBox",
"BufferBox",
"RegexBox",
"JSONBox",
"StreamBox",
"HTTPClientBox",
"IntentBox",
"P2PBox",
"SocketBox",
"HTTPServerBox",
"HTTPRequestBox",
"HTTPResponseBox",
"JitConfigBox",
]; ];
/// 🔥 ビルトインBox判定関数 - pack透明化システムの核心 /// 🔥 ビルトインBox判定関数 - pack透明化システムの核心
@ -110,11 +134,17 @@ pub trait NyashBox: BoxCore + Debug {
fn share_box(&self) -> Box<dyn NyashBox>; fn share_box(&self) -> Box<dyn NyashBox>;
/// Identity hint: boxes that wrap external/stateful handles should override to return true. /// Identity hint: boxes that wrap external/stateful handles should override to return true.
fn is_identity(&self) -> bool { false } fn is_identity(&self) -> bool {
false
}
/// Helper: pick share or clone based on identity semantics. /// Helper: pick share or clone based on identity semantics.
fn clone_or_share(&self) -> Box<dyn NyashBox> { fn clone_or_share(&self) -> Box<dyn NyashBox> {
if self.is_identity() { self.share_box() } else { self.clone_box() } if self.is_identity() {
self.share_box()
} else {
self.clone_box()
}
} }
/// Arc参照を返す新しいcloneメソッド参照共有 /// Arc参照を返す新しいcloneメソッド参照共有
@ -154,7 +184,8 @@ impl StringBox {
/// Split string by delimiter and return ArrayBox /// Split string by delimiter and return ArrayBox
pub fn split(&self, delimiter: &str) -> Box<dyn NyashBox> { pub fn split(&self, delimiter: &str) -> Box<dyn NyashBox> {
let parts: Vec<String> = self.value.split(delimiter).map(|s| s.to_string()).collect(); let parts: Vec<String> = self.value.split(delimiter).map(|s| s.to_string()).collect();
let array_elements: Vec<Box<dyn NyashBox>> = parts.into_iter() let array_elements: Vec<Box<dyn NyashBox>> = parts
.into_iter()
.map(|s| Box::new(StringBox::new(s)) as Box<dyn NyashBox>) .map(|s| Box::new(StringBox::new(s)) as Box<dyn NyashBox>)
.collect(); .collect();
Box::new(ArrayBox::new_with_elements(array_elements)) Box::new(ArrayBox::new_with_elements(array_elements))
@ -206,7 +237,10 @@ impl StringBox {
/// Join array elements using this string as delimiter /// Join array elements using this string as delimiter
pub fn join(&self, array_box: Box<dyn NyashBox>) -> Box<dyn NyashBox> { pub fn join(&self, array_box: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(array) = array_box.as_any().downcast_ref::<ArrayBox>() { if let Some(array) = array_box.as_any().downcast_ref::<ArrayBox>() {
let strings: Vec<String> = array.items.read().unwrap() let strings: Vec<String> = array
.items
.read()
.unwrap()
.iter() .iter()
.map(|element| element.to_string_box().value) .map(|element| element.to_string_box().value)
.collect(); .collect();
@ -318,7 +352,7 @@ impl IntegerBox {
pub fn new(value: i64) -> Self { pub fn new(value: i64) -> Self {
Self { Self {
value, value,
base: BoxBase::new() base: BoxBase::new(),
} }
} }
@ -393,7 +427,7 @@ impl BoolBox {
pub fn new(value: bool) -> Self { pub fn new(value: bool) -> Self {
Self { Self {
value, value,
base: BoxBase::new() base: BoxBase::new(),
} }
} }
@ -470,7 +504,7 @@ pub struct VoidBox {
impl VoidBox { impl VoidBox {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
base: BoxBase::new() base: BoxBase::new(),
} }
} }
} }
@ -693,7 +727,9 @@ impl NyashBox for ErrorBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_error) = other.as_any().downcast_ref::<ErrorBox>() { if let Some(other_error) = other.as_any().downcast_ref::<ErrorBox>() {
BoolBox::new(self.error_type == other_error.error_type && self.message == other_error.message) BoolBox::new(
self.error_type == other_error.error_type && self.message == other_error.message,
)
} else { } else {
BoolBox::new(false) BoolBox::new(false)
} }
@ -719,12 +755,13 @@ impl Display for ErrorBox {
} }
} }
// FutureBox is now implemented in src/boxes/future/mod.rs using RwLock pattern // FutureBox is now implemented in src/boxes/future/mod.rs using RwLock pattern
// and re-exported from src/boxes/mod.rs as both NyashFutureBox and FutureBox // and re-exported from src/boxes/mod.rs as both NyashFutureBox and FutureBox
// Re-export operation boxes from the dedicated operations module // Re-export operation boxes from the dedicated operations module
pub use crate::box_arithmetic::{AddBox, SubtractBox, MultiplyBox, DivideBox, ModuloBox, CompareBox}; pub use crate::box_arithmetic::{
AddBox, CompareBox, DivideBox, ModuloBox, MultiplyBox, SubtractBox,
};
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -1,25 +1,55 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any; use std::any::Any;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AotCompilerBox { base: BoxBase } pub struct AotCompilerBox {
base: BoxBase,
}
impl AotCompilerBox { pub fn new() -> Self { Self { base: BoxBase::new() } } } impl AotCompilerBox {
pub fn new() -> Self {
Self {
base: BoxBase::new(),
}
}
}
impl BoxCore for AotCompilerBox { impl BoxCore for AotCompilerBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "AotCompilerBox") } }
fn as_any(&self) -> &dyn Any { self } fn parent_type_id(&self) -> Option<std::any::TypeId> {
fn as_any_mut(&mut self) -> &mut dyn Any { self } self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "AotCompilerBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl NyashBox for AotCompilerBox { impl NyashBox for AotCompilerBox {
fn to_string_box(&self) -> StringBox { StringBox::new("AotCompilerBox".to_string()) } fn to_string_box(&self) -> StringBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<AotCompilerBox>()) } StringBox::new("AotCompilerBox".to_string())
fn type_name(&self) -> &'static str { "AotCompilerBox" } }
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) } fn equals(&self, other: &dyn NyashBox) -> BoolBox {
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() } BoolBox::new(other.as_any().is::<AotCompilerBox>())
}
fn type_name(&self) -> &'static str {
"AotCompilerBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self {
base: self.base.clone(),
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
} }
impl AotCompilerBox { impl AotCompilerBox {
@ -28,12 +58,15 @@ impl AotCompilerBox {
pub fn compile(&self, file: &str, out: &str) -> Box<dyn NyashBox> { pub fn compile(&self, file: &str, out: &str) -> Box<dyn NyashBox> {
let mut cmd = match std::env::current_exe() { let mut cmd = match std::env::current_exe() {
Ok(p) => std::process::Command::new(p), Ok(p) => std::process::Command::new(p),
Err(e) => return Box::new(StringBox::new(format!("ERR: current_exe(): {}", e))) Err(e) => return Box::new(StringBox::new(format!("ERR: current_exe(): {}", e))),
}; };
// Propagate relevant envs (AOT/JIT observe) // Propagate relevant envs (AOT/JIT observe)
let c = cmd.arg("--backend").arg("vm") // ensures runner path let c = cmd
.arg("--backend")
.arg("vm") // ensures runner path
.arg("--compile-native") .arg("--compile-native")
.arg("-o").arg(out) .arg("-o")
.arg(out)
.arg(file) .arg(file)
.envs(std::env::vars()); .envs(std::env::vars());
match c.output() { match c.output() {
@ -42,12 +75,15 @@ impl AotCompilerBox {
s.push_str(&String::from_utf8_lossy(&o.stdout)); s.push_str(&String::from_utf8_lossy(&o.stdout));
s.push_str(&String::from_utf8_lossy(&o.stderr)); s.push_str(&String::from_utf8_lossy(&o.stderr));
if !o.status.success() { if !o.status.success() {
s = format!("AOT FAILED (code={}):\n{}", o.status.code().unwrap_or(-1), s); s = format!(
"AOT FAILED (code={}):\n{}",
o.status.code().unwrap_or(-1),
s
);
} }
Box::new(StringBox::new(s)) Box::new(StringBox::new(s))
} }
Err(e) => Box::new(StringBox::new(format!("ERR: spawn compile-native: {}", e))) Err(e) => Box::new(StringBox::new(format!("ERR: spawn compile-native: {}", e))),
} }
} }
} }

View File

@ -1,4 +1,4 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any; use std::any::Any;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -10,44 +10,104 @@ pub struct AotConfigBox {
pub plugin_paths: Option<String>, pub plugin_paths: Option<String>,
} }
impl AotConfigBox { pub fn new() -> Self { Self { base: BoxBase::new(), output_file: None, emit_obj_out: None, plugin_paths: None } } } impl AotConfigBox {
pub fn new() -> Self {
Self {
base: BoxBase::new(),
output_file: None,
emit_obj_out: None,
plugin_paths: None,
}
}
}
impl BoxCore for AotConfigBox { impl BoxCore for AotConfigBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "AotConfigBox") } }
fn as_any(&self) -> &dyn Any { self } fn parent_type_id(&self) -> Option<std::any::TypeId> {
fn as_any_mut(&mut self) -> &mut dyn Any { self } self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "AotConfigBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl NyashBox for AotConfigBox { impl NyashBox for AotConfigBox {
fn to_string_box(&self) -> StringBox { self.summary() } fn to_string_box(&self) -> StringBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<AotConfigBox>()) } self.summary()
fn type_name(&self) -> &'static str { "AotConfigBox" } }
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone(), output_file: self.output_file.clone(), emit_obj_out: self.emit_obj_out.clone(), plugin_paths: self.plugin_paths.clone() }) } fn equals(&self, other: &dyn NyashBox) -> BoolBox {
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() } BoolBox::new(other.as_any().is::<AotConfigBox>())
}
fn type_name(&self) -> &'static str {
"AotConfigBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self {
base: self.base.clone(),
output_file: self.output_file.clone(),
emit_obj_out: self.emit_obj_out.clone(),
plugin_paths: self.plugin_paths.clone(),
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
} }
impl AotConfigBox { impl AotConfigBox {
pub fn set_output(&mut self, path: &str) -> Box<dyn NyashBox> { self.output_file = Some(path.to_string()); Box::new(VoidBox::new()) } pub fn set_output(&mut self, path: &str) -> Box<dyn NyashBox> {
pub fn set_obj_out(&mut self, path: &str) -> Box<dyn NyashBox> { self.emit_obj_out = Some(path.to_string()); Box::new(VoidBox::new()) } self.output_file = Some(path.to_string());
pub fn set_plugin_paths(&mut self, paths: &str) -> Box<dyn NyashBox> { self.plugin_paths = Some(paths.to_string()); Box::new(VoidBox::new()) } Box::new(VoidBox::new())
pub fn clear(&mut self) -> Box<dyn NyashBox> { self.output_file = None; self.emit_obj_out = None; self.plugin_paths = None; Box::new(VoidBox::new()) } }
pub fn set_obj_out(&mut self, path: &str) -> Box<dyn NyashBox> {
self.emit_obj_out = Some(path.to_string());
Box::new(VoidBox::new())
}
pub fn set_plugin_paths(&mut self, paths: &str) -> Box<dyn NyashBox> {
self.plugin_paths = Some(paths.to_string());
Box::new(VoidBox::new())
}
pub fn clear(&mut self) -> Box<dyn NyashBox> {
self.output_file = None;
self.emit_obj_out = None;
self.plugin_paths = None;
Box::new(VoidBox::new())
}
/// Apply staged config to environment for CLI/runner consumption /// Apply staged config to environment for CLI/runner consumption
pub fn apply(&self) -> Box<dyn NyashBox> { pub fn apply(&self) -> Box<dyn NyashBox> {
if let Some(p) = &self.output_file { std::env::set_var("NYASH_AOT_OUT", p); } if let Some(p) = &self.output_file {
if let Some(p) = &self.emit_obj_out { std::env::set_var("NYASH_AOT_OBJECT_OUT", p); } std::env::set_var("NYASH_AOT_OUT", p);
if let Some(p) = &self.plugin_paths { std::env::set_var("NYASH_PLUGIN_PATHS", p); } }
if let Some(p) = &self.emit_obj_out {
std::env::set_var("NYASH_AOT_OBJECT_OUT", p);
}
if let Some(p) = &self.plugin_paths {
std::env::set_var("NYASH_PLUGIN_PATHS", p);
}
Box::new(VoidBox::new()) Box::new(VoidBox::new())
} }
pub fn summary(&self) -> StringBox { pub fn summary(&self) -> StringBox {
let s = format!( let s = format!(
"output={} obj_out={} plugin_paths={}", "output={} obj_out={} plugin_paths={}",
self.output_file.clone().unwrap_or_else(|| "<none>".to_string()), self.output_file
self.emit_obj_out.clone().unwrap_or_else(|| "<none>".to_string()), .clone()
self.plugin_paths.clone().unwrap_or_else(|| "<none>".to_string()), .unwrap_or_else(|| "<none>".to_string()),
self.emit_obj_out
.clone()
.unwrap_or_else(|| "<none>".to_string()),
self.plugin_paths
.clone()
.unwrap_or_else(|| "<none>".to_string()),
); );
StringBox::new(s) StringBox::new(s)
} }

View File

@ -2,10 +2,10 @@
// Nyashの箱システムによる配列・リスト操作を提供します。 // Nyashの箱システムによる配列・リスト操作を提供します。
// RwLockパターンで内部可変性を実現Phase 9.75-B Arc<Mutex>削除) // RwLockパターンで内部可変性を実現Phase 9.75-B Arc<Mutex>削除)
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use std::any::Any; use std::any::Any;
use std::sync::{Arc, RwLock};
use std::fmt::Display; use std::fmt::Display;
use std::sync::{Arc, RwLock};
pub struct ArrayBox { pub struct ArrayBox {
pub items: Arc<RwLock<Vec<Box<dyn NyashBox>>>>, // Arc追加 pub items: Arc<RwLock<Vec<Box<dyn NyashBox>>>>, // Arc追加
@ -61,7 +61,11 @@ impl ArrayBox {
match items.get(idx) { match items.get(idx) {
Some(item) => { Some(item) => {
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
if item.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() { if item
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
return item.share_box(); return item.share_box();
} }
item.clone_box() item.clone_box()
@ -161,32 +165,44 @@ impl ArrayBox {
// Try to compare as numbers first // Try to compare as numbers first
if let (Some(a_int), Some(b_int)) = ( if let (Some(a_int), Some(b_int)) = (
a.as_any().downcast_ref::<IntegerBox>(), a.as_any().downcast_ref::<IntegerBox>(),
b.as_any().downcast_ref::<IntegerBox>() b.as_any().downcast_ref::<IntegerBox>(),
) { ) {
return a_int.value.cmp(&b_int.value); return a_int.value.cmp(&b_int.value);
} }
// Try FloatBox comparison // Try FloatBox comparison
if let (Some(a_float), Some(b_float)) = ( if let (Some(a_float), Some(b_float)) = (
a.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>(), a.as_any()
b.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>() .downcast_ref::<crate::boxes::math_box::FloatBox>(),
b.as_any()
.downcast_ref::<crate::boxes::math_box::FloatBox>(),
) { ) {
return a_float.value.partial_cmp(&b_float.value).unwrap_or(Ordering::Equal); return a_float
.value
.partial_cmp(&b_float.value)
.unwrap_or(Ordering::Equal);
} }
// Mixed numeric types // Mixed numeric types
if let (Some(a_int), Some(b_float)) = ( if let (Some(a_int), Some(b_float)) = (
a.as_any().downcast_ref::<IntegerBox>(), a.as_any().downcast_ref::<IntegerBox>(),
b.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>() b.as_any()
.downcast_ref::<crate::boxes::math_box::FloatBox>(),
) { ) {
return (a_int.value as f64).partial_cmp(&b_float.value).unwrap_or(Ordering::Equal); return (a_int.value as f64)
.partial_cmp(&b_float.value)
.unwrap_or(Ordering::Equal);
} }
if let (Some(a_float), Some(b_int)) = ( if let (Some(a_float), Some(b_int)) = (
a.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>(), a.as_any()
b.as_any().downcast_ref::<IntegerBox>() .downcast_ref::<crate::boxes::math_box::FloatBox>(),
b.as_any().downcast_ref::<IntegerBox>(),
) { ) {
return a_float.value.partial_cmp(&(b_int.value as f64)).unwrap_or(Ordering::Equal); return a_float
.value
.partial_cmp(&(b_int.value as f64))
.unwrap_or(Ordering::Equal);
} }
// Fall back to string comparison // Fall back to string comparison
@ -217,7 +233,9 @@ impl ArrayBox {
start_int.value as usize start_int.value as usize
} }
} else { } else {
return Box::new(StringBox::new("Error: slice() start index must be an integer")); return Box::new(StringBox::new(
"Error: slice() start index must be an integer",
));
}; };
let end_idx = if let Some(end_int) = end.as_any().downcast_ref::<IntegerBox>() { let end_idx = if let Some(end_int) = end.as_any().downcast_ref::<IntegerBox>() {
@ -227,7 +245,9 @@ impl ArrayBox {
(end_int.value as usize).min(items.len()) (end_int.value as usize).min(items.len())
} }
} else { } else {
return Box::new(StringBox::new("Error: slice() end index must be an integer")); return Box::new(StringBox::new(
"Error: slice() end index must be an integer",
));
}; };
// Validate indices // Validate indices
@ -240,7 +260,11 @@ impl ArrayBox {
.iter() .iter()
.map(|item| { .map(|item| {
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
if item.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() { if item
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
return item.share_box(); return item.share_box();
} }
item.clone_box() item.clone_box()
@ -256,10 +280,15 @@ impl Clone for ArrayBox {
fn clone(&self) -> Self { fn clone(&self) -> Self {
// ディープコピー(独立インスタンス) // ディープコピー(独立インスタンス)
let items_guard = self.items.read().unwrap(); let items_guard = self.items.read().unwrap();
let cloned_items: Vec<Box<dyn NyashBox>> = items_guard.iter() let cloned_items: Vec<Box<dyn NyashBox>> = items_guard
.iter()
.map(|item| { .map(|item| {
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
if item.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() { if item
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
return item.share_box(); return item.share_box();
} }
item.clone_box() item.clone_box()
@ -284,7 +313,8 @@ impl BoxCore for ArrayBox {
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let items = self.items.read().unwrap(); let items = self.items.read().unwrap();
let strings: Vec<String> = items.iter() let strings: Vec<String> = items
.iter()
.map(|item| item.to_string_box().value) .map(|item| item.to_string_box().value)
.collect(); .collect();
write!(f, "[{}]", strings.join(", ")) write!(f, "[{}]", strings.join(", "))
@ -306,7 +336,9 @@ impl Display for ArrayBox {
} }
impl NyashBox for ArrayBox { impl NyashBox for ArrayBox {
fn is_identity(&self) -> bool { true } fn is_identity(&self) -> bool {
true
}
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone()) Box::new(self.clone())
} }
@ -322,18 +354,17 @@ impl NyashBox for ArrayBox {
fn to_string_box(&self) -> StringBox { fn to_string_box(&self) -> StringBox {
let items = self.items.read().unwrap(); let items = self.items.read().unwrap();
let strings: Vec<String> = items.iter() let strings: Vec<String> = items
.iter()
.map(|item| item.to_string_box().value) .map(|item| item.to_string_box().value)
.collect(); .collect();
StringBox::new(format!("[{}]", strings.join(", "))) StringBox::new(format!("[{}]", strings.join(", ")))
} }
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
"ArrayBox" "ArrayBox"
} }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_array) = other.as_any().downcast_ref::<ArrayBox>() { if let Some(other_array) = other.as_any().downcast_ref::<ArrayBox>() {
let self_items = self.items.read().unwrap(); let self_items = self.items.read().unwrap();

View File

@ -50,7 +50,7 @@
* ``` * ```
*/ */
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any; use std::any::Any;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
@ -58,8 +58,8 @@ use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use web_sys::{ use web_sys::{
AudioContext, AudioBuffer, AudioBufferSourceNode, GainNode, AnalyserNode, AudioBuffer, AudioBufferSourceNode, AudioContext, AudioDestinationNode, GainNode,
AnalyserNode, AudioDestinationNode, PeriodicWave, OscillatorNode OscillatorNode, PeriodicWave,
}; };
/// 音声管理Box /// 音声管理Box
@ -109,8 +109,11 @@ impl AudioBox {
gain.gain().set_value(self.volume as f32); gain.gain().set_value(self.volume as f32);
// ノード接続 // ノード接続
oscillator.connect_with_audio_node(&gain).unwrap_or_default(); oscillator
gain.connect_with_audio_node(&context.destination()).unwrap_or_default(); .connect_with_audio_node(&gain)
.unwrap_or_default();
gain.connect_with_audio_node(&context.destination())
.unwrap_or_default();
// 再生 // 再生
let start_time = context.current_time(); let start_time = context.current_time();
@ -153,7 +156,8 @@ impl AudioBox {
if let Ok(gain) = context.create_gain() { if let Ok(gain) = context.create_gain() {
gain.gain().set_value(self.volume as f32); gain.gain().set_value(self.volume as f32);
source.connect_with_audio_node(&gain).unwrap_or_default(); source.connect_with_audio_node(&gain).unwrap_or_default();
gain.connect_with_audio_node(&context.destination()).unwrap_or_default(); gain.connect_with_audio_node(&context.destination())
.unwrap_or_default();
source.start().unwrap_or_default(); source.start().unwrap_or_default();
return true; return true;
@ -201,7 +205,10 @@ impl AudioBox {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
/// Non-WASM環境用のダミー実装 /// Non-WASM環境用のダミー実装
pub fn create_tone(&self, frequency: f64, duration: f64) -> bool { pub fn create_tone(&self, frequency: f64, duration: f64) -> bool {
println!("AudioBox: Playing tone {}Hz for {}ms (simulated)", frequency, duration); println!(
"AudioBox: Playing tone {}Hz for {}ms (simulated)",
frequency, duration
);
true true
} }
@ -220,13 +227,17 @@ impl AudioBox {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub fn get_frequency_data(&self) -> Vec<u8> { pub fn get_frequency_data(&self) -> Vec<u8> {
// シミュレーション用のダミーデータ // シミュレーション用のダミーデータ
(0..64).map(|i| ((i as f64 * 4.0).sin() * 128.0 + 128.0) as u8).collect() (0..64)
.map(|i| ((i as f64 * 4.0).sin() * 128.0 + 128.0) as u8)
.collect()
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub fn get_waveform_data(&self) -> Vec<u8> { pub fn get_waveform_data(&self) -> Vec<u8> {
// シミュレーション用のダミーデータ // シミュレーション用のダミーデータ
(0..128).map(|i| ((i as f64 * 0.1).sin() * 64.0 + 128.0) as u8).collect() (0..128)
.map(|i| ((i as f64 * 0.1).sin() * 64.0 + 128.0) as u8)
.collect()
} }
/// オーディオコンテキストの状態を確認 /// オーディオコンテキストの状態を確認
@ -266,7 +277,11 @@ impl BoxCore for AudioBox {
} }
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "AudioBox(volume={:.2}, playing={})", self.volume, self.is_playing) write!(
f,
"AudioBox(volume={:.2}, playing={})",
self.volume, self.is_playing
)
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
@ -289,7 +304,10 @@ impl NyashBox for AudioBox {
} }
fn to_string_box(&self) -> StringBox { fn to_string_box(&self) -> StringBox {
StringBox::new(format!("AudioBox(volume={:.2}, playing={})", self.volume, self.is_playing)) StringBox::new(format!(
"AudioBox(volume={:.2}, playing={})",
self.volume, self.is_playing
))
} }
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {

View File

@ -38,7 +38,7 @@
* - `a or b` - OR演算子 * - `a or b` - OR演算子
*/ */
use crate::box_trait::{NyashBox, BoxCore, BoxBase}; use crate::box_trait::{BoxBase, BoxCore, NyashBox};
use std::any::Any; use std::any::Any;
use std::fmt::Display; use std::fmt::Display;
@ -83,7 +83,6 @@ impl NyashBox for BoolBox {
"BoolBox" "BoolBox"
} }
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone()) Box::new(self.clone())
} }

View File

@ -28,11 +28,11 @@
* ``` * ```
*/ */
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use crate::boxes::array::ArrayBox; use crate::boxes::array::ArrayBox;
use std::any::Any; use std::any::Any;
use std::sync::{Arc, RwLock}; // Arc追加
use std::fmt::Display; use std::fmt::Display;
use std::sync::{Arc, RwLock}; // Arc追加
pub struct BufferBox { pub struct BufferBox {
data: Arc<RwLock<Vec<u8>>>, // Arc追加 data: Arc<RwLock<Vec<u8>>>, // Arc追加
@ -67,7 +67,10 @@ impl BufferBox {
/// データを書き込む /// データを書き込む
pub fn write(&self, data: Box<dyn NyashBox>) -> Box<dyn NyashBox> { pub fn write(&self, data: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
// ArrayBoxから変換 - use crate::boxes::array::ArrayBox directly // ArrayBoxから変換 - use crate::boxes::array::ArrayBox directly
if let Some(array_box) = data.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() { if let Some(array_box) = data
.as_any()
.downcast_ref::<crate::boxes::array::ArrayBox>()
{
let mut buffer = self.data.write().unwrap(); let mut buffer = self.data.write().unwrap();
let items = array_box.items.read().unwrap(); let items = array_box.items.read().unwrap();
for item in items.iter() { for item in items.iter() {
@ -80,7 +83,10 @@ impl BufferBox {
Box::new(IntegerBox::new(buffer.len() as i64)) Box::new(IntegerBox::new(buffer.len() as i64))
} else { } else {
let type_name = data.type_name(); let type_name = data.type_name();
Box::new(StringBox::new(&format!("Error: write() requires ArrayBox of integers, got {}", type_name))) Box::new(StringBox::new(&format!(
"Error: write() requires ArrayBox of integers, got {}",
type_name
)))
} }
} }
@ -139,7 +145,7 @@ impl BufferBox {
pub fn slice(&self, start: Box<dyn NyashBox>, end: Box<dyn NyashBox>) -> Box<dyn NyashBox> { pub fn slice(&self, start: Box<dyn NyashBox>, end: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(start_int), Some(end_int)) = ( if let (Some(start_int), Some(end_int)) = (
start.as_any().downcast_ref::<IntegerBox>(), start.as_any().downcast_ref::<IntegerBox>(),
end.as_any().downcast_ref::<IntegerBox>() end.as_any().downcast_ref::<IntegerBox>(),
) { ) {
let data = self.data.read().unwrap(); let data = self.data.read().unwrap();
let start = (start_int.value as usize).min(data.len()); let start = (start_int.value as usize).min(data.len());
@ -246,12 +252,10 @@ impl NyashBox for BufferBox {
StringBox::new(format!("BufferBox({} bytes)", data.len())) StringBox::new(format!("BufferBox({} bytes)", data.len()))
} }
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
"BufferBox" "BufferBox"
} }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_buffer) = other.as_any().downcast_ref::<BufferBox>() { if let Some(other_buffer) = other.as_any().downcast_ref::<BufferBox>() {
// RwLock内容を比較 // RwLock内容を比較

View File

@ -61,17 +61,14 @@
* ``` * ```
*/ */
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any; use std::any::Any;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use web_sys::{ use web_sys::{Element, EventTarget, HtmlCanvasElement, KeyboardEvent, MouseEvent, TouchEvent};
HtmlCanvasElement, MouseEvent, TouchEvent, KeyboardEvent,
EventTarget, Element
};
/// Canvas入力イベント管理Box /// Canvas入力イベント管理Box
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -140,7 +137,8 @@ impl CanvasEventBox {
callback.call0(&JsValue::NULL).unwrap_or_default(); callback.call0(&JsValue::NULL).unwrap_or_default();
}) as Box<dyn FnMut(MouseEvent)>); }) as Box<dyn FnMut(MouseEvent)>);
canvas.add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref()) canvas
.add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())
.unwrap_or_default(); .unwrap_or_default();
closure.forget(); // メモリリークを防ぐため通常は適切な管理が必要 closure.forget(); // メモリリークを防ぐため通常は適切な管理が必要
} }
@ -154,7 +152,8 @@ impl CanvasEventBox {
callback.call0(&JsValue::NULL).unwrap_or_default(); callback.call0(&JsValue::NULL).unwrap_or_default();
}) as Box<dyn FnMut(MouseEvent)>); }) as Box<dyn FnMut(MouseEvent)>);
canvas.add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref()) canvas
.add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref())
.unwrap_or_default(); .unwrap_or_default();
closure.forget(); closure.forget();
} }
@ -168,7 +167,8 @@ impl CanvasEventBox {
callback.call0(&JsValue::NULL).unwrap_or_default(); callback.call0(&JsValue::NULL).unwrap_or_default();
}) as Box<dyn FnMut(MouseEvent)>); }) as Box<dyn FnMut(MouseEvent)>);
canvas.add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref()) canvas
.add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref())
.unwrap_or_default(); .unwrap_or_default();
closure.forget(); closure.forget();
} }
@ -182,7 +182,8 @@ impl CanvasEventBox {
callback.call0(&JsValue::NULL).unwrap_or_default(); callback.call0(&JsValue::NULL).unwrap_or_default();
}) as Box<dyn FnMut(MouseEvent)>); }) as Box<dyn FnMut(MouseEvent)>);
canvas.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref()) canvas
.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref())
.unwrap_or_default(); .unwrap_or_default();
closure.forget(); closure.forget();
} }
@ -196,7 +197,8 @@ impl CanvasEventBox {
callback.call0(&JsValue::NULL).unwrap_or_default(); callback.call0(&JsValue::NULL).unwrap_or_default();
}) as Box<dyn FnMut(TouchEvent)>); }) as Box<dyn FnMut(TouchEvent)>);
canvas.add_event_listener_with_callback("touchstart", closure.as_ref().unchecked_ref()) canvas
.add_event_listener_with_callback("touchstart", closure.as_ref().unchecked_ref())
.unwrap_or_default(); .unwrap_or_default();
closure.forget(); closure.forget();
} }
@ -210,7 +212,8 @@ impl CanvasEventBox {
callback.call0(&JsValue::NULL).unwrap_or_default(); callback.call0(&JsValue::NULL).unwrap_or_default();
}) as Box<dyn FnMut(KeyboardEvent)>); }) as Box<dyn FnMut(KeyboardEvent)>);
window.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref()) window
.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())
.unwrap_or_default(); .unwrap_or_default();
closure.forget(); closure.forget();
} }

View File

@ -47,7 +47,7 @@
* ``` * ```
*/ */
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use crate::boxes::TimerBox; use crate::boxes::TimerBox;
use std::any::Any; use std::any::Any;
@ -108,10 +108,14 @@ impl CanvasLoopBox {
// アニメーションフレーム用のクロージャを作成 // アニメーションフレーム用のクロージャを作成
let closure = Closure::wrap(Box::new(move |time: f64| { let closure = Closure::wrap(Box::new(move |time: f64| {
// ここでフレーム処理を実行 // ここでフレーム処理を実行
callback.call1(&JsValue::NULL, &JsValue::from_f64(time)).unwrap_or_default(); callback
.call1(&JsValue::NULL, &JsValue::from_f64(time))
.unwrap_or_default();
}) as Box<dyn FnMut(f64)>); }) as Box<dyn FnMut(f64)>);
let id = self.timer.request_animation_frame(closure.as_ref().unchecked_ref()); let id = self
.timer
.request_animation_frame(closure.as_ref().unchecked_ref());
self.animation_id = Some(id); self.animation_id = Some(id);
closure.forget(); // クロージャの所有権を手放す closure.forget(); // クロージャの所有権を手放す
@ -170,10 +174,14 @@ impl CanvasLoopBox {
self.last_frame_time = self.timer.now(); // 時間をリセット self.last_frame_time = self.timer.now(); // 時間をリセット
let closure = Closure::wrap(Box::new(move |time: f64| { let closure = Closure::wrap(Box::new(move |time: f64| {
callback.call1(&JsValue::NULL, &JsValue::from_f64(time)).unwrap_or_default(); callback
.call1(&JsValue::NULL, &JsValue::from_f64(time))
.unwrap_or_default();
}) as Box<dyn FnMut(f64)>); }) as Box<dyn FnMut(f64)>);
let id = self.timer.request_animation_frame(closure.as_ref().unchecked_ref()); let id = self
.timer
.request_animation_frame(closure.as_ref().unchecked_ref());
self.animation_id = Some(id); self.animation_id = Some(id);
closure.forget(); closure.forget();
@ -268,7 +276,11 @@ impl BoxCore for CanvasLoopBox {
} }
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "CanvasLoopBox(running={}, fps={:.1})", self.is_running, self.fps) write!(
f,
"CanvasLoopBox(running={}, fps={:.1})",
self.is_running, self.fps
)
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
@ -291,7 +303,10 @@ impl NyashBox for CanvasLoopBox {
} }
fn to_string_box(&self) -> StringBox { fn to_string_box(&self) -> StringBox {
StringBox::new(format!("CanvasLoopBox(running={}, fps={:.1})", self.is_running, self.fps)) StringBox::new(format!(
"CanvasLoopBox(running={}, fps={:.1})",
self.is_running, self.fps
))
} }
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {

View File

@ -45,7 +45,7 @@
* ``` * ```
*/ */
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any; use std::any::Any;
use std::fmt::Display; use std::fmt::Display;
@ -59,7 +59,9 @@ pub struct ConsoleBox {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
impl ConsoleBox { impl ConsoleBox {
pub fn new() -> Self { pub fn new() -> Self {
Self { base: BoxBase::new() } Self {
base: BoxBase::new(),
}
} }
/// Log messages to browser console /// Log messages to browser console
@ -128,7 +130,6 @@ impl NyashBox for ConsoleBox {
fn share_box(&self) -> Box<dyn NyashBox> { fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box() self.clone_box()
} }
} }
// Non-WASM版 - モックアップ実装 // Non-WASM版 - モックアップ実装
@ -141,7 +142,9 @@ pub struct ConsoleBox {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
impl ConsoleBox { impl ConsoleBox {
pub fn new() -> Self { pub fn new() -> Self {
Self { base: BoxBase::new() } Self {
base: BoxBase::new(),
}
} }
/// Mock log method for non-WASM environments /// Mock log method for non-WASM environments
@ -207,10 +210,8 @@ impl NyashBox for ConsoleBox {
fn share_box(&self) -> Box<dyn NyashBox> { fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box() self.clone_box()
} }
} }
// Display implementations for both WASM and non-WASM versions // Display implementations for both WASM and non-WASM versions
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
impl Display for ConsoleBox { impl Display for ConsoleBox {

View File

@ -99,13 +99,13 @@
* - call stackは直近100件まで自動保持 * - call stackは直近100件まで自動保持
*/ */
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use crate::instance_v2::InstanceBox;
use crate::interpreter::RuntimeError;
use chrono::Local;
use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::RwLock; use std::sync::RwLock;
use chrono::Local;
use crate::box_trait::{BoxCore, BoxBase, NyashBox, StringBox, BoolBox, VoidBox};
use crate::interpreter::RuntimeError;
use crate::instance_v2::InstanceBox;
use std::any::Any;
#[derive(Debug)] #[derive(Debug)]
pub struct DebugBox { pub struct DebugBox {
@ -156,7 +156,11 @@ impl DebugBox {
Ok(Box::new(VoidBox::new())) Ok(Box::new(VoidBox::new()))
} }
pub fn track_box(&self, box_value: &dyn NyashBox, name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> { pub fn track_box(
&self,
box_value: &dyn NyashBox,
name: &str,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let enabled = self.tracking_enabled.read().unwrap(); let enabled = self.tracking_enabled.read().unwrap();
if !*enabled { if !*enabled {
return Ok(Box::new(VoidBox::new())); return Ok(Box::new(VoidBox::new()));
@ -192,7 +196,10 @@ impl DebugBox {
pub fn dump_all(&self) -> Result<Box<dyn NyashBox>, RuntimeError> { pub fn dump_all(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
let tracked = self.tracked_boxes.read().unwrap(); let tracked = self.tracked_boxes.read().unwrap();
let mut output = String::from("=== Box State Dump ===\n"); let mut output = String::from("=== Box State Dump ===\n");
output.push_str(&format!("Time: {}\n", Local::now().format("%Y-%m-%d %H:%M:%S"))); output.push_str(&format!(
"Time: {}\n",
Local::now().format("%Y-%m-%d %H:%M:%S")
));
output.push_str(&format!("Total tracked boxes: {}\n\n", tracked.len())); output.push_str(&format!("Total tracked boxes: {}\n\n", tracked.len()));
for (name, info) in tracked.iter() { for (name, info) in tracked.iter() {
@ -212,8 +219,7 @@ impl DebugBox {
let content = dump_result.to_string_box().value; let content = dump_result.to_string_box().value;
// Write to file using std::fs // Write to file using std::fs
std::fs::write(filename, content) std::fs::write(filename, content).map_err(|e| RuntimeError::InvalidOperation {
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("Failed to write debug file: {}", e), message: format!("Failed to write debug file: {}", e),
})?; })?;
@ -221,7 +227,11 @@ impl DebugBox {
Ok(Box::new(VoidBox::new())) Ok(Box::new(VoidBox::new()))
} }
pub fn watch(&self, box_value: &dyn NyashBox, name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> { pub fn watch(
&self,
box_value: &dyn NyashBox,
name: &str,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let value_str = box_value.to_string_box().value; let value_str = box_value.to_string_box().value;
let type_name = box_value.type_name(); let type_name = box_value.type_name();
@ -256,7 +266,11 @@ impl DebugBox {
Ok(Box::new(VoidBox::new())) Ok(Box::new(VoidBox::new()))
} }
pub fn trace_call(&self, function_name: &str, args: Vec<String>) -> Result<Box<dyn NyashBox>, RuntimeError> { pub fn trace_call(
&self,
function_name: &str,
args: Vec<String>,
) -> Result<Box<dyn NyashBox>, RuntimeError> {
let mut stack = self.call_stack.write().unwrap(); let mut stack = self.call_stack.write().unwrap();
stack.push(CallInfo { stack.push(CallInfo {
function_name: function_name.to_string(), function_name: function_name.to_string(),
@ -277,7 +291,8 @@ impl DebugBox {
let mut output = String::from("=== Call Stack ===\n"); let mut output = String::from("=== Call Stack ===\n");
for (i, call) in stack.iter().enumerate() { for (i, call) in stack.iter().enumerate() {
output.push_str(&format!("{}: [{}] {}({})\n", output.push_str(&format!(
"{}: [{}] {}({})\n",
i, i,
call.timestamp, call.timestamp,
call.function_name, call.function_name,
@ -306,13 +321,18 @@ impl DebugBox {
pub fn get_tracked_count(&self) -> Result<Box<dyn NyashBox>, RuntimeError> { pub fn get_tracked_count(&self) -> Result<Box<dyn NyashBox>, RuntimeError> {
let tracked = self.tracked_boxes.read().unwrap(); let tracked = self.tracked_boxes.read().unwrap();
Ok(Box::new(crate::box_trait::IntegerBox::new(tracked.len() as i64))) Ok(Box::new(crate::box_trait::IntegerBox::new(
tracked.len() as i64
)))
} }
// --- Phase 1: JIT/Plugin shim tracing --- // --- Phase 1: JIT/Plugin shim tracing ---
pub fn trace_plugin_calls(&self, on: bool) -> Result<Box<dyn NyashBox>, RuntimeError> { pub fn trace_plugin_calls(&self, on: bool) -> Result<Box<dyn NyashBox>, RuntimeError> {
crate::jit::shim_trace::set_enabled(on); crate::jit::shim_trace::set_enabled(on);
println!("[DEBUG] JIT shim trace: {}", if on {"ENABLED"} else {"DISABLED"}); println!(
"[DEBUG] JIT shim trace: {}",
if on { "ENABLED" } else { "DISABLED" }
);
Ok(Box::new(VoidBox::new())) Ok(Box::new(VoidBox::new()))
} }
@ -398,6 +418,4 @@ impl NyashBox for DebugBox {
fn share_box(&self) -> Box<dyn NyashBox> { fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box() self.clone_box()
} }
} }

View File

@ -1,4 +1,4 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any; use std::any::Any;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -20,13 +20,19 @@ impl DebugConfigBox {
Self { Self {
base: BoxBase::new(), base: BoxBase::new(),
jit_events: std::env::var("NYASH_JIT_EVENTS").ok().as_deref() == Some("1"), jit_events: std::env::var("NYASH_JIT_EVENTS").ok().as_deref() == Some("1"),
jit_events_compile: std::env::var("NYASH_JIT_EVENTS_COMPILE").ok().as_deref() == Some("1"), jit_events_compile: std::env::var("NYASH_JIT_EVENTS_COMPILE").ok().as_deref()
jit_events_runtime: std::env::var("NYASH_JIT_EVENTS_RUNTIME").ok().as_deref() == Some("1"), == Some("1"),
jit_events_runtime: std::env::var("NYASH_JIT_EVENTS_RUNTIME").ok().as_deref()
== Some("1"),
jit_stats: std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1"), jit_stats: std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1"),
jit_stats_json: std::env::var("NYASH_JIT_STATS_JSON").ok().as_deref() == Some("1"), jit_stats_json: std::env::var("NYASH_JIT_STATS_JSON").ok().as_deref() == Some("1"),
jit_dump: std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1"), jit_dump: std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1"),
jit_dot_path: std::env::var("NYASH_JIT_DOT").ok().filter(|s| !s.is_empty()), jit_dot_path: std::env::var("NYASH_JIT_DOT")
jit_events_path: std::env::var("NYASH_JIT_EVENTS_PATH").ok().filter(|s| !s.is_empty()), .ok()
.filter(|s| !s.is_empty()),
jit_events_path: std::env::var("NYASH_JIT_EVENTS_PATH")
.ok()
.filter(|s| !s.is_empty()),
} }
} }
@ -38,7 +44,7 @@ impl DebugConfigBox {
"jit_stats" => self.jit_stats = on, "jit_stats" => self.jit_stats = on,
"jit_stats_json" => self.jit_stats_json = on, "jit_stats_json" => self.jit_stats_json = on,
"jit_dump" => self.jit_dump = on, "jit_dump" => self.jit_dump = on,
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name))) _ => return Box::new(StringBox::new(format!("Unknown flag: {}", name))),
} }
Box::new(VoidBox::new()) Box::new(VoidBox::new())
} }
@ -47,7 +53,7 @@ impl DebugConfigBox {
match name { match name {
"jit_dot" | "jit_dot_path" => self.jit_dot_path = Some(value.to_string()), "jit_dot" | "jit_dot_path" => self.jit_dot_path = Some(value.to_string()),
"jit_events_path" => self.jit_events_path = Some(value.to_string()), "jit_events_path" => self.jit_events_path = Some(value.to_string()),
_ => return Box::new(StringBox::new(format!("Unknown path: {}", name))) _ => return Box::new(StringBox::new(format!("Unknown path: {}", name))),
} }
Box::new(VoidBox::new()) Box::new(VoidBox::new())
} }
@ -75,17 +81,29 @@ impl DebugConfigBox {
} }
pub fn apply(&self) -> Box<dyn NyashBox> { pub fn apply(&self) -> Box<dyn NyashBox> {
let setb = |k: &str, v: bool| { if v { std::env::set_var(k, "1"); } else { std::env::remove_var(k); } }; let setb = |k: &str, v: bool| {
if v {
std::env::set_var(k, "1");
} else {
std::env::remove_var(k);
}
};
setb("NYASH_JIT_EVENTS", self.jit_events); setb("NYASH_JIT_EVENTS", self.jit_events);
setb("NYASH_JIT_EVENTS_COMPILE", self.jit_events_compile); setb("NYASH_JIT_EVENTS_COMPILE", self.jit_events_compile);
setb("NYASH_JIT_EVENTS_RUNTIME", self.jit_events_runtime); setb("NYASH_JIT_EVENTS_RUNTIME", self.jit_events_runtime);
setb("NYASH_JIT_STATS", self.jit_stats); setb("NYASH_JIT_STATS", self.jit_stats);
setb("NYASH_JIT_STATS_JSON", self.jit_stats_json); setb("NYASH_JIT_STATS_JSON", self.jit_stats_json);
setb("NYASH_JIT_DUMP", self.jit_dump); setb("NYASH_JIT_DUMP", self.jit_dump);
if let Some(p) = &self.jit_dot_path { std::env::set_var("NYASH_JIT_DOT", p); } if let Some(p) = &self.jit_dot_path {
else { std::env::remove_var("NYASH_JIT_DOT"); } std::env::set_var("NYASH_JIT_DOT", p);
if let Some(p) = &self.jit_events_path { std::env::set_var("NYASH_JIT_EVENTS_PATH", p); } } else {
else { std::env::remove_var("NYASH_JIT_EVENTS_PATH"); } std::env::remove_var("NYASH_JIT_DOT");
}
if let Some(p) = &self.jit_events_path {
std::env::set_var("NYASH_JIT_EVENTS_PATH", p);
} else {
std::env::remove_var("NYASH_JIT_EVENTS_PATH");
}
// If any events are enabled and threshold is not set, default to 1 so lowering runs early // If any events are enabled and threshold is not set, default to 1 so lowering runs early
if (self.jit_events || self.jit_events_compile || self.jit_events_runtime) if (self.jit_events || self.jit_events_compile || self.jit_events_runtime)
&& std::env::var("NYASH_JIT_THRESHOLD").is_err() && std::env::var("NYASH_JIT_THRESHOLD").is_err()
@ -108,17 +126,40 @@ impl DebugConfigBox {
} }
impl BoxCore for DebugConfigBox { impl BoxCore for DebugConfigBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "DebugConfigBox") } }
fn as_any(&self) -> &dyn Any { self } fn parent_type_id(&self) -> Option<std::any::TypeId> {
fn as_any_mut(&mut self) -> &mut dyn Any { self } self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "DebugConfigBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl NyashBox for DebugConfigBox { impl NyashBox for DebugConfigBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<DebugConfigBox>()) } fn equals(&self, other: &dyn NyashBox) -> BoolBox {
fn type_name(&self) -> &'static str { "DebugConfigBox" } BoolBox::new(other.as_any().is::<DebugConfigBox>())
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone(), ..self.clone() }) } }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() } fn type_name(&self) -> &'static str {
fn to_string_box(&self) -> StringBox { StringBox::new("DebugConfigBox".to_string()) } "DebugConfigBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self {
base: self.base.clone(),
..self.clone()
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn to_string_box(&self) -> StringBox {
StringBox::new("DebugConfigBox".to_string())
}
} }

View File

@ -33,11 +33,11 @@
* - `run()`はブロッキング動作(アプリ終了まで制御を返さない) * - `run()`はブロッキング動作(アプリ終了まで制御を返さない)
*/ */
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use crate::interpreter::RuntimeError; use crate::interpreter::RuntimeError;
use eframe::{self, egui, epaint::Vec2};
use std::any::Any; use std::any::Any;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use eframe::{self, egui, epaint::Vec2};
/// EguiBox - GUI アプリケーションを包むBox /// EguiBox - GUI アプリケーションを包むBox
/// ///
@ -98,7 +98,7 @@ impl EguiBox {
/// 更新関数を設定 /// 更新関数を設定
pub fn set_update_fn<F>(&mut self, f: F) pub fn set_update_fn<F>(&mut self, f: F)
where where
F: Fn(&mut Box<dyn Any + Send + Sync>, &egui::Context) + Send + Sync + 'static F: Fn(&mut Box<dyn Any + Send + Sync>, &egui::Context) + Send + Sync + 'static,
{ {
self.update_fn = Some(Arc::new(f)); self.update_fn = Some(Arc::new(f));
} }
@ -128,7 +128,11 @@ impl BoxCore for EguiBox {
} }
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "EguiBox('{}', {}x{})", self.title, self.size.x, self.size.y) write!(
f,
"EguiBox('{}', {}x{})",
self.title, self.size.x, self.size.y
)
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
@ -148,9 +152,10 @@ impl std::fmt::Display for EguiBox {
impl NyashBox for EguiBox { impl NyashBox for EguiBox {
fn to_string_box(&self) -> StringBox { fn to_string_box(&self) -> StringBox {
StringBox::new( StringBox::new(format!(
format!("EguiBox('{}', {}x{})", self.title, self.size.x, self.size.y) "EguiBox('{}', {}x{})",
) self.title, self.size.x, self.size.y
))
} }
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
@ -162,7 +167,6 @@ impl NyashBox for EguiBox {
self.clone_box() self.clone_box()
} }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_egui) = other.as_any().downcast_ref::<EguiBox>() { if let Some(other_egui) = other.as_any().downcast_ref::<EguiBox>() {
BoolBox::new(self.title == other_egui.title && self.size == other_egui.size) BoolBox::new(self.title == other_egui.title && self.size == other_egui.size)
@ -174,7 +178,6 @@ impl NyashBox for EguiBox {
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
"EguiBox" "EguiBox"
} }
} }
// EguiBoxのメソッド実装実際にはインタープリターから呼ばれない // EguiBoxのメソッド実装実際にはインタープリターから呼ばれない
@ -203,11 +206,7 @@ impl EguiBox {
}; };
// 注意: これはブロッキング呼び出し // 注意: これはブロッキング呼び出し
let _ = eframe::run_native( let _ = eframe::run_native(&self.title, options, Box::new(|_cc| Ok(Box::new(app))));
&self.title,
options,
Box::new(|_cc| Ok(Box::new(app))),
);
Ok(()) Ok(())
} else { } else {

View File

@ -2,10 +2,10 @@
// Nyashの箱システムによるファイル入出力を提供します。 // Nyashの箱システムによるファイル入出力を提供します。
// 参考: 既存Boxの設計思想 // 参考: 既存Boxの設計思想
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any; use std::any::Any;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::io::{Read, Write, Result}; use std::io::{Read, Result, Write};
use std::sync::RwLock; use std::sync::RwLock;
#[derive(Debug)] #[derive(Debug)]
@ -38,8 +38,12 @@ impl FileBox {
Err(_) => { Err(_) => {
// Fallback: create with empty file handle - only for dispatch // Fallback: create with empty file handle - only for dispatch
use std::fs::OpenOptions; use std::fs::OpenOptions;
let file = OpenOptions::new().create(true).write(true).read(true) let file = OpenOptions::new()
.open("/dev/null").unwrap_or_else(|_| File::open("/dev/null").unwrap()); .create(true)
.write(true)
.read(true)
.open("/dev/null")
.unwrap_or_else(|_| File::open("/dev/null").unwrap());
FileBox { FileBox {
file: RwLock::new(file), file: RwLock::new(file),
path: String::new(), path: String::new(),
@ -50,7 +54,11 @@ impl FileBox {
} }
pub fn open(path: &str) -> Result<Self> { pub fn open(path: &str) -> Result<Self> {
let file = OpenOptions::new().read(true).write(true).create(true).open(path)?; let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)?;
Ok(FileBox { Ok(FileBox {
file: RwLock::new(file), file: RwLock::new(file),
path: path.to_string(), path: path.to_string(),
@ -137,7 +145,7 @@ impl NyashBox for FileBox {
// Note: Cannot truly clone a File handle, so create a new one to the same path // Note: Cannot truly clone a File handle, so create a new one to the same path
match FileBox::open(&self.path) { match FileBox::open(&self.path) {
Ok(new_file) => Box::new(new_file), Ok(new_file) => Box::new(new_file),
Err(_) => Box::new(crate::box_trait::VoidBox::new()) // Return void on error Err(_) => Box::new(crate::box_trait::VoidBox::new()), // Return void on error
} }
} }
@ -150,12 +158,10 @@ impl NyashBox for FileBox {
StringBox::new(format!("FileBox({})", self.path)) StringBox::new(format!("FileBox({})", self.path))
} }
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
"FileBox" "FileBox"
} }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_file) = other.as_any().downcast_ref::<FileBox>() { if let Some(other_file) = other.as_any().downcast_ref::<FileBox>() {
BoolBox::new(self.path == other_file.path) BoolBox::new(self.path == other_file.path)

View File

@ -1,8 +1,8 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
use crate::ast::ASTNode; use crate::ast::ASTNode;
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Weak; use std::sync::Weak;
use std::any::Any;
#[derive(Debug)] #[derive(Debug)]
pub struct ClosureEnv { pub struct ClosureEnv {
@ -11,7 +11,12 @@ pub struct ClosureEnv {
} }
impl ClosureEnv { impl ClosureEnv {
pub fn new() -> Self { Self { me_value: None, captures: HashMap::new() } } pub fn new() -> Self {
Self {
me_value: None,
captures: HashMap::new(),
}
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -24,32 +29,70 @@ pub struct FunctionBox {
impl FunctionBox { impl FunctionBox {
pub fn new(params: Vec<String>, body: Vec<ASTNode>) -> Self { pub fn new(params: Vec<String>, body: Vec<ASTNode>) -> Self {
Self { params, body, env: ClosureEnv::new(), base: BoxBase::new() } Self {
params,
body,
env: ClosureEnv::new(),
base: BoxBase::new(),
}
} }
pub fn with_env(params: Vec<String>, body: Vec<ASTNode>, env: ClosureEnv) -> Self { pub fn with_env(params: Vec<String>, body: Vec<ASTNode>, env: ClosureEnv) -> Self {
Self { params, body, env, base: BoxBase::new() } Self {
params,
body,
env,
base: BoxBase::new(),
}
} }
} }
impl BoxCore for FunctionBox { impl BoxCore for FunctionBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { }
write!(f, "FunctionBox(params={}, body={})", self.params.len(), self.body.len()) fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"FunctionBox(params={}, body={})",
self.params.len(),
self.body.len()
)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
} }
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
} }
impl NyashBox for FunctionBox { impl NyashBox for FunctionBox {
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) } fn clone_box(&self) -> Box<dyn NyashBox> {
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() } Box::new(self.clone())
fn to_string_box(&self) -> StringBox { StringBox::new(format!("FunctionBox(params={}, captures={}, body={})", self.params.len(), self.env.captures.len(), self.body.len())) } }
fn type_name(&self) -> &'static str { "FunctionBox" } fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn to_string_box(&self) -> StringBox {
StringBox::new(format!(
"FunctionBox(params={}, captures={}, body={})",
self.params.len(),
self.env.captures.len(),
self.body.len()
))
}
fn type_name(&self) -> &'static str {
"FunctionBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(o) = other.as_any().downcast_ref::<FunctionBox>() { if let Some(o) = other.as_any().downcast_ref::<FunctionBox>() {
BoolBox::new(self.box_id() == o.box_id()) BoolBox::new(self.box_id() == o.box_id())
} else { BoolBox::new(false) } } else {
BoolBox::new(false)
}
} }
} }
@ -57,13 +100,20 @@ impl Clone for ClosureEnv {
fn clone(&self) -> Self { fn clone(&self) -> Self {
let me_value = self.me_value.as_ref().map(|w| Weak::clone(w)); let me_value = self.me_value.as_ref().map(|w| Weak::clone(w));
let mut captures: HashMap<String, Box<dyn NyashBox>> = HashMap::new(); let mut captures: HashMap<String, Box<dyn NyashBox>> = HashMap::new();
for (k, v) in &self.captures { captures.insert(k.clone(), v.clone_box()); } for (k, v) in &self.captures {
captures.insert(k.clone(), v.clone_box());
}
Self { me_value, captures } Self { me_value, captures }
} }
} }
impl Clone for FunctionBox { impl Clone for FunctionBox {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { params: self.params.clone(), body: self.body.clone(), env: self.env.clone(), base: BoxBase::new() } Self {
params: self.params.clone(),
body: self.body.clone(),
env: self.env.clone(),
base: BoxBase::new(),
}
} }
} }

View File

@ -2,9 +2,9 @@
// Nyashの箱システムによる非同期処理の基盤を提供します。 // Nyashの箱システムによる非同期処理の基盤を提供します。
// 参考: 既存Boxの設計思想 // 参考: 既存Boxの設計思想
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any; use std::any::Any;
use std::sync::{Mutex, Condvar, Arc, Weak}; use std::sync::{Arc, Condvar, Mutex, Weak};
#[derive(Debug)] #[derive(Debug)]
pub struct NyashFutureBox { pub struct NyashFutureBox {
@ -33,7 +33,10 @@ pub struct FutureWeak {
impl Clone for NyashFutureBox { impl Clone for NyashFutureBox {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { inner: self.inner.clone(), base: BoxBase::new() } Self {
inner: self.inner.clone(),
base: BoxBase::new(),
}
} }
} }
@ -41,7 +44,10 @@ impl NyashFutureBox {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
inner: Arc::new(Inner { inner: Arc::new(Inner {
state: Mutex::new(FutureState { result: None, ready: false }), state: Mutex::new(FutureState {
result: None,
ready: false,
}),
cv: Condvar::new(), cv: Condvar::new(),
}), }),
base: BoxBase::new(), base: BoxBase::new(),
@ -71,7 +77,11 @@ impl NyashFutureBox {
} }
/// Create a non-owning weak handle to this Future's state /// Create a non-owning weak handle to this Future's state
pub fn downgrade(&self) -> FutureWeak { FutureWeak { inner: Arc::downgrade(&self.inner) } } pub fn downgrade(&self) -> FutureWeak {
FutureWeak {
inner: Arc::downgrade(&self.inner),
}
}
} }
impl NyashBox for NyashFutureBox { impl NyashBox for NyashFutureBox {
@ -98,12 +108,10 @@ impl NyashBox for NyashFutureBox {
} }
} }
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
"NyashFutureBox" "NyashFutureBox"
} }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_future) = other.as_any().downcast_ref::<NyashFutureBox>() { if let Some(other_future) = other.as_any().downcast_ref::<NyashFutureBox>() {
BoolBox::new(self.base.id == other_future.base.id) BoolBox::new(self.base.id == other_future.base.id)
@ -164,6 +172,8 @@ impl FutureBox {
impl FutureWeak { impl FutureWeak {
/// Try to upgrade and check readiness /// Try to upgrade and check readiness
pub(crate) fn is_ready(&self) -> Option<bool> { pub(crate) fn is_ready(&self) -> Option<bool> {
self.inner.upgrade().map(|arc| arc.state.lock().unwrap().ready) self.inner
.upgrade()
.map(|arc| arc.state.lock().unwrap().ready)
} }
} }

View File

@ -1,8 +1,13 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any; use std::any::Any;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct GcConfigBox { base: BoxBase, counting: bool, trace: bool, barrier_strict: bool } pub struct GcConfigBox {
base: BoxBase,
counting: bool,
trace: bool,
barrier_strict: bool,
}
impl GcConfigBox { impl GcConfigBox {
pub fn new() -> Self { pub fn new() -> Self {
@ -18,7 +23,7 @@ impl GcConfigBox {
"counting" => self.counting = on, "counting" => self.counting = on,
"trace" => self.trace = on, "trace" => self.trace = on,
"barrier_strict" | "strict" => self.barrier_strict = on, "barrier_strict" | "strict" => self.barrier_strict = on,
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name))) _ => return Box::new(StringBox::new(format!("Unknown flag: {}", name))),
} }
Box::new(VoidBox::new()) Box::new(VoidBox::new())
} }
@ -32,31 +37,59 @@ impl GcConfigBox {
Box::new(BoolBox::new(v)) Box::new(BoolBox::new(v))
} }
pub fn apply(&self) -> Box<dyn NyashBox> { pub fn apply(&self) -> Box<dyn NyashBox> {
let setb = |k: &str, v: bool| { if v { std::env::set_var(k, "1"); } else { std::env::remove_var(k); } }; let setb = |k: &str, v: bool| {
if v {
std::env::set_var(k, "1");
} else {
std::env::remove_var(k);
}
};
setb("NYASH_GC_COUNTING", self.counting); setb("NYASH_GC_COUNTING", self.counting);
setb("NYASH_GC_TRACE", self.trace); setb("NYASH_GC_TRACE", self.trace);
setb("NYASH_GC_BARRIER_STRICT", self.barrier_strict); setb("NYASH_GC_BARRIER_STRICT", self.barrier_strict);
Box::new(VoidBox::new()) Box::new(VoidBox::new())
} }
pub fn summary(&self) -> Box<dyn NyashBox> { pub fn summary(&self) -> Box<dyn NyashBox> {
let s = format!("counting={} trace={} barrier_strict={}", self.counting, self.trace, self.barrier_strict); let s = format!(
"counting={} trace={} barrier_strict={}",
self.counting, self.trace, self.barrier_strict
);
Box::new(StringBox::new(s)) Box::new(StringBox::new(s))
} }
} }
impl BoxCore for GcConfigBox { impl BoxCore for GcConfigBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "GcConfigBox") } }
fn as_any(&self) -> &dyn Any { self } fn parent_type_id(&self) -> Option<std::any::TypeId> {
fn as_any_mut(&mut self) -> &mut dyn Any { self } self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "GcConfigBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl NyashBox for GcConfigBox { impl NyashBox for GcConfigBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<GcConfigBox>()) } fn equals(&self, other: &dyn NyashBox) -> BoolBox {
fn type_name(&self) -> &'static str { "GcConfigBox" } BoolBox::new(other.as_any().is::<GcConfigBox>())
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) } }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() } fn type_name(&self) -> &'static str {
fn to_string_box(&self) -> StringBox { StringBox::new(self.summary().to_string_box().value) } "GcConfigBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
fn to_string_box(&self) -> StringBox {
StringBox::new(self.summary().to_string_box().value)
}
} }

View File

@ -5,7 +5,7 @@
// NOTE: HTTPサポートは現在開発中です。 // NOTE: HTTPサポートは現在開発中です。
// reqwestクレートの依存関係のため、一時的に無効化されています。 // reqwestクレートの依存関係のため、一時的に無効化されています。
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any; use std::any::Any;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -16,7 +16,7 @@ pub struct HttpClientBox {
impl HttpClientBox { impl HttpClientBox {
pub fn new() -> Self { pub fn new() -> Self {
HttpClientBox { HttpClientBox {
base: BoxBase::new() base: BoxBase::new(),
} }
} }
@ -41,7 +41,12 @@ impl HttpClientBox {
} }
/// ヘッダー付きHTTPリクエストスタブ /// ヘッダー付きHTTPリクエストスタブ
pub fn request(&self, _method: Box<dyn NyashBox>, _url: Box<dyn NyashBox>, _options: Box<dyn NyashBox>) -> Box<dyn NyashBox> { pub fn request(
&self,
_method: Box<dyn NyashBox>,
_url: Box<dyn NyashBox>,
_options: Box<dyn NyashBox>,
) -> Box<dyn NyashBox> {
Box::new(StringBox::new("HTTP support is currently disabled")) Box::new(StringBox::new("HTTP support is currently disabled"))
} }
} }
@ -60,12 +65,10 @@ impl NyashBox for HttpClientBox {
StringBox::new(format!("HttpClientBox(id: {})", self.base.id)) StringBox::new(format!("HttpClientBox(id: {})", self.base.id))
} }
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
"HttpClientBox" "HttpClientBox"
} }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_http) = other.as_any().downcast_ref::<HttpClientBox>() { if let Some(other_http) = other.as_any().downcast_ref::<HttpClientBox>() {
BoolBox::new(self.base.id == other_http.base.id) BoolBox::new(self.base.id == other_http.base.id)

View File

@ -48,7 +48,7 @@
* ``` * ```
*/ */
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use crate::boxes::MapBox; use crate::boxes::MapBox;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
@ -180,11 +180,9 @@ impl HTTPRequestBox {
/// Content-Length取得 /// Content-Length取得
pub fn get_content_length(&self) -> Box<dyn NyashBox> { pub fn get_content_length(&self) -> Box<dyn NyashBox> {
match self.headers.get("content-length") { match self.headers.get("content-length") {
Some(length_str) => { Some(length_str) => match length_str.parse::<i64>() {
match length_str.parse::<i64>() {
Ok(length) => Box::new(IntegerBox::new(length)), Ok(length) => Box::new(IntegerBox::new(length)),
Err(_) => Box::new(IntegerBox::new(0)), Err(_) => Box::new(IntegerBox::new(0)),
}
}, },
None => Box::new(IntegerBox::new(0)), None => Box::new(IntegerBox::new(0)),
} }
@ -202,8 +200,12 @@ impl NyashBox for HTTPRequestBox {
} }
fn to_string_box(&self) -> StringBox { fn to_string_box(&self) -> StringBox {
StringBox::new(format!("HTTPRequest({} {} - {} headers)", StringBox::new(format!(
self.method, self.path, self.headers.len())) "HTTPRequest({} {} - {} headers)",
self.method,
self.path,
self.headers.len()
))
} }
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
@ -229,8 +231,13 @@ impl BoxCore for HTTPRequestBox {
} }
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "HTTPRequest({} {} - {} headers)", write!(
self.method, self.path, self.headers.len()) f,
"HTTPRequest({} {} - {} headers)",
self.method,
self.path,
self.headers.len()
)
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
@ -272,7 +279,11 @@ impl HTTPResponseBox {
} }
/// ステータスコード・メッセージ設定 /// ステータスコード・メッセージ設定
pub fn set_status(&self, code: Box<dyn NyashBox>, message: Box<dyn NyashBox>) -> Box<dyn NyashBox> { pub fn set_status(
&self,
code: Box<dyn NyashBox>,
message: Box<dyn NyashBox>,
) -> Box<dyn NyashBox> {
// Note: This would need interior mutability for actual mutation // Note: This would need interior mutability for actual mutation
// For now, this is a placeholder for the API structure // For now, this is a placeholder for the API structure
let _code_val = code.to_string_box().value.parse::<i32>().unwrap_or(200); let _code_val = code.to_string_box().value.parse::<i32>().unwrap_or(200);
@ -283,7 +294,11 @@ impl HTTPResponseBox {
} }
/// ヘッダー設定 /// ヘッダー設定
pub fn set_header(&self, name: Box<dyn NyashBox>, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> { pub fn set_header(
&self,
name: Box<dyn NyashBox>,
value: Box<dyn NyashBox>,
) -> Box<dyn NyashBox> {
let _name_str = name.to_string_box().value; let _name_str = name.to_string_box().value;
let _value_str = value.to_string_box().value; let _value_str = value.to_string_box().value;
@ -296,7 +311,7 @@ impl HTTPResponseBox {
let content_type_str = content_type.to_string_box().value; let content_type_str = content_type.to_string_box().value;
self.set_header( self.set_header(
Box::new(StringBox::new("Content-Type".to_string())), Box::new(StringBox::new("Content-Type".to_string())),
Box::new(StringBox::new(content_type_str)) Box::new(StringBox::new(content_type_str)),
) )
} }
@ -321,8 +336,10 @@ impl HTTPResponseBox {
let mut response = String::new(); let mut response = String::new();
// Status line // Status line
response.push_str(&format!("{} {} {}\r\n", response.push_str(&format!(
self.http_version, self.status_code, self.status_message)); "{} {} {}\r\n",
self.http_version, self.status_code, self.status_message
));
// Headers // Headers
for (name, value) in &self.headers { for (name, value) in &self.headers {
@ -348,7 +365,10 @@ impl HTTPResponseBox {
let mut response = HTTPResponseBox::new(); let mut response = HTTPResponseBox::new();
response.status_code = 200; response.status_code = 200;
response.status_message = "OK".to_string(); response.status_message = "OK".to_string();
response.headers.insert("Content-Type".to_string(), "text/html; charset=utf-8".to_string()); response.headers.insert(
"Content-Type".to_string(),
"text/html; charset=utf-8".to_string(),
);
response.body = content.to_string_box().value; response.body = content.to_string_box().value;
response response
} }
@ -358,7 +378,9 @@ impl HTTPResponseBox {
let mut response = HTTPResponseBox::new(); let mut response = HTTPResponseBox::new();
response.status_code = 200; response.status_code = 200;
response.status_message = "OK".to_string(); response.status_message = "OK".to_string();
response.headers.insert("Content-Type".to_string(), "application/json".to_string()); response
.headers
.insert("Content-Type".to_string(), "application/json".to_string());
response.body = content.to_string_box().value; response.body = content.to_string_box().value;
response response
} }
@ -368,7 +390,10 @@ impl HTTPResponseBox {
let mut response = HTTPResponseBox::new(); let mut response = HTTPResponseBox::new();
response.status_code = 404; response.status_code = 404;
response.status_message = "Not Found".to_string(); response.status_message = "Not Found".to_string();
response.headers.insert("Content-Type".to_string(), "text/html; charset=utf-8".to_string()); response.headers.insert(
"Content-Type".to_string(),
"text/html; charset=utf-8".to_string(),
);
response.body = "<html><body><h1>404 - Not Found</h1></body></html>".to_string(); response.body = "<html><body><h1>404 - Not Found</h1></body></html>".to_string();
response response
} }
@ -385,8 +410,12 @@ impl NyashBox for HTTPResponseBox {
} }
fn to_string_box(&self) -> StringBox { fn to_string_box(&self) -> StringBox {
StringBox::new(format!("HTTPResponse({} {} - {} bytes)", StringBox::new(format!(
self.status_code, self.status_message, self.body.len())) "HTTPResponse({} {} - {} bytes)",
self.status_code,
self.status_message,
self.body.len()
))
} }
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
@ -412,8 +441,13 @@ impl BoxCore for HTTPResponseBox {
} }
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "HTTPResponse({} {} - {} bytes)", write!(
self.status_code, self.status_message, self.body.len()) f,
"HTTPResponse({} {} - {} bytes)",
self.status_code,
self.status_message,
self.body.len()
)
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {

View File

@ -40,12 +40,12 @@
* ``` * ```
*/ */
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use crate::boxes::SocketBox;
use crate::boxes::http_message_box::{HTTPRequestBox, HTTPResponseBox}; use crate::boxes::http_message_box::{HTTPRequestBox, HTTPResponseBox};
use crate::boxes::SocketBox;
use std::any::Any; use std::any::Any;
use std::sync::RwLock;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::RwLock;
use std::thread; use std::thread;
/// HTTP サーバーを提供するBox /// HTTP サーバーを提供するBox
@ -68,12 +68,14 @@ impl Clone for HTTPServerBox {
let socket_val = socket_guard.as_ref().map(|s| s.clone()); let socket_val = socket_guard.as_ref().map(|s| s.clone());
let routes_guard = self.routes.read().unwrap(); let routes_guard = self.routes.read().unwrap();
let routes_val: HashMap<String, Box<dyn NyashBox>> = routes_guard.iter() let routes_val: HashMap<String, Box<dyn NyashBox>> = routes_guard
.iter()
.map(|(k, v)| (k.clone(), v.clone_box())) .map(|(k, v)| (k.clone(), v.clone_box()))
.collect(); .collect();
let middleware_guard = self.middleware.read().unwrap(); let middleware_guard = self.middleware.read().unwrap();
let middleware_val: Vec<Box<dyn NyashBox>> = middleware_guard.iter() let middleware_val: Vec<Box<dyn NyashBox>> = middleware_guard
.iter()
.map(|item| item.clone_box()) .map(|item| item.clone_box())
.collect(); .collect();
@ -82,7 +84,8 @@ impl Clone for HTTPServerBox {
let timeout_val = *self.timeout_seconds.read().unwrap(); let timeout_val = *self.timeout_seconds.read().unwrap();
let connections_guard = self.active_connections.read().unwrap(); let connections_guard = self.active_connections.read().unwrap();
let connections_val: Vec<Box<dyn NyashBox>> = connections_guard.iter() let connections_val: Vec<Box<dyn NyashBox>> = connections_guard
.iter()
.map(|item| item.clone_box()) .map(|item| item.clone_box())
.collect(); .collect();
@ -123,10 +126,10 @@ impl HTTPServerBox {
Ok(mut socket_guard) => { Ok(mut socket_guard) => {
*socket_guard = Some(socket); *socket_guard = Some(socket);
Box::new(BoolBox::new(true)) Box::new(BoolBox::new(true))
},
Err(_) => {
Box::new(StringBox::new("Error: Failed to acquire socket lock".to_string()))
} }
Err(_) => Box::new(StringBox::new(
"Error: Failed to acquire socket lock".to_string(),
)),
} }
} else { } else {
Box::new(BoolBox::new(false)) Box::new(BoolBox::new(false))
@ -137,7 +140,11 @@ impl HTTPServerBox {
pub fn listen(&self, _backlog: Box<dyn NyashBox>) -> Box<dyn NyashBox> { pub fn listen(&self, _backlog: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let socket_guard = match self.socket.read() { let socket_guard = match self.socket.read() {
Ok(guard) => guard, Ok(guard) => guard,
Err(_) => return Box::new(StringBox::new("Error: Failed to acquire socket lock".to_string())), Err(_) => {
return Box::new(StringBox::new(
"Error: Failed to acquire socket lock".to_string(),
))
}
}; };
if let Some(ref _socket) = *socket_guard { if let Some(ref _socket) = *socket_guard {
@ -158,12 +165,20 @@ impl HTTPServerBox {
// Set running state // Set running state
match self.running.write() { match self.running.write() {
Ok(mut running) => *running = true, Ok(mut running) => *running = true,
Err(_) => return Box::new(StringBox::new("Error: Failed to set running state".to_string())), Err(_) => {
return Box::new(StringBox::new(
"Error: Failed to set running state".to_string(),
))
}
}; };
let socket_guard = match self.socket.read() { let socket_guard = match self.socket.read() {
Ok(guard) => guard, Ok(guard) => guard,
Err(_) => return Box::new(StringBox::new("Error: Failed to acquire socket lock".to_string())), Err(_) => {
return Box::new(StringBox::new(
"Error: Failed to acquire socket lock".to_string(),
))
}
}; };
if let Some(ref socket) = *socket_guard { if let Some(ref socket) = *socket_guard {
@ -203,11 +218,12 @@ impl HTTPServerBox {
// For RwLock pattern, we need to pass the data needed for the thread // For RwLock pattern, we need to pass the data needed for the thread
let routes_snapshot = match self.routes.read() { let routes_snapshot = match self.routes.read() {
Ok(routes_guard) => { Ok(routes_guard) => {
let routes_clone: HashMap<String, Box<dyn NyashBox>> = routes_guard.iter() let routes_clone: HashMap<String, Box<dyn NyashBox>> = routes_guard
.iter()
.map(|(k, v)| (k.clone(), v.clone_box())) .map(|(k, v)| (k.clone(), v.clone_box()))
.collect(); .collect();
routes_clone routes_clone
}, }
Err(_) => continue, // Skip this connection if we can't read routes Err(_) => continue, // Skip this connection if we can't read routes
}; };
@ -307,7 +323,7 @@ impl HTTPServerBox {
/// クライアントリクエスト処理(内部メソッド) /// クライアントリクエスト処理(内部メソッド)
fn handle_client_request_with_routes( fn handle_client_request_with_routes(
client_socket: SocketBox, client_socket: SocketBox,
routes: HashMap<String, Box<dyn NyashBox>> routes: HashMap<String, Box<dyn NyashBox>>,
) { ) {
// Read HTTP request // Read HTTP request
let raw_request = client_socket.read_http_request(); let raw_request = client_socket.read_http_request();
@ -332,14 +348,14 @@ impl HTTPServerBox {
let response = if let Some(_handler) = routes.get(&route_key) { let response = if let Some(_handler) = routes.get(&route_key) {
// Found specific method route // Found specific method route
// TODO: Actual handler invocation would need method calling infrastructure // TODO: Actual handler invocation would need method calling infrastructure
HTTPResponseBox::create_json_response( HTTPResponseBox::create_json_response(Box::new(StringBox::new(
Box::new(StringBox::new(r#"{"message": "Route found", "method": ""#.to_string() + &method + r#""}"#)) r#"{"message": "Route found", "method": ""#.to_string() + &method + r#""}"#,
) )))
} else if let Some(_handler) = routes.get(&fallback_key) { } else if let Some(_handler) = routes.get(&fallback_key) {
// Found generic route // Found generic route
HTTPResponseBox::create_json_response( HTTPResponseBox::create_json_response(Box::new(StringBox::new(
Box::new(StringBox::new(r#"{"message": "Generic route found"}"#)) r#"{"message": "Generic route found"}"#,
) )))
} else { } else {
// No route found - 404 // No route found - 404
HTTPResponseBox::create_404_response() HTTPResponseBox::create_404_response()
@ -411,8 +427,11 @@ impl BoxCore for HTTPServerBox {
let routes_count = self.routes.read().unwrap().len(); let routes_count = self.routes.read().unwrap().len();
let connections_count = self.active_connections.read().unwrap().len(); let connections_count = self.active_connections.read().unwrap().len();
write!(f, "HTTPServer(id: {}, running: {}, routes: {}, connections: {})", write!(
self.base.id, running, routes_count, connections_count) f,
"HTTPServer(id: {}, running: {}, routes: {}, connections: {})",
self.base.id, running, routes_count, connections_count
)
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {

View File

@ -32,10 +32,10 @@
* ``` * ```
*/ */
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any; use std::any::Any;
use std::sync::RwLock;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::RwLock;
/// IntentBox - 構造化メッセージBox (RwLock pattern) /// IntentBox - 構造化メッセージBox (RwLock pattern)
#[derive(Debug)] #[derive(Debug)]
@ -89,8 +89,8 @@ impl IntentBox {
Ok(json_val) => { Ok(json_val) => {
*self.payload.write().unwrap() = json_val; *self.payload.write().unwrap() = json_val;
Box::new(BoolBox::new(true)) Box::new(BoolBox::new(true))
}, }
Err(_) => Box::new(BoolBox::new(false)) Err(_) => Box::new(BoolBox::new(false)),
} }
} }
} }
@ -151,4 +151,3 @@ impl std::fmt::Display for IntentBox {
self.fmt_box(f) self.fmt_box(f)
} }
} }

View File

@ -1,6 +1,6 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, VoidBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox, VoidBox};
use crate::jit::config::JitConfig;
use crate::interpreter::RuntimeError; use crate::interpreter::RuntimeError;
use crate::jit::config::JitConfig;
use std::any::Any; use std::any::Any;
use std::sync::RwLock; use std::sync::RwLock;
@ -11,12 +11,19 @@ pub struct JitConfigBox {
} }
impl JitConfigBox { impl JitConfigBox {
pub fn new() -> Self { Self { base: BoxBase::new(), config: RwLock::new(JitConfig::from_env()) } } pub fn new() -> Self {
Self {
base: BoxBase::new(),
config: RwLock::new(JitConfig::from_env()),
}
}
/// Update internal config flags from runtime capability probe /// Update internal config flags from runtime capability probe
pub fn from_runtime_probe(&self) -> Box<dyn NyashBox> { pub fn from_runtime_probe(&self) -> Box<dyn NyashBox> {
let caps = crate::jit::config::probe_capabilities(); let caps = crate::jit::config::probe_capabilities();
let mut cfg = self.config.write().unwrap(); let mut cfg = self.config.write().unwrap();
if cfg.native_bool_abi && !caps.supports_b1_sig { cfg.native_bool_abi = false; } if cfg.native_bool_abi && !caps.supports_b1_sig {
cfg.native_bool_abi = false;
}
Box::new(VoidBox::new()) Box::new(VoidBox::new())
} }
pub fn set_flag(&self, name: &str, on: bool) -> Result<Box<dyn NyashBox>, RuntimeError> { pub fn set_flag(&self, name: &str, on: bool) -> Result<Box<dyn NyashBox>, RuntimeError> {
@ -34,7 +41,11 @@ impl JitConfigBox {
"bool_abi" | "native_bool_abi" => cfg.native_bool_abi = on, "bool_abi" | "native_bool_abi" => cfg.native_bool_abi = on,
"ret_b1" | "ret_bool_b1" => cfg.ret_bool_b1 = on, "ret_b1" | "ret_bool_b1" => cfg.ret_bool_b1 = on,
"relax_numeric" | "hostcall_relax_numeric" => cfg.relax_numeric = on, "relax_numeric" | "hostcall_relax_numeric" => cfg.relax_numeric = on,
_ => return Err(RuntimeError::InvalidOperation { message: format!("Unknown flag: {}", name) }), _ => {
return Err(RuntimeError::InvalidOperation {
message: format!("Unknown flag: {}", name),
})
}
} }
Ok(Box::new(VoidBox::new())) Ok(Box::new(VoidBox::new()))
} }
@ -53,19 +64,28 @@ impl JitConfigBox {
"bool_abi" | "native_bool_abi" => cfg.native_bool_abi, "bool_abi" | "native_bool_abi" => cfg.native_bool_abi,
"ret_b1" | "ret_bool_b1" => cfg.ret_bool_b1, "ret_b1" | "ret_bool_b1" => cfg.ret_bool_b1,
"relax_numeric" | "hostcall_relax_numeric" => cfg.relax_numeric, "relax_numeric" | "hostcall_relax_numeric" => cfg.relax_numeric,
_ => return Err(RuntimeError::InvalidOperation { message: format!("Unknown flag: {}", name) }), _ => {
return Err(RuntimeError::InvalidOperation {
message: format!("Unknown flag: {}", name),
})
}
}; };
Ok(Box::new(BoolBox::new(b))) Ok(Box::new(BoolBox::new(b)))
} }
pub fn set_threshold(&self, v: i64) -> Result<Box<dyn NyashBox>, RuntimeError> { pub fn set_threshold(&self, v: i64) -> Result<Box<dyn NyashBox>, RuntimeError> {
let mut cfg = self.config.write().unwrap(); let mut cfg = self.config.write().unwrap();
if v <= 0 { cfg.threshold = None; } if v <= 0 {
else { cfg.threshold = Some(v as u32); } cfg.threshold = None;
} else {
cfg.threshold = Some(v as u32);
}
Ok(Box::new(VoidBox::new())) Ok(Box::new(VoidBox::new()))
} }
pub fn get_threshold(&self) -> Box<dyn NyashBox> { pub fn get_threshold(&self) -> Box<dyn NyashBox> {
let cfg = self.config.read().unwrap(); let cfg = self.config.read().unwrap();
Box::new(IntegerBox::new(cfg.threshold.map(|v| v as i64).unwrap_or(0))) Box::new(IntegerBox::new(
cfg.threshold.map(|v| v as i64).unwrap_or(0),
))
} }
pub fn to_json(&self) -> Box<dyn NyashBox> { pub fn to_json(&self) -> Box<dyn NyashBox> {
let cfg = self.config.read().unwrap(); let cfg = self.config.read().unwrap();
@ -88,20 +108,49 @@ impl JitConfigBox {
} }
pub fn from_json(&self, s: &str) -> Result<Box<dyn NyashBox>, RuntimeError> { pub fn from_json(&self, s: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
let mut cfg = self.config.write().unwrap(); let mut cfg = self.config.write().unwrap();
let v: serde_json::Value = serde_json::from_str(s).map_err(|e| RuntimeError::InvalidOperation { message: format!("Invalid JSON: {}", e) })?; let v: serde_json::Value =
if let Some(b) = v.get("exec").and_then(|x| x.as_bool()) { cfg.exec = b; } serde_json::from_str(s).map_err(|e| RuntimeError::InvalidOperation {
if let Some(b) = v.get("stats").and_then(|x| x.as_bool()) { cfg.stats = b; } message: format!("Invalid JSON: {}", e),
if let Some(b) = v.get("stats_json").and_then(|x| x.as_bool()) { cfg.stats_json = b; } })?;
if let Some(b) = v.get("dump").and_then(|x| x.as_bool()) { cfg.dump = b; } if let Some(b) = v.get("exec").and_then(|x| x.as_bool()) {
if let Some(n) = v.get("threshold").and_then(|x| x.as_u64()) { cfg.threshold = Some(n as u32); } cfg.exec = b;
if let Some(b) = v.get("phi_min").and_then(|x| x.as_bool()) { cfg.phi_min = b; } }
if let Some(b) = v.get("hostcall").and_then(|x| x.as_bool()) { cfg.hostcall = b; } if let Some(b) = v.get("stats").and_then(|x| x.as_bool()) {
if let Some(b) = v.get("handle_debug").and_then(|x| x.as_bool()) { cfg.handle_debug = b; } cfg.stats = b;
if let Some(b) = v.get("native_f64").and_then(|x| x.as_bool()) { cfg.native_f64 = b; } }
if let Some(b) = v.get("native_bool").and_then(|x| x.as_bool()) { cfg.native_bool = b; } if let Some(b) = v.get("stats_json").and_then(|x| x.as_bool()) {
if let Some(b) = v.get("native_bool_abi").and_then(|x| x.as_bool()) { cfg.native_bool_abi = b; } cfg.stats_json = b;
if let Some(b) = v.get("ret_bool_b1").and_then(|x| x.as_bool()) { cfg.ret_bool_b1 = b; } }
if let Some(b) = v.get("relax_numeric").and_then(|x| x.as_bool()) { cfg.relax_numeric = b; } if let Some(b) = v.get("dump").and_then(|x| x.as_bool()) {
cfg.dump = b;
}
if let Some(n) = v.get("threshold").and_then(|x| x.as_u64()) {
cfg.threshold = Some(n as u32);
}
if let Some(b) = v.get("phi_min").and_then(|x| x.as_bool()) {
cfg.phi_min = b;
}
if let Some(b) = v.get("hostcall").and_then(|x| x.as_bool()) {
cfg.hostcall = b;
}
if let Some(b) = v.get("handle_debug").and_then(|x| x.as_bool()) {
cfg.handle_debug = b;
}
if let Some(b) = v.get("native_f64").and_then(|x| x.as_bool()) {
cfg.native_f64 = b;
}
if let Some(b) = v.get("native_bool").and_then(|x| x.as_bool()) {
cfg.native_bool = b;
}
if let Some(b) = v.get("native_bool_abi").and_then(|x| x.as_bool()) {
cfg.native_bool_abi = b;
}
if let Some(b) = v.get("ret_bool_b1").and_then(|x| x.as_bool()) {
cfg.ret_bool_b1 = b;
}
if let Some(b) = v.get("relax_numeric").and_then(|x| x.as_bool()) {
cfg.relax_numeric = b;
}
Ok(Box::new(VoidBox::new())) Ok(Box::new(VoidBox::new()))
} }
pub fn apply(&self) -> Box<dyn NyashBox> { pub fn apply(&self) -> Box<dyn NyashBox> {
@ -124,20 +173,41 @@ impl JitConfigBox {
} }
impl BoxCore for JitConfigBox { impl BoxCore for JitConfigBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitConfigBox") } }
fn as_any(&self) -> &dyn Any { self } fn parent_type_id(&self) -> Option<std::any::TypeId> {
fn as_any_mut(&mut self) -> &mut dyn Any { self } self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JitConfigBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl NyashBox for JitConfigBox { impl NyashBox for JitConfigBox {
fn to_string_box(&self) -> StringBox { StringBox::new(self.summary().to_string_box().value) } fn to_string_box(&self) -> StringBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitConfigBox>()) } StringBox::new(self.summary().to_string_box().value)
fn type_name(&self) -> &'static str { "JitConfigBox" } }
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
BoolBox::new(other.as_any().is::<JitConfigBox>())
}
fn type_name(&self) -> &'static str {
"JitConfigBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
let cfg = self.config.read().unwrap().clone(); let cfg = self.config.read().unwrap().clone();
Box::new(JitConfigBox { base: self.base.clone(), config: RwLock::new(cfg) }) Box::new(JitConfigBox {
base: self.base.clone(),
config: RwLock::new(cfg),
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
} }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
} }

View File

@ -1,25 +1,55 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any; use std::any::Any;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct JitEventsBox { base: BoxBase } pub struct JitEventsBox {
base: BoxBase,
}
impl JitEventsBox { pub fn new() -> Self { Self { base: BoxBase::new() } } } impl JitEventsBox {
pub fn new() -> Self {
Self {
base: BoxBase::new(),
}
}
}
impl BoxCore for JitEventsBox { impl BoxCore for JitEventsBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitEventsBox") } }
fn as_any(&self) -> &dyn Any { self } fn parent_type_id(&self) -> Option<std::any::TypeId> {
fn as_any_mut(&mut self) -> &mut dyn Any { self } self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JitEventsBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl NyashBox for JitEventsBox { impl NyashBox for JitEventsBox {
fn to_string_box(&self) -> StringBox { StringBox::new("JitEventsBox") } fn to_string_box(&self) -> StringBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitEventsBox>()) } StringBox::new("JitEventsBox")
fn type_name(&self) -> &'static str { "JitEventsBox" } }
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) } fn equals(&self, other: &dyn NyashBox) -> BoolBox {
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() } BoolBox::new(other.as_any().is::<JitEventsBox>())
}
fn type_name(&self) -> &'static str {
"JitEventsBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self {
base: self.base.clone(),
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
} }
impl JitEventsBox { impl JitEventsBox {
@ -28,14 +58,17 @@ impl JitEventsBox {
Box::new(VoidBox::new()) Box::new(VoidBox::new())
} }
pub fn enable(&self, on: bool) -> Box<dyn NyashBox> { pub fn enable(&self, on: bool) -> Box<dyn NyashBox> {
if on { std::env::set_var("NYASH_JIT_EVENTS", "1"); } if on {
else { std::env::remove_var("NYASH_JIT_EVENTS"); } std::env::set_var("NYASH_JIT_EVENTS", "1");
} else {
std::env::remove_var("NYASH_JIT_EVENTS");
}
Box::new(VoidBox::new()) Box::new(VoidBox::new())
} }
pub fn log(&self, kind: &str, function: &str, note_json: &str) -> Box<dyn NyashBox> { pub fn log(&self, kind: &str, function: &str, note_json: &str) -> Box<dyn NyashBox> {
let extra = serde_json::from_str::<serde_json::Value>(note_json).unwrap_or_else(|_| serde_json::json!({"note": note_json})); let extra = serde_json::from_str::<serde_json::Value>(note_json)
.unwrap_or_else(|_| serde_json::json!({"note": note_json}));
crate::jit::events::emit(kind, function, None, None, extra); crate::jit::events::emit(kind, function, None, None, extra);
Box::new(VoidBox::new()) Box::new(VoidBox::new())
} }
} }

View File

@ -1,17 +1,35 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any; use std::any::Any;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct JitHostcallRegistryBox { base: BoxBase } pub struct JitHostcallRegistryBox {
base: BoxBase,
}
impl JitHostcallRegistryBox { pub fn new() -> Self { Self { base: BoxBase::new() } } } impl JitHostcallRegistryBox {
pub fn new() -> Self {
Self {
base: BoxBase::new(),
}
}
}
impl BoxCore for JitHostcallRegistryBox { impl BoxCore for JitHostcallRegistryBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitHostcallRegistryBox") } }
fn as_any(&self) -> &dyn Any { self } fn parent_type_id(&self) -> Option<std::any::TypeId> {
fn as_any_mut(&mut self) -> &mut dyn Any { self } self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JitHostcallRegistryBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl NyashBox for JitHostcallRegistryBox { impl NyashBox for JitHostcallRegistryBox {
@ -20,18 +38,41 @@ impl NyashBox for JitHostcallRegistryBox {
let payload = serde_json::json!({ "readonly": ro, "mutating": mu }); let payload = serde_json::json!({ "readonly": ro, "mutating": mu });
StringBox::new(payload.to_string()) StringBox::new(payload.to_string())
} }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitHostcallRegistryBox>()) } fn equals(&self, other: &dyn NyashBox) -> BoolBox {
fn type_name(&self) -> &'static str { "JitHostcallRegistryBox" } BoolBox::new(other.as_any().is::<JitHostcallRegistryBox>())
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) } }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() } fn type_name(&self) -> &'static str {
"JitHostcallRegistryBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self {
base: self.base.clone(),
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
} }
impl JitHostcallRegistryBox { impl JitHostcallRegistryBox {
pub fn add_readonly(&self, sym: &str) -> Box<dyn NyashBox> { crate::jit::hostcall_registry::add_readonly(sym); Box::new(VoidBox::new()) } pub fn add_readonly(&self, sym: &str) -> Box<dyn NyashBox> {
pub fn add_mutating(&self, sym: &str) -> Box<dyn NyashBox> { crate::jit::hostcall_registry::add_mutating(sym); Box::new(VoidBox::new()) } crate::jit::hostcall_registry::add_readonly(sym);
pub fn set_from_csv(&self, ro_csv: &str, mu_csv: &str) -> Box<dyn NyashBox> { crate::jit::hostcall_registry::set_from_csv(ro_csv, mu_csv); Box::new(VoidBox::new()) } Box::new(VoidBox::new())
}
pub fn add_mutating(&self, sym: &str) -> Box<dyn NyashBox> {
crate::jit::hostcall_registry::add_mutating(sym);
Box::new(VoidBox::new())
}
pub fn set_from_csv(&self, ro_csv: &str, mu_csv: &str) -> Box<dyn NyashBox> {
crate::jit::hostcall_registry::set_from_csv(ro_csv, mu_csv);
Box::new(VoidBox::new())
}
pub fn add_signature(&self, sym: &str, args_csv: &str, ret_str: &str) -> Box<dyn NyashBox> { pub fn add_signature(&self, sym: &str, args_csv: &str, ret_str: &str) -> Box<dyn NyashBox> {
let ok = crate::jit::hostcall_registry::set_signature_csv(sym, args_csv, ret_str); let ok = crate::jit::hostcall_registry::set_signature_csv(sym, args_csv, ret_str);
if ok { Box::new(VoidBox::new()) } else { Box::new(StringBox::new("Invalid signature")) } if ok {
Box::new(VoidBox::new())
} else {
Box::new(StringBox::new("Invalid signature"))
}
} }
} }

View File

@ -1,29 +1,61 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any; use std::any::Any;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct JitPolicyBox { base: BoxBase } pub struct JitPolicyBox {
base: BoxBase,
}
impl JitPolicyBox { pub fn new() -> Self { Self { base: BoxBase::new() } } } impl JitPolicyBox {
pub fn new() -> Self {
Self {
base: BoxBase::new(),
}
}
}
impl BoxCore for JitPolicyBox { impl BoxCore for JitPolicyBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitPolicyBox") } }
fn as_any(&self) -> &dyn Any { self } fn parent_type_id(&self) -> Option<std::any::TypeId> {
fn as_any_mut(&mut self) -> &mut dyn Any { self } self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JitPolicyBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl NyashBox for JitPolicyBox { impl NyashBox for JitPolicyBox {
fn to_string_box(&self) -> StringBox { fn to_string_box(&self) -> StringBox {
let p = crate::jit::policy::current(); let p = crate::jit::policy::current();
let s = format!("read_only={} whitelist={}", p.read_only, p.hostcall_whitelist.join(",")); let s = format!(
"read_only={} whitelist={}",
p.read_only,
p.hostcall_whitelist.join(",")
);
StringBox::new(s) StringBox::new(s)
} }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitPolicyBox>()) } fn equals(&self, other: &dyn NyashBox) -> BoolBox {
fn type_name(&self) -> &'static str { "JitPolicyBox" } BoolBox::new(other.as_any().is::<JitPolicyBox>())
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) } }
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() } fn type_name(&self) -> &'static str {
"JitPolicyBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self {
base: self.base.clone(),
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
} }
// Methods (exposed via VM dispatch): // Methods (exposed via VM dispatch):
@ -32,19 +64,26 @@ impl JitPolicyBox {
let mut cur = crate::jit::policy::current(); let mut cur = crate::jit::policy::current();
match name { match name {
"read_only" | "readonly" => cur.read_only = on, "read_only" | "readonly" => cur.read_only = on,
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name))) _ => return Box::new(StringBox::new(format!("Unknown flag: {}", name))),
} }
crate::jit::policy::set_current(cur); crate::jit::policy::set_current(cur);
Box::new(VoidBox::new()) Box::new(VoidBox::new())
} }
pub fn get_flag(&self, name: &str) -> Box<dyn NyashBox> { pub fn get_flag(&self, name: &str) -> Box<dyn NyashBox> {
let cur = crate::jit::policy::current(); let cur = crate::jit::policy::current();
let v = match name { "read_only" | "readonly" => cur.read_only, _ => false }; let v = match name {
"read_only" | "readonly" => cur.read_only,
_ => false,
};
Box::new(BoolBox::new(v)) Box::new(BoolBox::new(v))
} }
pub fn set_whitelist_csv(&self, csv: &str) -> Box<dyn NyashBox> { pub fn set_whitelist_csv(&self, csv: &str) -> Box<dyn NyashBox> {
let mut cur = crate::jit::policy::current(); let mut cur = crate::jit::policy::current();
cur.hostcall_whitelist = csv.split(',').map(|t| t.trim().to_string()).filter(|s| !s.is_empty()).collect(); cur.hostcall_whitelist = csv
.split(',')
.map(|t| t.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
crate::jit::policy::set_current(cur); crate::jit::policy::set_current(cur);
Box::new(VoidBox::new()) Box::new(VoidBox::new())
} }
@ -89,9 +128,15 @@ impl JitPolicyBox {
crate::jit::r#extern::collections::SYM_ARRAY_SET_H, crate::jit::r#extern::collections::SYM_ARRAY_SET_H,
crate::jit::r#extern::collections::SYM_MAP_SET_H, crate::jit::r#extern::collections::SYM_MAP_SET_H,
]; ];
for id in ids { if !cur.hostcall_whitelist.iter().any(|s| s == id) { cur.hostcall_whitelist.push(id.to_string()); } } for id in ids {
if !cur.hostcall_whitelist.iter().any(|s| s == id) {
cur.hostcall_whitelist.push(id.to_string());
}
}
}
_ => {
return Box::new(StringBox::new(format!("Unknown preset: {}", name)));
} }
_ => { return Box::new(StringBox::new(format!("Unknown preset: {}", name))); }
} }
crate::jit::policy::set_current(cur); crate::jit::policy::set_current(cur);
Box::new(VoidBox::new()) Box::new(VoidBox::new())

View File

@ -1,15 +1,25 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use std::any::Any; use std::any::Any;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct JitStatsBox { base: BoxBase } pub struct JitStatsBox {
base: BoxBase,
}
impl JitStatsBox { impl JitStatsBox {
pub fn new() -> Self { Self { base: BoxBase::new() } } pub fn new() -> Self {
Self {
base: BoxBase::new(),
}
}
pub fn to_json(&self) -> Box<dyn NyashBox> { pub fn to_json(&self) -> Box<dyn NyashBox> {
let cfg = crate::jit::config::current(); let cfg = crate::jit::config::current();
let caps = crate::jit::config::probe_capabilities(); let caps = crate::jit::config::probe_capabilities();
let mode = if cfg.native_bool_abi && caps.supports_b1_sig { "b1_bool" } else { "i64_bool" }; let mode = if cfg.native_bool_abi && caps.supports_b1_sig {
"b1_bool"
} else {
"i64_bool"
};
let payload = serde_json::json!({ let payload = serde_json::json!({
"version": 1, "version": 1,
"abi_mode": mode, "abi_mode": mode,
@ -25,17 +35,37 @@ impl JitStatsBox {
} }
impl BoxCore for JitStatsBox { impl BoxCore for JitStatsBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitStatsBox") } }
fn as_any(&self) -> &dyn Any { self } fn parent_type_id(&self) -> Option<std::any::TypeId> {
fn as_any_mut(&mut self) -> &mut dyn Any { self } self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JitStatsBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl NyashBox for JitStatsBox { impl NyashBox for JitStatsBox {
fn to_string_box(&self) -> StringBox { StringBox::new(self.to_json().to_string_box().value) } fn to_string_box(&self) -> StringBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitStatsBox>()) } StringBox::new(self.to_json().to_string_box().value)
fn type_name(&self) -> &'static str { "JitStatsBox" } }
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) } fn equals(&self, other: &dyn NyashBox) -> BoolBox {
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() } BoolBox::new(other.as_any().is::<JitStatsBox>())
}
fn type_name(&self) -> &'static str {
"JitStatsBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
} }

View File

@ -1,25 +1,55 @@
use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
use std::any::Any; use std::any::Any;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct JitStrictBox { base: BoxBase } pub struct JitStrictBox {
base: BoxBase,
}
impl JitStrictBox { pub fn new() -> Self { Self { base: BoxBase::new() } } } impl JitStrictBox {
pub fn new() -> Self {
Self {
base: BoxBase::new(),
}
}
}
impl BoxCore for JitStrictBox { impl BoxCore for JitStrictBox {
fn box_id(&self) -> u64 { self.base.id } fn box_id(&self) -> u64 {
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id } self.base.id
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "JitStrictBox") } }
fn as_any(&self) -> &dyn Any { self } fn parent_type_id(&self) -> Option<std::any::TypeId> {
fn as_any_mut(&mut self) -> &mut dyn Any { self } self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JitStrictBox")
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl NyashBox for JitStrictBox { impl NyashBox for JitStrictBox {
fn to_string_box(&self) -> StringBox { StringBox::new("JitStrictBox".to_string()) } fn to_string_box(&self) -> StringBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::<JitStrictBox>()) } StringBox::new("JitStrictBox".to_string())
fn type_name(&self) -> &'static str { "JitStrictBox" } }
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) } fn equals(&self, other: &dyn NyashBox) -> BoolBox {
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() } BoolBox::new(other.as_any().is::<JitStrictBox>())
}
fn type_name(&self) -> &'static str {
"JitStrictBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(Self {
base: self.base.clone(),
})
}
fn share_box(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
} }
impl JitStrictBox { impl JitStrictBox {
@ -27,8 +57,12 @@ impl JitStrictBox {
pub fn enable(&self, on: bool) -> Box<dyn NyashBox> { pub fn enable(&self, on: bool) -> Box<dyn NyashBox> {
if on { if on {
std::env::set_var("NYASH_JIT_STRICT", "1"); std::env::set_var("NYASH_JIT_STRICT", "1");
if std::env::var("NYASH_JIT_ONLY").ok().is_none() { std::env::set_var("NYASH_JIT_ONLY", "1"); } if std::env::var("NYASH_JIT_ONLY").ok().is_none() {
if std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().is_none() { std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1"); } std::env::set_var("NYASH_JIT_ONLY", "1");
}
if std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().is_none() {
std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1");
}
} else { } else {
std::env::remove_var("NYASH_JIT_STRICT"); std::env::remove_var("NYASH_JIT_STRICT");
} }
@ -36,11 +70,19 @@ impl JitStrictBox {
} }
pub fn set_only(&self, on: bool) -> Box<dyn NyashBox> { pub fn set_only(&self, on: bool) -> Box<dyn NyashBox> {
if on { std::env::set_var("NYASH_JIT_ONLY", "1"); } else { std::env::remove_var("NYASH_JIT_ONLY"); } if on {
std::env::set_var("NYASH_JIT_ONLY", "1");
} else {
std::env::remove_var("NYASH_JIT_ONLY");
}
Box::new(VoidBox::new()) Box::new(VoidBox::new())
} }
pub fn set_handle_only(&self, on: bool) -> Box<dyn NyashBox> { pub fn set_handle_only(&self, on: bool) -> Box<dyn NyashBox> {
if on { std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1"); } else { std::env::remove_var("NYASH_JIT_ARGS_HANDLE_ONLY"); } if on {
std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1");
} else {
std::env::remove_var("NYASH_JIT_ARGS_HANDLE_ONLY");
}
Box::new(VoidBox::new()) Box::new(VoidBox::new())
} }
@ -60,4 +102,3 @@ impl JitStrictBox {
Box::new(VoidBox::new()) Box::new(VoidBox::new())
} }
} }

View File

@ -2,12 +2,12 @@
// Nyashの箱システムによるJSON解析・生成を提供します。 // Nyashの箱システムによるJSON解析・生成を提供します。
// 参考: 既存Boxの設計思想 // 参考: 既存Boxの設計思想
use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox, IntegerBox}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use crate::boxes::array::ArrayBox; use crate::boxes::array::ArrayBox;
use crate::boxes::map_box::MapBox; use crate::boxes::map_box::MapBox;
use serde_json::{Error, Value};
use std::any::Any; use std::any::Any;
use std::sync::RwLock; use std::sync::RwLock;
use serde_json::{Value, Error};
#[derive(Debug)] #[derive(Debug)]
pub struct JSONBox { pub struct JSONBox {
@ -31,14 +31,14 @@ impl JSONBox {
let value = serde_json::from_str(s)?; let value = serde_json::from_str(s)?;
Ok(JSONBox { Ok(JSONBox {
value: RwLock::new(value), value: RwLock::new(value),
base: BoxBase::new() base: BoxBase::new(),
}) })
} }
pub fn new(value: Value) -> Self { pub fn new(value: Value) -> Self {
JSONBox { JSONBox {
value: RwLock::new(value), value: RwLock::new(value),
base: BoxBase::new() base: BoxBase::new(),
} }
} }
@ -148,10 +148,10 @@ impl BoxCore for JSONBox {
Value::String(_) => "string", Value::String(_) => "string",
Value::Array(ref arr) => { Value::Array(ref arr) => {
return write!(f, "JSONBox[array:{}]", arr.len()); return write!(f, "JSONBox[array:{}]", arr.len());
}, }
Value::Object(ref obj) => { Value::Object(ref obj) => {
return write!(f, "JSONBox[object:{}]", obj.len()); return write!(f, "JSONBox[object:{}]", obj.len());
}, }
}; };
write!(f, "JSONBox[{}]", json_type) write!(f, "JSONBox[{}]", json_type)
} }
@ -186,12 +186,10 @@ impl NyashBox for JSONBox {
StringBox::new(value.to_string()) StringBox::new(value.to_string())
} }
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
"JSONBox" "JSONBox"
} }
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_json) = other.as_any().downcast_ref::<JSONBox>() { if let Some(other_json) = other.as_any().downcast_ref::<JSONBox>() {
let self_value = self.value.read().unwrap(); let self_value = self.value.read().unwrap();
@ -230,10 +228,7 @@ fn json_value_to_nyash_box(value: &Value) -> Box<dyn NyashBox> {
Value::Object(obj) => { Value::Object(obj) => {
let map_box = MapBox::new(); let map_box = MapBox::new();
for (key, val) in obj { for (key, val) in obj {
map_box.set( map_box.set(Box::new(StringBox::new(key)), json_value_to_nyash_box(val));
Box::new(StringBox::new(key)),
json_value_to_nyash_box(val)
);
} }
Box::new(map_box) Box::new(map_box)
} }
@ -242,7 +237,11 @@ fn json_value_to_nyash_box(value: &Value) -> Box<dyn NyashBox> {
/// NyashBox を JSON Value に変換 /// NyashBox を JSON Value に変換
fn nyash_box_to_json_value(value: Box<dyn NyashBox>) -> Value { fn nyash_box_to_json_value(value: Box<dyn NyashBox>) -> Value {
if value.as_any().downcast_ref::<crate::boxes::null_box::NullBox>().is_some() { if value
.as_any()
.downcast_ref::<crate::boxes::null_box::NullBox>()
.is_some()
{
Value::Null Value::Null
} else if let Some(bool_box) = value.as_any().downcast_ref::<BoolBox>() { } else if let Some(bool_box) = value.as_any().downcast_ref::<BoolBox>() {
Value::Bool(bool_box.value) Value::Bool(bool_box.value)
@ -259,7 +258,8 @@ fn nyash_box_to_json_value(value: Box<dyn NyashBox>) -> Value {
Value::String(string_box.value.clone()) Value::String(string_box.value.clone())
} else if let Some(array_box) = value.as_any().downcast_ref::<ArrayBox>() { } else if let Some(array_box) = value.as_any().downcast_ref::<ArrayBox>() {
let items = array_box.items.read().unwrap(); let items = array_box.items.read().unwrap();
let arr: Vec<Value> = items.iter() let arr: Vec<Value> = items
.iter()
.map(|item| nyash_box_to_json_value(item.clone_box())) .map(|item| nyash_box_to_json_value(item.clone_box()))
.collect(); .collect();
Value::Array(arr) Value::Array(arr)

View File

@ -103,11 +103,11 @@
* - 存在しないキーの取得は "Key not found" メッセージ返却 * - 存在しないキーの取得は "Key not found" メッセージ返却
*/ */
use crate::box_trait::{BoxCore, BoxBase, NyashBox, StringBox, IntegerBox, BoolBox}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use crate::boxes::ArrayBox; use crate::boxes::ArrayBox;
use std::fmt::{Debug, Display};
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{Debug, Display};
use std::sync::{Arc, RwLock}; // Arc追加 use std::sync::{Arc, RwLock}; // Arc追加
/// キーバリューストアを表すBox /// キーバリューストアを表すBox
@ -137,7 +137,11 @@ impl MapBox {
match self.data.read().unwrap().get(&key_str) { match self.data.read().unwrap().get(&key_str) {
Some(value) => { Some(value) => {
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
if value.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() { if value
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
return value.share_box(); return value.share_box();
} }
value.clone_box() value.clone_box()
@ -149,7 +153,9 @@ impl MapBox {
/// キーが存在するかチェック /// キーが存在するかチェック
pub fn has(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> { pub fn has(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let key_str = key.to_string_box().value; let key_str = key.to_string_box().value;
Box::new(BoolBox::new(self.data.read().unwrap().contains_key(&key_str))) Box::new(BoolBox::new(
self.data.read().unwrap().contains_key(&key_str),
))
} }
/// キーを削除 /// キーを削除
@ -173,7 +179,10 @@ impl MapBox {
/// 全ての値を取得 /// 全ての値を取得
pub fn values(&self) -> Box<dyn NyashBox> { pub fn values(&self) -> Box<dyn NyashBox> {
let values: Vec<Box<dyn NyashBox>> = self.data.read().unwrap() let values: Vec<Box<dyn NyashBox>> = self
.data
.read()
.unwrap()
.values() .values()
.map(|v| v.clone_box()) .map(|v| v.clone_box())
.collect(); .collect();
@ -211,7 +220,8 @@ impl MapBox {
let value_str = value.to_string_box().value; let value_str = value.to_string_box().value;
// 値が数値の場合はそのまま、文字列の場合は引用符で囲む // 値が数値の場合はそのまま、文字列の場合は引用符で囲む
let formatted_value = if value.as_any().downcast_ref::<IntegerBox>().is_some() let formatted_value = if value.as_any().downcast_ref::<IntegerBox>().is_some()
|| value.as_any().downcast_ref::<BoolBox>().is_some() { || value.as_any().downcast_ref::<BoolBox>().is_some()
{
value_str value_str
} else { } else {
format!("\"{}\"", value_str.replace("\"", "\\\"")) format!("\"{}\"", value_str.replace("\"", "\\\""))
@ -233,7 +243,8 @@ impl Clone for MapBox {
fn clone(&self) -> Self { fn clone(&self) -> Self {
// ディープコピー(独立インスタンス) // ディープコピー(独立インスタンス)
let data_guard = self.data.read().unwrap(); let data_guard = self.data.read().unwrap();
let cloned_data: HashMap<String, Box<dyn NyashBox>> = data_guard.iter() let cloned_data: HashMap<String, Box<dyn NyashBox>> = data_guard
.iter()
.map(|(k, v)| (k.clone(), v.clone_box())) // 要素もディープコピー .map(|(k, v)| (k.clone(), v.clone_box())) // 要素もディープコピー
.collect(); .collect();
MapBox { MapBox {
@ -267,7 +278,9 @@ impl BoxCore for MapBox {
} }
impl NyashBox for MapBox { impl NyashBox for MapBox {
fn is_identity(&self) -> bool { true } fn is_identity(&self) -> bool {
true
}
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
"MapBox" "MapBox"
} }
@ -277,7 +290,6 @@ impl NyashBox for MapBox {
StringBox::new(&format!("MapBox(size={})", size)) StringBox::new(&format!("MapBox(size={})", size))
} }
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone()) Box::new(self.clone())
} }
@ -299,7 +311,6 @@ impl NyashBox for MapBox {
BoolBox::new(false) BoolBox::new(false)
} }
} }
} }
impl Display for MapBox { impl Display for MapBox {

View File

@ -56,9 +56,9 @@
* - 整数演算は自動でFloatBoxに変換される場合あり * - 整数演算は自動でFloatBoxに変換される場合あり
*/ */
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase}; use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
use std::fmt::{Debug, Display};
use std::any::Any; use std::any::Any;
use std::fmt::{Debug, Display};
/// 数学演算を提供するBox /// 数学演算を提供するBox
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -69,7 +69,7 @@ pub struct MathBox {
impl MathBox { impl MathBox {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
base: BoxBase::new() base: BoxBase::new(),
} }
} }
@ -88,12 +88,12 @@ impl MathBox {
pub fn max(&self, a: Box<dyn NyashBox>, b: Box<dyn NyashBox>) -> Box<dyn NyashBox> { pub fn max(&self, a: Box<dyn NyashBox>, b: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(a_int), Some(b_int)) = ( if let (Some(a_int), Some(b_int)) = (
a.as_any().downcast_ref::<IntegerBox>(), a.as_any().downcast_ref::<IntegerBox>(),
b.as_any().downcast_ref::<IntegerBox>() b.as_any().downcast_ref::<IntegerBox>(),
) { ) {
Box::new(IntegerBox::new(a_int.value.max(b_int.value))) Box::new(IntegerBox::new(a_int.value.max(b_int.value)))
} else if let (Some(a_float), Some(b_float)) = ( } else if let (Some(a_float), Some(b_float)) = (
a.as_any().downcast_ref::<FloatBox>(), a.as_any().downcast_ref::<FloatBox>(),
b.as_any().downcast_ref::<FloatBox>() b.as_any().downcast_ref::<FloatBox>(),
) { ) {
Box::new(FloatBox::new(a_float.value.max(b_float.value))) Box::new(FloatBox::new(a_float.value.max(b_float.value)))
} else { } else {
@ -105,12 +105,12 @@ impl MathBox {
pub fn min(&self, a: Box<dyn NyashBox>, b: Box<dyn NyashBox>) -> Box<dyn NyashBox> { pub fn min(&self, a: Box<dyn NyashBox>, b: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(a_int), Some(b_int)) = ( if let (Some(a_int), Some(b_int)) = (
a.as_any().downcast_ref::<IntegerBox>(), a.as_any().downcast_ref::<IntegerBox>(),
b.as_any().downcast_ref::<IntegerBox>() b.as_any().downcast_ref::<IntegerBox>(),
) { ) {
Box::new(IntegerBox::new(a_int.value.min(b_int.value))) Box::new(IntegerBox::new(a_int.value.min(b_int.value)))
} else if let (Some(a_float), Some(b_float)) = ( } else if let (Some(a_float), Some(b_float)) = (
a.as_any().downcast_ref::<FloatBox>(), a.as_any().downcast_ref::<FloatBox>(),
b.as_any().downcast_ref::<FloatBox>() b.as_any().downcast_ref::<FloatBox>(),
) { ) {
Box::new(FloatBox::new(a_float.value.min(b_float.value))) Box::new(FloatBox::new(a_float.value.min(b_float.value)))
} else { } else {
@ -122,7 +122,7 @@ impl MathBox {
pub fn pow(&self, base: Box<dyn NyashBox>, exp: Box<dyn NyashBox>) -> Box<dyn NyashBox> { pub fn pow(&self, base: Box<dyn NyashBox>, exp: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(base_int), Some(exp_int)) = ( if let (Some(base_int), Some(exp_int)) = (
base.as_any().downcast_ref::<IntegerBox>(), base.as_any().downcast_ref::<IntegerBox>(),
exp.as_any().downcast_ref::<IntegerBox>() exp.as_any().downcast_ref::<IntegerBox>(),
) { ) {
if exp_int.value >= 0 { if exp_int.value >= 0 {
let result = (base_int.value as f64).powi(exp_int.value as i32); let result = (base_int.value as f64).powi(exp_int.value as i32);
@ -329,7 +329,6 @@ impl NyashBox for MathBox {
BoolBox::new(false) BoolBox::new(false)
} }
} }
} }
impl Display for MathBox { impl Display for MathBox {
@ -349,7 +348,7 @@ impl FloatBox {
pub fn new(value: f64) -> Self { pub fn new(value: f64) -> Self {
Self { Self {
value, value,
base: BoxBase::new() base: BoxBase::new(),
} }
} }
} }
@ -403,7 +402,6 @@ impl NyashBox for FloatBox {
BoolBox::new(false) BoolBox::new(false)
} }
} }
} }
impl Display for FloatBox { impl Display for FloatBox {
@ -427,7 +425,7 @@ impl RangeBox {
start, start,
end, end,
step, step,
base: BoxBase::new() base: BoxBase::new(),
} }
} }
@ -480,7 +478,10 @@ impl NyashBox for RangeBox {
} }
fn to_string_box(&self) -> StringBox { fn to_string_box(&self) -> StringBox {
StringBox::new(&format!("Range({}, {}, {})", self.start, self.end, self.step)) StringBox::new(&format!(
"Range({}, {}, {})",
self.start, self.end, self.step
))
} }
fn clone_box(&self) -> Box<dyn NyashBox> { fn clone_box(&self) -> Box<dyn NyashBox> {
@ -495,15 +496,14 @@ impl NyashBox for RangeBox {
fn equals(&self, other: &dyn NyashBox) -> BoolBox { fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_range) = other.as_any().downcast_ref::<RangeBox>() { if let Some(other_range) = other.as_any().downcast_ref::<RangeBox>() {
BoolBox::new( BoolBox::new(
self.start == other_range.start && self.start == other_range.start
self.end == other_range.end && && self.end == other_range.end
self.step == other_range.step && self.step == other_range.step,
) )
} else { } else {
BoolBox::new(false) BoolBox::new(false)
} }
} }
} }
impl Display for RangeBox { impl Display for RangeBox {

Some files were not shown because too many files have changed in this diff Show More