diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 33e4af0c..4356c730 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -342,3 +342,58 @@ Namespaces / Using(現状) **AOT Quick** - 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` + +Refactoring Plan (Phase‑15 follow‑up) +- 背景: 巨大ファイル(AST/MIR/LLVM)が保守コスト増の主因。機能非変化の小刻みリファクタで視認性と変更容易性を上げる。 + +- 目的と範囲(非機能変更・段階適用) + 1) AST(src/ast.rs:1): 定義とヘルパの分離、将来のサブenum活用導線の整備(現行API互換)。 + 2) MIR Builder(src/mir/builder.rs:1): build_module の骨格化・責務分離、既存分割(builder/*)の徹底。 + 3) Python LLVM(src/llvm_py/llvm_builder.py:1): lower_function の前処理/本体分離、lower_block の責務拡張。 + 4) MIR 命令(src/mir/instruction.rs:1): 構造体+トレイト導線の導入(enum への委譲を維持した非破壊移行)。 + +- 実施順(小PR単位、CI緑維持) + PR‑1: AST utils 抽出(非破壊) + - 追加: `src/ast/utils.rs` に classify/info/span/to_string などのヘルパを移設。 + - `src/ast.rs` は ASTNode/StructureNode/ExpressionNode/StatementNode の定義中心に縮退。 + - 互換維持: `pub use ast::utils::*;` で既存呼び出しを壊さない。 + - 受入: 全ビルド/スモーク緑、差分はファイル移動のみ。 + + PR‑2: MIR Builder build_module 分割(非破壊) + - `build_module` を `prepare_module`/`lower_root`/`finalize_module` に3分割。 + - 型推定(value_types→返り値)は finalize 側へ集約(現行ロジック移設)。 + - 既存の exprs/stmts などの委譲を明示し、build_module 本体を「骨格のみ」に縮退。 + - 受入: LLVM/PyVM/Bridge スモーク緑(挙動非変化)。 + + PR‑3: Python LLVM lower_function の前処理抽出 + - 新設: `setup_phi_placeholders()` を導入し、PHI 宣言/CFG 依存前処理をここへ移設。 + - `lower_block()` に snapshot(block_end_values 収集)までの責務を移動。メインループは薄い周回に。 + - 受入: `tools/smokes/curated_llvm.sh` / `curated_llvm_stage3.sh` 緑。 + + PR‑4: AST ラッパー導入(非破壊導線) + - 追加: `src/ast/nodes.rs` に小さな構造体群(Assign/Return/...)。 + - enum `StatementNode/ExpressionNode` を構造体で保持。`From for ASTNode` / `TryFrom for T` を提供。 + - Builder 入口(`builder/stmts.rs`, `builder/exprs.rs`)で ASTNode → TryFrom 変換を 1 行追加し以降はサブ型で match。 + - 受入: ビルド/スモーク緑(機能非変化)。 + + PR‑5: MIR 命令トレイト POC(Const/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 と共存し、段階移行を可能にする。 diff --git a/examples/nyash_explorer_with_icons.rs b/examples/nyash_explorer_with_icons.rs index ead77a0e..ad77ceae 100644 --- a/examples/nyash_explorer_with_icons.rs +++ b/examples/nyash_explorer_with_icons.rs @@ -424,7 +424,7 @@ impl eframe::App for NyashExplorer { ctx.send_viewport_cmd(egui::ViewportCommand::Close); } }); - + ui.menu_button("表示", |ui| { if ui.button("大きいアイコン").clicked() { self.status = "表示モード: 大きいアイコン".to_string(); @@ -433,7 +433,7 @@ impl eframe::App for NyashExplorer { self.status = "表示モード: 詳細".to_string(); } }); - + ui.menu_button("ヘルプ", |ui| { if ui.button("Nyash Explorerについて").clicked() { self.status = "Nyash Explorer - Everything is Box! アイコンも取得できる化け物言語!🐱".to_string(); diff --git a/src/ast.rs b/src/ast.rs index 588db91c..745f700a 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,6 +1,6 @@ /*! * Nyash AST (Abstract Syntax Tree) - Rust Implementation - * + * * Python版nyashc_v4.pyのAST構造をRustで完全再実装 * Everything is Box哲学に基づく型安全なAST設計 */ @@ -20,9 +20,9 @@ mod utils; /// ASTノードの種類分類 #[derive(Debug, Clone, PartialEq)] pub enum ASTNodeType { - Structure, // 構造定義: box, function, if, loop, try/catch - Expression, // 式: リテラル, 変数, 演算, 呼び出し - Statement, // 文: 代入, return, break, include + Structure, // 構造定義: box, function, if, loop, try/catch + Expression, // 式: リテラル, 変数, 演算, 呼び出し + Statement, // 文: 代入, return, break, include } /// 構造ノード - 言語の基本構造を定義 @@ -34,9 +34,9 @@ pub enum StructureNode { methods: Vec, constructors: Vec, init_fields: Vec, - weak_fields: Vec, // 🔗 weak修飾子が付いたフィールドのリスト + weak_fields: Vec, // 🔗 weak修飾子が付いたフィールドのリスト is_interface: bool, - extends: Vec, // 🚀 Multi-delegation: Changed from Option to Vec + extends: Vec, // 🚀 Multi-delegation: Changed from Option to Vec implements: Vec, /// 🔥 ジェネリクス型パラメータ (例: ["T", "U"]) type_parameters: Vec, @@ -50,8 +50,8 @@ pub enum StructureNode { name: String, params: Vec, body: Vec, - is_static: bool, // 🔥 静的メソッドフラグ - is_override: bool, // 🔥 オーバーライドフラグ + is_static: bool, // 🔥 静的メソッドフラグ + is_override: bool, // 🔥 オーバーライドフラグ span: Span, }, IfStructure { @@ -178,8 +178,8 @@ pub enum StatementNode { /// Catch節の構造体 #[derive(Debug, Clone, PartialEq)] pub struct CatchClause { - pub exception_type: Option, // None = catch-all - pub variable_name: Option, // 例外を受け取る変数名 + pub exception_type: Option, // None = catch-all + pub variable_name: Option, // 例外を受け取る変数名 pub body: Vec, // catch本体 pub span: Span, // ソースコード位置 } @@ -189,18 +189,18 @@ pub struct CatchClause { pub enum LiteralValue { String(String), Integer(i64), - Float(f64), // 浮動小数点数サポート追加 + Float(f64), // 浮動小数点数サポート追加 Bool(bool), - Null, // null値 + Null, // null値 Void, } impl LiteralValue { /// LiteralValueをNyashBoxに変換 pub fn to_nyash_box(&self) -> Box { - use crate::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox}; + use crate::box_trait::{BoolBox, IntegerBox, StringBox, VoidBox}; use crate::boxes::FloatBox; - + match self { LiteralValue::String(s) => Box::new(StringBox::new(s)), LiteralValue::Integer(i) => Box::new(IntegerBox::new(*i)), @@ -210,14 +210,14 @@ impl LiteralValue { LiteralValue::Void => Box::new(VoidBox::new()), } } - + /// NyashBoxからLiteralValueに変換 pub fn from_nyash_box(box_val: &dyn NyashBox) -> Option { + use crate::box_trait::{BoolBox, IntegerBox, StringBox, VoidBox}; + use crate::boxes::FloatBox; #[allow(unused_imports)] 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::() { Some(LiteralValue::String(string_box.value.clone())) } else if let Some(int_box) = box_val.as_any().downcast_ref::() { @@ -226,7 +226,11 @@ impl LiteralValue { Some(LiteralValue::Float(float_box.value)) } else if let Some(bool_box) = box_val.as_any().downcast_ref::() { Some(LiteralValue::Bool(bool_box.value)) - } else if box_val.as_any().downcast_ref::().is_some() { + } else if box_val + .as_any() + .downcast_ref::() + .is_some() + { Some(LiteralValue::Null) } else if box_val.as_any().downcast_ref::().is_some() { Some(LiteralValue::Void) @@ -252,22 +256,22 @@ impl fmt::Display for LiteralValue { /// 単項演算子の種類 #[derive(Debug, Clone, PartialEq)] pub enum UnaryOperator { - Minus, // -x - Not, // not x + Minus, // -x + Not, // not x } /// 二項演算子の種類 #[derive(Debug, Clone, PartialEq)] pub enum BinaryOperator { Add, - Subtract, + Subtract, Multiply, Divide, Modulo, BitAnd, BitOr, BitXor, - Shl, // << shift-left (Phase 1) + Shl, // << shift-left (Phase 1) Shr, Equal, NotEqual, @@ -323,22 +327,21 @@ pub enum ASTNode { statements: Vec, span: Span, }, - + // ===== 文 (Statements) ===== - /// 代入文: target = value Assignment { target: Box, value: Box, span: Span, }, - + /// print文: print(expression) Print { expression: Box, span: Span, }, - + /// if文: if condition { then_body } else { else_body } If { condition: Box, @@ -346,60 +349,53 @@ pub enum ASTNode { else_body: Option>, span: Span, }, - + /// loop文: loop(condition) { body } のみ Loop { condition: Box, body: Vec, span: Span, }, - + /// return文: return value Return { value: Option>, span: Span, }, - + /// break文 - Break { - span: Span, - }, + Break { span: Span }, /// continue文 - Continue { - span: Span, - }, - + Continue { span: Span }, + /// using文: using namespace_name - UsingStatement { - namespace_name: String, - span: Span, - }, + UsingStatement { namespace_name: String, span: Span }, /// import文: import "path" (as Alias)? ImportStatement { path: String, alias: Option, span: Span, }, - + /// nowait文: nowait variable = expression Nowait { variable: String, expression: Box, span: Span, }, - + /// await式: await expression AwaitExpression { expression: Box, span: Span, }, - + /// result伝播: expr? (ResultBoxなら isOk/getValue or 早期return) QMarkPropagate { expression: Box, span: Span, }, - + /// peek式: peek { lit => expr, ... else => expr } PeekExpr { scrutinee: Box, @@ -408,30 +404,27 @@ pub enum ASTNode { span: Span, }, /// 配列リテラル(糖衣): [e1, e2, ...] - ArrayLiteral { - elements: Vec, - span: Span, - }, + ArrayLiteral { elements: Vec, span: Span }, /// マップリテラル(糖衣): { "k": v, ... } (Stage‑2: 文字列キー限定) MapLiteral { entries: Vec<(String, ASTNode)>, span: Span, }, - + /// 無名関数(最小P1: 値としてのみ。呼び出しは未対応) Lambda { params: Vec, body: Vec, span: Span, }, - + /// arrow文: (sender >> receiver).method(args) Arrow { sender: Box, receiver: Box, span: Span, }, - + /// try/catch/finally文: try { ... } catch (Type e) { ... } finally { ... } TryCatch { try_body: Vec, @@ -439,15 +432,14 @@ pub enum ASTNode { finally_body: Option>, span: Span, }, - + /// throw文: throw expression Throw { expression: Box, span: Span, }, - + // ===== 宣言 (Declarations) ===== - /// box宣言: box Name { fields... methods... } BoxDeclaration { name: String, @@ -458,65 +450,58 @@ pub enum ASTNode { private_fields: Vec, methods: HashMap, // method_name -> FunctionDeclaration constructors: HashMap, // constructor_key -> FunctionDeclaration - init_fields: Vec, // initブロック内のフィールド定義 - weak_fields: Vec, // 🔗 weak修飾子が付いたフィールドのリスト - is_interface: bool, // interface box かどうか - extends: Vec, // 🚀 Multi-delegation: Changed from Option to Vec - implements: Vec, // 実装するinterface名のリスト - type_parameters: Vec, // 🔥 ジェネリクス型パラメータ (例: ["T", "U"]) + init_fields: Vec, // initブロック内のフィールド定義 + weak_fields: Vec, // 🔗 weak修飾子が付いたフィールドのリスト + is_interface: bool, // interface box かどうか + extends: Vec, // 🚀 Multi-delegation: Changed from Option to Vec + implements: Vec, // 実装するinterface名のリスト + type_parameters: Vec, // 🔥 ジェネリクス型パラメータ (例: ["T", "U"]) /// 🔥 Static boxかどうかのフラグ is_static: bool, /// 🔥 Static初期化ブロック (static { ... }) static_init: Option>, span: Span, }, - + /// 関数宣言: functionName(params) { body } FunctionDeclaration { name: String, params: Vec, body: Vec, - is_static: bool, // 🔥 静的メソッドフラグ - is_override: bool, // 🔥 オーバーライドフラグ + is_static: bool, // 🔥 静的メソッドフラグ + is_override: bool, // 🔥 オーバーライドフラグ span: Span, }, - + /// グローバル変数: global name = value GlobalVar { name: String, value: Box, span: Span, }, - + // ===== 式 (Expressions) ===== - /// リテラル値: "string", 42, true, etc - Literal { - value: LiteralValue, - span: Span, - }, - + Literal { value: LiteralValue, span: Span }, + /// 変数参照: variableName - Variable { - name: String, - span: Span, - }, - + Variable { name: String, span: Span }, + /// 単項演算: operator operand UnaryOp { operator: UnaryOperator, operand: Box, span: Span, }, - + /// 二項演算: left operator right BinaryOp { operator: BinaryOperator, left: Box, right: Box, - span: Span, + span: Span, }, - + /// メソッド呼び出し: object.method(arguments) MethodCall { object: Box, @@ -524,58 +509,45 @@ pub enum ASTNode { arguments: Vec, span: Span, }, - + /// フィールドアクセス: object.field FieldAccess { object: Box, field: String, span: Span, }, - + /// コンストラクタ呼び出し: new ClassName(arguments) New { class: String, arguments: Vec, - type_arguments: Vec, // 🔥 ジェネリクス型引数 (例: ["IntegerBox", "StringBox"]) + type_arguments: Vec, // 🔥 ジェネリクス型引数 (例: ["IntegerBox", "StringBox"]) span: Span, }, - + /// this参照 - This { - span: Span, - }, - + This { span: Span }, + /// me参照 - Me { - span: Span, - }, - + Me { span: Span }, + /// 🔥 from呼び出し: from Parent.method(arguments) or from Parent.constructor(arguments) FromCall { - parent: String, // Parent名 - method: String, // method名またはconstructor + parent: String, // Parent名 + method: String, // method名またはconstructor arguments: Vec, // 引数 span: Span, }, - + /// thisフィールドアクセス: this.field - ThisField { - field: String, - span: Span, - }, - + ThisField { field: String, span: Span }, + /// meフィールドアクセス: me.field - MeField { - field: String, - span: Span, - }, - + MeField { field: String, span: Span }, + /// ファイル読み込み: include "filename.nyash" - Include { - filename: String, - span: Span, - }, - + Include { filename: String, span: Span }, + /// ローカル変数宣言: local x, y, z Local { variables: Vec, @@ -583,7 +555,7 @@ pub enum ASTNode { initial_values: Vec>>, span: Span, }, - + /// Outbox変数宣言: outbox x, y, z (static関数内専用) Outbox { variables: Vec, @@ -591,14 +563,14 @@ pub enum ASTNode { initial_values: Vec>>, span: Span, }, - + /// 関数呼び出し: functionName(arguments) FunctionCall { name: String, arguments: Vec, span: Span, }, - + /// 一般式呼び出し: (callee)(arguments) Call { callee: Box, diff --git a/src/ast/span.rs b/src/ast/span.rs index 04e57eab..f07f9311 100644 --- a/src/ast/span.rs +++ b/src/ast/span.rs @@ -3,23 +3,33 @@ use std::fmt; /// ソースコード位置情報 - エラー報告とデバッグの革命 #[derive(Debug, Clone, Copy, PartialEq)] pub struct Span { - pub start: usize, // 開始位置(バイトオフセット) - pub end: usize, // 終了位置(バイトオフセット) - pub line: usize, // 行番号(1から開始) - pub column: usize, // 列番号(1から開始) + pub start: usize, // 開始位置(バイトオフセット) + pub end: usize, // 終了位置(バイトオフセット) + pub line: usize, // 行番号(1から開始) + pub column: usize, // 列番号(1から開始) } impl Span { /// 新しいSpanを作成 pub fn new(start: usize, end: usize, line: usize, column: usize) -> Self { - Self { start, end, line, column } + Self { + start, + end, + line, + column, + } } - + /// デフォルトのSpan(不明な位置) pub fn unknown() -> Self { - Self { start: 0, end: 0, line: 1, column: 1 } + Self { + start: 0, + end: 0, + line: 1, + column: 1, + } } - + /// 2つのSpanを結合(開始位置から終了位置まで) pub fn merge(&self, other: Span) -> Span { Span { @@ -29,34 +39,40 @@ impl Span { column: self.column, } } - + /// ソースコードから該当箇所を抽出してエラー表示用文字列を生成 pub fn error_context(&self, source: &str) -> String { let lines: Vec<&str> = source.lines().collect(); if self.line == 0 || self.line > lines.len() { return format!("line {}, column {}", self.line, self.column); } - + let line_content = lines[self.line - 1]; let mut context = String::new(); - + // 行番号とソース行を表示 context.push_str(&format!(" |\n{:3} | {}\n", self.line, line_content)); - + // カーソル位置を表示(簡易版) if self.column > 0 && self.column <= line_content.len() + 1 { context.push_str(" | "); - for _ in 1..self.column { context.push(' '); } - let span_length = if self.end > self.start { + for _ in 1..self.column { + context.push(' '); + } + let span_length = if self.end > self.start { (self.end - self.start).min(line_content.len() - self.column + 1) - } else { 1 }; - for _ in 0..span_length.max(1) { context.push('^'); } + } else { + 1 + }; + for _ in 0..span_length.max(1) { + context.push('^'); + } context.push('\n'); } - + context } - + /// 位置情報の文字列表現 pub fn location_string(&self) -> String { format!("line {}, column {}", self.line, self.column) @@ -68,4 +84,3 @@ impl fmt::Display for Span { write!(f, "line {}, column {}", self.line, self.column) } } - diff --git a/src/backend/abi_util.rs b/src/backend/abi_util.rs index 0af79603..3b98eaaa 100644 --- a/src/backend/abi_util.rs +++ b/src/backend/abi_util.rs @@ -5,8 +5,8 @@ * 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::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox}; use std::sync::Arc; /// Opaque handle type used by JIT/runtime bridges. @@ -80,4 +80,3 @@ pub fn handle_of(boxref: Arc) -> Handle { pub fn handle_get(h: Handle) -> Option> { crate::jit::rt::handles::get(h) } - diff --git a/src/backend/aot/compiler.rs b/src/backend/aot/compiler.rs index 90352bca..725450c3 100644 --- a/src/backend/aot/compiler.rs +++ b/src/backend/aot/compiler.rs @@ -1,14 +1,14 @@ /*! * AOT Compiler - Converts MIR to precompiled native code - * + * * Handles the MIR -> WASM -> Native compilation pipeline */ -use super::{AotError, AotConfig, AotStats}; -use crate::mir::MirModule; +use super::{AotConfig, AotError, AotStats}; use crate::backend::wasm::{WasmBackend, WasmError}; -use wasmtime::{Engine, Module}; +use crate::mir::MirModule; use std::time::Instant; +use wasmtime::{Engine, Module}; /// AOT compiler that handles the full compilation pipeline pub struct AotCompiler { @@ -21,109 +21,123 @@ impl AotCompiler { /// Create a new AOT compiler with the given configuration pub fn new(config: &AotConfig) -> Result { // Create wasmtime engine with optimized configuration - let engine = Engine::new(config.wasmtime_config()) - .map_err(|e| AotError::WasmtimeError(format!("Failed to create wasmtime engine: {}", e)))?; - + let engine = Engine::new(config.wasmtime_config()).map_err(|e| { + AotError::WasmtimeError(format!("Failed to create wasmtime engine: {}", e)) + })?; + // Create WASM backend for MIR -> WASM compilation let wasm_backend = WasmBackend::new(); - + let stats = AotStats { wasm_size: 0, precompiled_size: 0, compilation_time_ms: 0, optimization_level: format!("O{}", config.optimization_level()), }; - + Ok(Self { wasm_backend, wasmtime_engine: engine, stats, }) } - + /// Compile MIR module to WASM bytecode pub fn compile_mir_to_wasm(&mut self, mir_module: MirModule) -> Result, AotError> { let start_time = Instant::now(); - + // 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 { - WasmError::CodegenError(msg) => AotError::CompilationError(format!("WASM codegen 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::CodegenError(msg) => { + AotError::CompilationError(format!("WASM codegen 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), })?; - + self.stats.wasm_size = wasm_bytes.len(); self.stats.compilation_time_ms += start_time.elapsed().as_millis() as u64; - + Ok(wasm_bytes) } - + /// Precompile WASM bytecode to native machine code pub fn precompile_wasm(&mut self, wasm_bytes: &[u8]) -> Result, AotError> { let start_time = Instant::now(); - + // Parse and validate the WASM module let module = Module::from_binary(&self.wasmtime_engine, wasm_bytes) .map_err(|e| AotError::WasmtimeError(format!("Failed to parse WASM module: {}", e)))?; - + // Serialize the precompiled module to bytes - let precompiled_bytes = module.serialize() - .map_err(|e| AotError::WasmtimeError(format!("Failed to serialize precompiled module: {}", e)))?; - + let precompiled_bytes = module.serialize().map_err(|e| { + AotError::WasmtimeError(format!("Failed to serialize precompiled module: {}", e)) + })?; + self.stats.precompiled_size = precompiled_bytes.len(); self.stats.compilation_time_ms += start_time.elapsed().as_millis() as u64; - + Ok(precompiled_bytes) } - + /// Compile MIR directly to precompiled native code (convenience method) pub fn compile_mir_to_native(&mut self, mir_module: MirModule) -> Result, AotError> { let wasm_bytes = self.compile_mir_to_wasm(mir_module)?; self.precompile_wasm(&wasm_bytes) } - + /// Load and execute a precompiled module (for testing) pub fn execute_precompiled(&self, precompiled_bytes: &[u8]) -> Result { // Deserialize the precompiled module let module = unsafe { - Module::deserialize(&self.wasmtime_engine, precompiled_bytes) - .map_err(|e| AotError::WasmtimeError(format!("Failed to deserialize module: {}", e)))? + Module::deserialize(&self.wasmtime_engine, precompiled_bytes).map_err(|e| { + AotError::WasmtimeError(format!("Failed to deserialize module: {}", e)) + })? }; - + // Create instance and execute let mut store = wasmtime::Store::new(&self.wasmtime_engine, ()); let instance = wasmtime::Instance::new(&mut store, &module, &[]) .map_err(|e| AotError::RuntimeError(format!("Failed to create instance: {}", e)))?; - + // Look for main function or default export let main_func = instance .get_typed_func::<(), i32>(&mut store, "main") .or_else(|_| instance.get_typed_func::<(), i32>(&mut store, "_start")) .or_else(|_| instance.get_typed_func::<(), i32>(&mut store, "run")) .map_err(|e| AotError::RuntimeError(format!("No main function found: {}", e)))?; - + // 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)))?; - + Ok(result) } - + /// Validate a WASM module before precompilation pub fn validate_wasm(&self, wasm_bytes: &[u8]) -> Result<(), AotError> { Module::validate(&self.wasmtime_engine, wasm_bytes) .map_err(|e| AotError::WasmtimeError(format!("WASM validation failed: {}", e)))?; Ok(()) } - + /// Get compilation statistics pub fn get_stats(&self) -> AotStats { self.stats.clone() } - + /// Reset statistics pub fn reset_stats(&mut self) { self.stats = AotStats { @@ -133,7 +147,7 @@ impl AotCompiler { optimization_level: self.stats.optimization_level.clone(), }; } - + /// Get compression ratio (precompiled size / WASM size) pub fn compression_ratio(&self) -> f64 { if self.stats.wasm_size == 0 { @@ -141,7 +155,7 @@ impl AotCompiler { } self.stats.precompiled_size as f64 / self.stats.wasm_size as f64 } - + /// Get wasmtime engine info pub fn engine_info(&self) -> String { format!( @@ -155,7 +169,7 @@ impl AotCompiler { mod tests { use super::*; use crate::mir::MirModule; - + #[test] fn test_compiler_creation() { let config = AotConfig::new().expect("Failed to create config"); @@ -163,13 +177,13 @@ mod tests { // Should not panic assert!(true); } - + #[test] fn test_empty_module_compilation() { let config = AotConfig::new().expect("Failed to create config"); let mut compiler = AotCompiler::new(&config).expect("Failed to create compiler"); let module = MirModule::new("test".to_string()); - + // Should handle empty module gracefully let result = compiler.compile_mir_to_wasm(module); // Note: This might fail due to empty module, but should not panic @@ -179,45 +193,45 @@ mod tests { Err(_) => assert!(true), // Empty modules might legitimately fail } } - + #[test] fn test_stats_tracking() { let config = AotConfig::new().expect("Failed to create config"); let compiler = AotCompiler::new(&config).expect("Failed to create compiler"); let stats = compiler.get_stats(); - + assert_eq!(stats.wasm_size, 0); assert_eq!(stats.precompiled_size, 0); assert_eq!(stats.compilation_time_ms, 0); assert!(stats.optimization_level.contains("O")); } - + #[test] fn test_wasm_validation() { let config = AotConfig::new().expect("Failed to create config"); let compiler = AotCompiler::new(&config).expect("Failed to create compiler"); - + // Test with invalid WASM bytes let invalid_wasm = vec![0x00, 0x61, 0x73, 0x6d]; // Incomplete WASM header assert!(compiler.validate_wasm(&invalid_wasm).is_err()); } - + #[test] fn test_compression_ratio() { let config = AotConfig::new().expect("Failed to create config"); let compiler = AotCompiler::new(&config).expect("Failed to create compiler"); - + // With no compilation done, ratio should be 0 assert_eq!(compiler.compression_ratio(), 0.0); } - + #[test] fn test_engine_info() { let config = AotConfig::new().expect("Failed to create config"); let compiler = AotCompiler::new(&config).expect("Failed to create compiler"); let info = compiler.engine_info(); - + assert!(info.contains("Wasmtime")); assert!(info.contains("Cranelift")); } -} \ No newline at end of file +} diff --git a/src/backend/aot/config.rs b/src/backend/aot/config.rs index 9d4fe725..39dc3c97 100644 --- a/src/backend/aot/config.rs +++ b/src/backend/aot/config.rs @@ -1,6 +1,6 @@ /*! * AOT Configuration - Wasmtime optimization settings - * + * * Manages compilation settings, CPU features, and performance tuning */ @@ -22,25 +22,25 @@ impl AotConfig { /// Create default configuration optimized for performance pub fn new() -> Result { let mut config = Config::new(); - + // Enable maximum optimizations config.strategy(Strategy::Cranelift); config.cranelift_opt_level(OptLevel::Speed); - + // Enable WebAssembly features for better performance config.wasm_simd(true); config.wasm_bulk_memory(true); config.wasm_multi_memory(true); - + // Enable advanced optimizations unsafe { config.cranelift_flag_enable("enable_verifier"); config.cranelift_flag_enable("enable_nan_canonicalization"); } - + // Set memory limits for safety (64MB max) config.max_wasm_stack(8 * 1024 * 1024); // 8MB stack - + let target_arch = if cfg!(target_arch = "x86_64") { "x86_64" } else if cfg!(target_arch = "aarch64") { @@ -49,8 +49,9 @@ impl AotConfig { "x86" } else { "unknown" - }.to_string(); - + } + .to_string(); + Ok(Self { wasmtime_config: config, optimization_level: 3, // Maximum optimization @@ -60,23 +61,23 @@ impl AotConfig { target_arch, }) } - + /// Create configuration optimized for debug builds pub fn debug() -> Result { let mut config = Config::new(); - + config.strategy(Strategy::Cranelift); config.cranelift_opt_level(OptLevel::None); - + // Enable debug features config.debug_info(true); - + // Basic WASM features only config.wasm_simd(false); config.wasm_bulk_memory(true); - + let target_arch = std::env::consts::ARCH.to_string(); - + Ok(Self { wasmtime_config: config, optimization_level: 0, @@ -86,60 +87,63 @@ impl AotConfig { target_arch, }) } - + /// Create configuration for specific target architecture pub fn for_target(target: &str) -> Result { let mut config = Self::new()?; config.target_arch = target.to_string(); - + // Adjust features based on target match target { "x86_64" => { // Enable all advanced features for x86_64 config.enable_simd = true; config.enable_multi_memory = true; - }, + } "aarch64" => { // ARM64 - enable SIMD but be conservative with memory features config.enable_simd = true; config.enable_multi_memory = false; - }, + } "x86" => { // x86 - be conservative config.enable_simd = false; config.enable_multi_memory = false; - }, + } _ => { - return Err(AotError::ConfigError(format!("Unsupported target architecture: {}", target))); + return Err(AotError::ConfigError(format!( + "Unsupported target architecture: {}", + target + ))); } } - + // Rebuild wasmtime config with new settings config.rebuild_wasmtime_config()?; - + Ok(config) } - + /// Get the wasmtime configuration pub fn wasmtime_config(&self) -> &Config { &self.wasmtime_config } - + /// Get optimization level (0-3) pub fn optimization_level(&self) -> u8 { self.optimization_level } - + /// Get target architecture pub fn target_arch(&self) -> &str { &self.target_arch } - + /// Check if SIMD is enabled pub fn simd_enabled(&self) -> bool { self.enable_simd } - + /// Get compatibility key for cache validation pub fn compatibility_key(&self) -> String { format!( @@ -152,13 +156,13 @@ impl AotConfig { "18.0" // Wasmtime version from Cargo.toml ) } - + /// Rebuild wasmtime config with current settings fn rebuild_wasmtime_config(&mut self) -> Result<(), AotError> { let mut config = Config::new(); - + config.strategy(Strategy::Cranelift); - + let opt_level = match self.optimization_level { 0 => OptLevel::None, 1 => OptLevel::Speed, @@ -166,35 +170,37 @@ impl AotConfig { 3 => OptLevel::SpeedAndSize, _ => OptLevel::Speed, }; - + config.cranelift_opt_level(opt_level); config.wasm_simd(self.enable_simd); config.wasm_bulk_memory(self.enable_bulk_memory); config.wasm_multi_memory(self.enable_multi_memory); - + // Set memory limits config.max_wasm_stack(8 * 1024 * 1024); // 8MB stack - + if self.optimization_level >= 2 { unsafe { config.cranelift_flag_enable("enable_verifier"); } } - + self.wasmtime_config = config; Ok(()) } - + /// Set custom optimization level pub fn set_optimization_level(&mut self, level: u8) -> Result<(), AotError> { 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.rebuild_wasmtime_config() } - + /// Enable or disable SIMD pub fn set_simd(&mut self, enabled: bool) -> Result<(), AotError> { self.enable_simd = enabled; @@ -211,21 +217,21 @@ impl Default for AotConfig { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_default_config() { let config = AotConfig::new().expect("Failed to create config"); assert_eq!(config.optimization_level(), 3); assert!(config.simd_enabled()); } - + #[test] fn test_debug_config() { let config = AotConfig::debug().expect("Failed to create debug config"); assert_eq!(config.optimization_level(), 0); assert!(!config.simd_enabled()); } - + #[test] fn test_compatibility_key() { let config = AotConfig::new().expect("Failed to create config"); @@ -233,24 +239,26 @@ mod tests { assert!(key.contains("nyash-aot")); assert!(key.contains("wasmtime")); } - + #[test] fn test_target_config() { let config = AotConfig::for_target("x86_64").expect("Failed to create x86_64 config"); assert_eq!(config.target_arch(), "x86_64"); assert!(config.simd_enabled()); } - + #[test] fn test_optimization_level_setting() { 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); } - + #[test] fn test_invalid_optimization_level() { let mut config = AotConfig::new().expect("Failed to create config"); assert!(config.set_optimization_level(4).is_err()); } -} \ No newline at end of file +} diff --git a/src/backend/aot/executable.rs b/src/backend/aot/executable.rs index cd7c226b..e7d497ff 100644 --- a/src/backend/aot/executable.rs +++ b/src/backend/aot/executable.rs @@ -1,12 +1,12 @@ /*! * Executable Builder - Creates standalone native executables - * + * * Embeds precompiled WASM modules into self-contained executables */ -use super::{AotError, AotConfig}; -use std::path::Path; +use super::{AotConfig, AotError}; use std::fs; +use std::path::Path; /// Builder for creating standalone executable files pub struct ExecutableBuilder<'a> { @@ -24,73 +24,79 @@ impl<'a> ExecutableBuilder<'a> { runtime_template: RUNTIME_TEMPLATE, } } - + /// Embed precompiled module data pub fn embed_precompiled_module(&mut self, module_data: Vec) -> Result<(), AotError> { self.precompiled_module = Some(module_data); Ok(()) } - + /// Create the standalone executable pub fn create_executable>(&self, output_path: P) -> Result<(), AotError> { - let module_data = self.precompiled_module.as_ref() - .ok_or_else(|| AotError::CompilationError("No precompiled module embedded".to_string()))?; - + let module_data = self.precompiled_module.as_ref().ok_or_else(|| { + AotError::CompilationError("No precompiled module embedded".to_string()) + })?; + // Generate the runtime code with embedded module let runtime_code = self.generate_runtime_code(module_data)?; - + // Write to temporary Rust source file let temp_dir = std::env::temp_dir(); let temp_main = temp_dir.join("nyash_aot_main.rs"); let temp_cargo = temp_dir.join("Cargo.toml"); - + fs::write(&temp_main, runtime_code)?; fs::write(&temp_cargo, self.generate_cargo_toml())?; - + // Compile with Rust compiler self.compile_rust_executable(&temp_dir, output_path)?; - + // Clean up temporary files let _ = fs::remove_file(&temp_main); let _ = fs::remove_file(&temp_cargo); - + Ok(()) } - + /// Generate the runtime code with embedded module fn generate_runtime_code(&self, module_data: &[u8]) -> Result { let module_bytes = self.format_module_bytes(module_data); 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("{{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("{{WASMTIME_VERSION}}", "18.0"); - + Ok(runtime_code) } - + /// Format module bytes as Rust byte array literal fn format_module_bytes(&self, data: &[u8]) -> String { let mut result = String::with_capacity(data.len() * 6); result.push_str("&[\n "); - + for (i, byte) in data.iter().enumerate() { if i > 0 && i % 16 == 0 { result.push_str("\n "); } result.push_str(&format!("0x{:02x}, ", byte)); } - + result.push_str("\n]"); result } - + /// Generate Cargo.toml for the executable fn generate_cargo_toml(&self) -> String { - format!(r#"[package] + format!( + r#"[package] name = "nyash-aot-executable" version = "0.1.0" edition = "2021" @@ -108,27 +114,36 @@ strip = true [[bin]] name = "nyash-aot-executable" path = "nyash_aot_main.rs" -"#) +"# + ) } - + /// Compile the Rust executable - fn compile_rust_executable, Q: AsRef>(&self, temp_dir: P, output_path: Q) -> Result<(), AotError> { + fn compile_rust_executable, Q: AsRef>( + &self, + temp_dir: P, + output_path: Q, + ) -> Result<(), AotError> { let temp_dir = temp_dir.as_ref(); let output_path = output_path.as_ref(); - + // Use cargo to compile let mut cmd = std::process::Command::new("cargo"); cmd.current_dir(temp_dir) - .args(&["build", "--release", "--bin", "nyash-aot-executable"]); - - let output = cmd.output() + .args(&["build", "--release", "--bin", "nyash-aot-executable"]); + + let output = cmd + .output() .map_err(|e| AotError::CompilationError(format!("Failed to run cargo: {}", e)))?; - + if !output.status.success() { 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 let compiled_exe = temp_dir.join("target/release/nyash-aot-executable"); let compiled_exe = if cfg!(windows) { @@ -136,14 +151,16 @@ path = "nyash_aot_main.rs" } else { compiled_exe }; - + 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) .map_err(|e| AotError::IOError(format!("Failed to copy executable: {}", e)))?; - + Ok(()) } } @@ -223,7 +240,7 @@ fn run_aot_module() -> Result<(), Box> { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_executable_builder_creation() { let config = AotConfig::new().expect("Failed to create config"); @@ -231,50 +248,54 @@ mod tests { // Should not panic assert!(true); } - + #[test] fn test_embed_module() { let config = AotConfig::new().expect("Failed to create config"); let mut builder = ExecutableBuilder::new(&config); 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()); } - + #[test] fn test_format_module_bytes() { let config = AotConfig::new().expect("Failed to create config"); let builder = ExecutableBuilder::new(&config); let test_data = vec![0x00, 0x61, 0x73, 0x6d]; - + let formatted = builder.format_module_bytes(&test_data); assert!(formatted.contains("0x00")); assert!(formatted.contains("0x61")); assert!(formatted.contains("0x73")); assert!(formatted.contains("0x6d")); } - + #[test] fn test_cargo_toml_generation() { let config = AotConfig::new().expect("Failed to create config"); let builder = ExecutableBuilder::new(&config); let cargo_toml = builder.generate_cargo_toml(); - + assert!(cargo_toml.contains("nyash-aot-executable")); assert!(cargo_toml.contains("wasmtime")); assert!(cargo_toml.contains("opt-level = 3")); } - + #[test] fn test_runtime_code_generation() { let config = AotConfig::new().expect("Failed to create config"); let builder = ExecutableBuilder::new(&config); 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("0x00")); - assert!(runtime_code.contains("18.0")); // Wasmtime version + assert!(runtime_code.contains("18.0")); // Wasmtime version } -} \ No newline at end of file +} diff --git a/src/backend/aot/mod.rs b/src/backend/aot/mod.rs index 9597897c..38ecd64a 100644 --- a/src/backend/aot/mod.rs +++ b/src/backend/aot/mod.rs @@ -1,17 +1,17 @@ /*! * AOT (Ahead-of-Time) Backend - Phase 9 Implementation - * + * * Provides native executable generation using wasmtime precompilation * for maximum performance and zero JIT startup overhead */ mod compiler; -mod executable; mod config; +mod executable; pub use compiler::AotCompiler; -pub use executable::ExecutableBuilder; pub use config::AotConfig; +pub use executable::ExecutableBuilder; use crate::mir::MirModule; use std::path::Path; @@ -64,53 +64,47 @@ impl AotBackend { pub fn new() -> Result { let config = AotConfig::new()?; let compiler = AotCompiler::new(&config)?; - - Ok(Self { - compiler, - config, - }) + + Ok(Self { compiler, config }) } - + /// Create AOT backend with custom configuration pub fn with_config(config: AotConfig) -> Result { let compiler = AotCompiler::new(&config)?; - - Ok(Self { - compiler, - config, - }) + + Ok(Self { compiler, config }) } - + /// Compile MIR module to standalone native executable pub fn compile_to_executable>( - &mut self, - mir_module: MirModule, - output_path: P + &mut self, + mir_module: MirModule, + output_path: P, ) -> Result<(), AotError> { // For now, just create a .cwasm precompiled module // TODO: Implement full standalone executable generation let cwasm_path = output_path.as_ref().with_extension("cwasm"); self.compile_to_precompiled(mir_module, cwasm_path) } - + /// Compile MIR module to .cwasm precompiled module pub fn compile_to_precompiled>( &mut self, mir_module: MirModule, - output_path: P + output_path: P, ) -> Result<(), AotError> { // Compile MIR to WASM let wasm_bytes = self.compiler.compile_mir_to_wasm(mir_module)?; - + // Precompile WASM to .cwasm let precompiled_module = self.compiler.precompile_wasm(&wasm_bytes)?; - + // Write to file std::fs::write(output_path, precompiled_module)?; - + Ok(()) } - + /// Get performance statistics pub fn get_stats(&self) -> AotStats { self.compiler.get_stats() @@ -136,17 +130,17 @@ pub struct AotStats { mod tests { use super::*; use crate::mir::MirModule; - + #[test] fn test_aot_backend_creation() { let _backend = AotBackend::new(); // Should not panic - basic creation test assert!(true); } - - #[test] + + #[test] fn test_default_config() { let config = AotConfig::new().expect("Failed to create default config"); assert!(config.optimization_level() >= 1); } -} \ No newline at end of file +} diff --git a/src/backend/control_flow.rs b/src/backend/control_flow.rs index db6cb4b0..53c98697 100644 --- a/src/backend/control_flow.rs +++ b/src/backend/control_flow.rs @@ -5,8 +5,8 @@ * Status: Initial skeleton for future extraction from vm.rs */ +use super::vm::VMError; use crate::mir::BasicBlockId; -use super::vm::{VMError}; /// Result of a block step when evaluating a terminator #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -30,4 +30,3 @@ pub fn record_transition( loop_recorder.record_transition(from, to); Ok(()) } - diff --git a/src/backend/cranelift/builder.rs b/src/backend/cranelift/builder.rs index f3cd21df..19d264eb 100644 --- a/src/backend/cranelift/builder.rs +++ b/src/backend/cranelift/builder.rs @@ -7,11 +7,16 @@ #![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; // 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 consts: usize, @@ -23,24 +28,36 @@ pub struct 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 pub fn finish_and_execute(&self) -> Result { - use cranelift_codegen::ir::{Signature, AbiParam, types}; + use cranelift_codegen::ir::{types, AbiParam, Signature}; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; - use cranelift_module::{Module, Linkage}; + use cranelift_module::{Linkage, Module}; // JIT setup let isa_builder = cranelift_native::builder().map_err(|e| e.to_string())?; let flag_builder = cranelift_codegen::settings::builder(); let flags = cranelift_codegen::settings::Flags::new(flag_builder); 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); // Signature ()->i64 let mut sig = Signature::new(module.target_config().default_call_conv); 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(); ctx.func.signature = sig; let mut fbc = FunctionBuilderContext::new(); @@ -53,16 +70,31 @@ impl ClifBuilder { let mut did_return = false; for op in &self.ops { match *op { - RecOp::ConstI64(i) => { 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::ConstI64(i) => { + 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) => { - if vs.len() < 2 { 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)); + if vs.len() < 2 { + 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::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]); did_return = true; } @@ -70,12 +102,18 @@ impl ClifBuilder { } // Ensure function ends with 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.seal_block(entry); 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); let _ = module.finalize_definitions(); let code = module.get_finalized_function(func_id); @@ -87,35 +125,77 @@ impl ClifBuilder { impl IRBuilder for ClifBuilder { fn begin_function(&mut self, _name: &str) {} fn end_function(&mut self) {} - fn prepare_signature_i64(&mut self, _argc: usize, _has_ret: bool) { } - fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) { } - 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_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 prepare_signature_i64(&mut self, _argc: usize, _has_ret: bool) {} + fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) {} + 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_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_branch(&mut self) { 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_typed(&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 prepare_blocks(&mut self, _count: usize) { } - fn switch_to_block(&mut self, _index: usize) { } - fn seal_block(&mut self, _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 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_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_b1_at(&mut self, pos: usize) { self.push_block_param_i64_at(pos); } - 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 ensure_local_i64(&mut self, _index: usize) { } - fn store_local_i64(&mut self, _index: usize) { } - fn load_local_i64(&mut self, _index: usize) { } + fn emit_branch(&mut self) { + 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_typed( + &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 prepare_blocks(&mut self, _count: usize) {} + fn switch_to_block(&mut self, _index: usize) {} + fn seal_block(&mut self, _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 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_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_b1_at(&mut self, pos: usize) { + self.push_block_param_i64_at(pos); + } + 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 ensure_local_i64(&mut self, _index: usize) {} + fn store_local_i64(&mut self, _index: usize) {} + fn load_local_i64(&mut self, _index: usize) {} } diff --git a/src/backend/cranelift/context.rs b/src/backend/cranelift/context.rs index 3933b1f7..1a97e1f4 100644 --- a/src/backend/cranelift/context.rs +++ b/src/backend/cranelift/context.rs @@ -2,10 +2,10 @@ 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_jit::{JITBuilder, JITModule}; -use cranelift_module::{Module, Linkage, FuncId}; +use cranelift_module::{FuncId, Linkage, Module}; use crate::mir::{BasicBlockId, MirFunction, ValueId}; @@ -13,9 +13,15 @@ use crate::mir::{BasicBlockId, MirFunction, ValueId}; pub struct BlockMap(pub HashMap); impl BlockMap { - pub fn new() -> Self { Self(HashMap::new()) } - 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); } + pub fn new() -> Self { + Self(HashMap::new()) + } + 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 pub fn create_for_function(func: &MirFunction, builder: &mut FunctionBuilder) -> Self { let mut m = HashMap::new(); @@ -33,15 +39,31 @@ pub struct ValueEnv { } impl ValueEnv { - pub fn new() -> Self { Self { vals: HashMap::new(), mem: HashMap::new() } } - pub fn get_val(&self, id: &ValueId) -> Result { - self.vals.get(id).cloned().ok_or_else(|| format!("undef {:?}", id)) + pub fn new() -> Self { + Self { + 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 { + 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) } - 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) @@ -56,24 +78,37 @@ impl ClifContext { let flags = cranelift_codegen::settings::Flags::new(flag_builder); let isa = isa_builder.finish(flags).map_err(|e| e.to_string())?; 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 - 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); 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(); ctx.func.signature = sig.clone(); Ok((func_id, ctx, sig)) } - pub fn finalize(&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())?; + pub fn finalize( + &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); let _ = self.module.finalize_definitions(); Ok(self.module.get_finalized_function(func_id)) } } - diff --git a/src/backend/cranelift/jit.rs b/src/backend/cranelift/jit.rs index ff9d9ba6..7b84641b 100644 --- a/src/backend/cranelift/jit.rs +++ b/src/backend/cranelift/jit.rs @@ -1,12 +1,15 @@ #![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_frontend::{FunctionBuilder, FunctionBuilderContext}; 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. /// 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 { // Signature: () -> i64 let mut sig = Signature::new(module.target_config().default_call_conv); - sig.returns.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())?; + sig.returns + .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(); ctx.func.signature = sig; @@ -36,7 +42,9 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result { let mut vals: HashMap = HashMap::new(); let mut slots: HashMap = 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 let entry = *clif_blocks.get(&main.entry_block).unwrap(); builder.switch_to_block(entry); @@ -55,9 +63,16 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result { MirInstruction::Const { dst, value } => { let v = match value { ConstValue::Integer(i) => builder.ins().iconst(types::I64, *i), - ConstValue::Bool(b) => builder.ins().iconst(types::I64, if *b { 1 } else { 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), + ConstValue::Bool(b) => { + builder.ins().iconst(types::I64, if *b { 1 } else { 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); } @@ -78,7 +93,14 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result { MirInstruction::Compare { dst, op, lhs, rhs } => { let l = *vals.get(lhs).ok_or_else(|| format!("undef {:?}", lhs))?; 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 one = builder.ins().iconst(types::I64, 1); let zero = builder.ins().iconst(types::I64, 0); @@ -94,8 +116,15 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result { } } MirInstruction::Store { value, ptr } => { - let v = *vals.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))); + let v = *vals + .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); } MirInstruction::Copy { dst, src } => { @@ -108,21 +137,32 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result { // Terminator match &bb.terminator { 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]); } Some(MirInstruction::Jump { target }) => { let t = *clif_blocks.get(target).unwrap(); builder.ins().jump(t, &[]); } - Some(MirInstruction::Branch { condition, then_bb, else_bb }) => { - let cond_i64 = *vals.get(condition).unwrap_or(&builder.ins().iconst(types::I64, 0)); + Some(MirInstruction::Branch { + 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 tb = *clif_blocks.get(then_bb).unwrap(); let eb = *clif_blocks.get(else_bb).unwrap(); 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); builder.ins().return_(&[z]); } @@ -132,7 +172,9 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result { 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); let _ = module.finalize_definitions(); diff --git a/src/backend/cranelift/mod.rs b/src/backend/cranelift/mod.rs index c91337e5..d9799371 100644 --- a/src/backend/cranelift/mod.rs +++ b/src/backend/cranelift/mod.rs @@ -6,31 +6,43 @@ #![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")] use crate::semantics::Semantics; -use crate::box_trait::{NyashBox, IntegerBox, BoolBox, StringBox, VoidBox}; 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; // Clif IRBuilder implementation (skeleton) +pub mod builder; +pub mod context; // Context/Block/Value env wrappers // Clif IRBuilder implementation (skeleton) pub mod lower {} pub mod jit; // JIT compile/execute using Cranelift (minimal) pub mod object {} /// JIT: compile and execute a MIR module (skeleton) -pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result, String> { +pub fn compile_and_execute( + mir_module: &MirModule, + _temp_name: &str, +) -> Result, String> { // 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 カバレッジ確認 if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") { let mut builder = NoopBuilder::new(); let mut lower = LowerCore::new(); 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 -> 実行) @@ -41,10 +53,13 @@ pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result = match vmv { 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::String(s) => Box::new(StringBox::new(&s)), crate::backend::vm::VMValue::BoxRef(b) => b.share_box(), @@ -63,19 +78,30 @@ pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result = None; 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 for inst in &bb.instructions { if let MirInstruction::Phi { dst, inputs } = inst { 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); } } - } else if let Some((_, v)) = inputs.first() { if let Some(val) = regs.get(v).cloned() { regs.insert(*dst, val); } } + if let Some((_, v)) = inputs.iter().find(|(b, _)| *b == pred) { + 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(); - for inst in &bb.instructions { - match inst { - MirInstruction::Const { dst, value } => { + let mut sem = ClifSemanticsSkeleton::new(); + for inst in &bb.instructions { + match inst { + MirInstruction::Const { dst, value } => { let vv = match value { ConstValue::Integer(i) => sem.const_i64(*i), ConstValue::Float(f) => sem.const_f64(*f), @@ -84,39 +110,87 @@ pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result sem.const_null(), }; regs.insert(*dst, vv); - } - MirInstruction::BinOp { dst, op, lhs, rhs } if matches!(op, BinaryOp::Add) => { - use crate::backend::vm::VMValue as V; - let a = regs.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); - regs.insert(*dst, out); - } - MirInstruction::Copy { dst, src } => { - 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::BinOp { dst, op, lhs, rhs } if matches!(op, BinaryOp::Add) => { + use crate::backend::vm::VMValue as V; + let a = regs + .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); + regs.insert(*dst, out); + } + MirInstruction::Copy { dst, src } => { + 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 { .. } => { // 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; match (iface_name.as_str(), method_name.as_str()) { ("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") => { - 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 } ("env.box", "new") => { if let Some(d) = dst { if let Some(a0) = args.get(0) { 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 - let mut ny_args: Vec> = Vec::new(); + let mut ny_args: Vec> = + Vec::new(); 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) { regs.insert(*d, V::from_nyash_box(b)); @@ -151,11 +225,25 @@ pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result { last_pred = Some(bb.id); cur = *target; } - Some(MirInstruction::Branch { condition, then_bb, else_bb }) => { + Some(MirInstruction::Jump { target }) => { + last_pred = Some(bb.id); + cur = *target; + } + Some(MirInstruction::Branch { + condition, + then_bb, + else_bb, + }) => { // Minimal: integer/bool truthiness - let c = regs.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 }; + let c = regs + .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); cur = if t { *then_bb } else { *else_bb }; } diff --git a/src/backend/dispatch.rs b/src/backend/dispatch.rs index 7636efee..3c40b25f 100644 --- a/src/backend/dispatch.rs +++ b/src/backend/dispatch.rs @@ -5,15 +5,19 @@ * 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 crate::mir::CompareOp; 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. /// 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 { +pub(super) fn execute_instruction( + vm: &mut VM, + instruction: &MirInstruction, + debug_global: bool, +) -> Result { match instruction { // Basic operations 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); } vm.execute_binop(*dst, op, *lhs, *rhs) - }, + } MirInstruction::UnaryOp { dst, op, operand } => vm.execute_unaryop(*dst, op, *operand), MirInstruction::Compare { dst, op, lhs, rhs } => { - let debug_cmp = 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); } + let debug_cmp = + 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 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 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); } - let li = lb.as_any().downcast_ref::().map(|x| x.value) + 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 + ); + } + let li = lb + .as_any() + .downcast_ref::() + .map(|x| x.value) .or_else(|| lb.to_string_box().value.trim().parse::().ok()); - let ri = rb.as_any().downcast_ref::().map(|x| x.value) + let ri = rb + .as_any() + .downcast_ref::() + .map(|x| x.value) .or_else(|| rb.to_string_box().value.trim().parse::().ok()); 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)); return Ok(ControlFlow::Continue); } } } vm.execute_compare(*dst, op, *lhs, *rhs) - }, + } // I/O operations MirInstruction::Print { value, .. } => vm.execute_print(*value), @@ -57,12 +90,20 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb // Control flow MirInstruction::Return { value } => { 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) - }, + } 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), // 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), // Complex operations - MirInstruction::Call { dst, func, 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), + MirInstruction::Call { + dst, + func, + 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 - MirInstruction::ArrayGet { dst, array, index } => vm.execute_array_get(*dst, *array, *index), - MirInstruction::ArraySet { array, index, value } => vm.execute_array_set(*array, *index, *value), + MirInstruction::ArrayGet { dst, array, index } => { + vm.execute_array_get(*dst, *array, *index) + } + MirInstruction::ArraySet { + array, + index, + value, + } => vm.execute_array_set(*array, *index, *value), // Reference operations 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::RefSet { reference, field, value } => vm.execute_ref_set(*reference, field, *value), + MirInstruction::RefGet { + dst, + reference, + field, + } => vm.execute_ref_get(*dst, *reference, field), + MirInstruction::RefSet { + reference, + field, + value, + } => vm.execute_ref_set(*reference, field, *value), // Weak references 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(); 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) } 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() { let (func, bb, pc) = vm.gc_site_info(); 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 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 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()); Ok(ControlFlow::Continue) } else { - Err(VMError::TypeError(format!("Expected Future, got {:?}", future_val))) + Err(VMError::TypeError(format!( + "Expected Future, got {:?}", + future_val + ))) } } // Special 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::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::ExternCall { + dst, + 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::Nop => Ok(ControlFlow::Continue), MirInstruction::Safepoint => { @@ -159,9 +266,11 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb } vm.runtime.gc.safepoint(); // 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) - }, + } } } @@ -172,11 +281,17 @@ pub struct DispatchEntry; pub struct DispatchTable; impl DispatchTable { - pub fn new() -> Self { Self } + pub fn new() -> Self { + Self + } /// Example API for future use: resolve a handler for an instruction - pub fn resolve(&self, _instr: &MirInstruction) -> Option { None } + pub fn resolve(&self, _instr: &MirInstruction) -> Option { + None + } } /// Example execution of a dispatch entry -pub fn execute_entry(_entry: &DispatchEntry) -> Result<(), VMError> { Ok(()) } +pub fn execute_entry(_entry: &DispatchEntry) -> Result<(), VMError> { + Ok(()) +} diff --git a/src/backend/frame.rs b/src/backend/frame.rs index 29cff799..9f7f5989 100644 --- a/src/backend/frame.rs +++ b/src/backend/frame.rs @@ -15,7 +15,16 @@ pub struct ExecutionFrame { } impl ExecutionFrame { - pub fn new() -> Self { 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; } + pub fn new() -> Self { + 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; + } } - diff --git a/src/backend/gc_helpers.rs b/src/backend/gc_helpers.rs index 88314b6d..468b9357 100644 --- a/src/backend/gc_helpers.rs +++ b/src/backend/gc_helpers.rs @@ -12,10 +12,16 @@ pub fn is_mutating_builtin_call(recv: &VMValue, method: &str) -> bool { match recv { VMValue::BoxRef(b) => { - if b.as_any().downcast_ref::().is_some() { + if b.as_any() + .downcast_ref::() + .is_some() + { return ARRAY_METHODS.iter().any(|m| *m == method); } - if b.as_any().downcast_ref::().is_some() { + if b.as_any() + .downcast_ref::() + .is_some() + { return MAP_METHODS.iter().any(|m| *m == method); } 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) { let trace = crate::config::env::gc_trace(); 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 { eprintln!("[GC] barrier: Write @{}", site); } @@ -38,10 +48,16 @@ pub fn gc_write_barrier_site(runtime: &crate::runtime::NyashRuntime, site: &str) match (before, after) { (Some((_, _, bw)), Some((_, _, aw))) if aw > bw => {} (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 + ); } } } diff --git a/src/backend/llvm/mod.rs b/src/backend/llvm/mod.rs index 0c4b28fd..297a796f 100644 --- a/src/backend/llvm/mod.rs +++ b/src/backend/llvm/mod.rs @@ -13,4 +13,3 @@ pub mod compiler { pub mod box_types { pub use crate::backend::llvm_legacy::box_types::*; } - diff --git a/src/backend/llvm_legacy/box_types.rs b/src/backend/llvm_legacy/box_types.rs index b34eeda9..99b92993 100644 --- a/src/backend/llvm_legacy/box_types.rs +++ b/src/backend/llvm_legacy/box_types.rs @@ -3,4 +3,3 @@ pub fn load_box_type_ids() -> std::collections::HashMap { std::collections::HashMap::new() } - diff --git a/src/backend/llvm_legacy/compiler/aot.rs b/src/backend/llvm_legacy/compiler/aot.rs index 5ca8b337..66f7b814 100644 --- a/src/backend/llvm_legacy/compiler/aot.rs +++ b/src/backend/llvm_legacy/compiler/aot.rs @@ -1,2 +1 @@ // legacy aot placeholder; full implementation retained in archived branch or prior history - diff --git a/src/backend/llvm_legacy/compiler/codegen.rs b/src/backend/llvm_legacy/compiler/codegen.rs new file mode 100644 index 00000000..2914197d --- /dev/null +++ b/src/backend/llvm_legacy/compiler/codegen.rs @@ -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. +} diff --git a/src/backend/llvm_legacy/compiler/helpers.rs b/src/backend/llvm_legacy/compiler/helpers.rs index e477633f..e6e4f554 100644 --- a/src/backend/llvm_legacy/compiler/helpers.rs +++ b/src/backend/llvm_legacy/compiler/helpers.rs @@ -1,2 +1 @@ // legacy helpers placeholder; kept to satisfy module structure after move - diff --git a/src/backend/llvm_legacy/compiler/interpreter.rs b/src/backend/llvm_legacy/compiler/interpreter.rs new file mode 100644 index 00000000..1693b797 --- /dev/null +++ b/src/backend/llvm_legacy/compiler/interpreter.rs @@ -0,0 +1,6 @@ +//! Legacy LLVM interpreter placeholder. + +#[allow(dead_code)] +pub(crate) fn execute() { + // Stub implementation. +} diff --git a/src/backend/llvm_legacy/compiler/mock.rs b/src/backend/llvm_legacy/compiler/mock.rs index 00c413b9..79ab4ce1 100644 --- a/src/backend/llvm_legacy/compiler/mock.rs +++ b/src/backend/llvm_legacy/compiler/mock.rs @@ -1,5 +1,5 @@ +use crate::box_trait::{IntegerBox, NyashBox}; use crate::mir::function::MirModule; -use crate::box_trait::{NyashBox, IntegerBox}; use std::collections::HashMap; pub struct LLVMCompiler { @@ -8,7 +8,9 @@ pub struct LLVMCompiler { impl LLVMCompiler { pub fn new() -> Result { - Ok(Self { values: HashMap::new() }) + Ok(Self { + values: HashMap::new(), + }) } pub fn compile_module(&self, _mir: &MirModule, _out: &str) -> Result<(), String> { @@ -16,8 +18,11 @@ impl LLVMCompiler { Ok(()) } - pub fn compile_and_execute(&mut self, _mir: &MirModule, _out: &str) -> Result, String> { + pub fn compile_and_execute( + &mut self, + _mir: &MirModule, + _out: &str, + ) -> Result, String> { Ok(Box::new(IntegerBox::new(0))) } } - diff --git a/src/backend/llvm_legacy/compiler/mod.rs b/src/backend/llvm_legacy/compiler/mod.rs index a55fd819..ec24451a 100644 --- a/src/backend/llvm_legacy/compiler/mod.rs +++ b/src/backend/llvm_legacy/compiler/mod.rs @@ -31,4 +31,3 @@ mod tests { assert!(true); } } - diff --git a/src/backend/llvm_legacy/context.rs b/src/backend/llvm_legacy/context.rs index c2866d22..66fad8e0 100644 --- a/src/backend/llvm_legacy/context.rs +++ b/src/backend/llvm_legacy/context.rs @@ -11,19 +11,21 @@ pub struct CodegenContext { #[cfg(not(feature = "llvm-inkwell-legacy"))] impl CodegenContext { pub fn new(_module_name: &str) -> Result { - Ok(Self { _phantom: std::marker::PhantomData }) + Ok(Self { + _phantom: std::marker::PhantomData, + }) } } // Real implementation (compiled only when feature "llvm-inkwell-legacy" is enabled) #[cfg(feature = "llvm-inkwell-legacy")] +use inkwell::builder::Builder; +#[cfg(feature = "llvm-inkwell-legacy")] use inkwell::context::Context; #[cfg(feature = "llvm-inkwell-legacy")] use inkwell::module::Module; #[cfg(feature = "llvm-inkwell-legacy")] -use inkwell::builder::Builder; -#[cfg(feature = "llvm-inkwell-legacy")] -use inkwell::targets::{Target, TargetMachine, InitializationConfig}; +use inkwell::targets::{InitializationConfig, Target, TargetMachine}; #[cfg(feature = "llvm-inkwell-legacy")] pub struct CodegenContext<'ctx> { @@ -40,8 +42,8 @@ impl<'ctx> CodegenContext<'ctx> { .map_err(|e| format!("Failed to initialize native target: {}", e))?; let module = context.create_module(module_name); let triple = TargetMachine::get_default_triple(); - let target = Target::from_triple(&triple) - .map_err(|e| format!("Failed to get target: {}", e))?; + let target = + Target::from_triple(&triple).map_err(|e| format!("Failed to get target: {}", e))?; let target_machine = target .create_target_machine( &triple, @@ -53,7 +55,11 @@ impl<'ctx> CodegenContext<'ctx> { ) .ok_or_else(|| "Failed to create target machine".to_string())?; let builder = context.create_builder(); - Ok(Self { context, module, builder, target_machine }) + Ok(Self { + context, + module, + builder, + target_machine, + }) } } - diff --git a/src/backend/llvm_legacy/mod.rs b/src/backend/llvm_legacy/mod.rs index 87902109..aa934672 100644 --- a/src/backend/llvm_legacy/mod.rs +++ b/src/backend/llvm_legacy/mod.rs @@ -34,4 +34,3 @@ mod tests { assert!(true); } } - diff --git a/src/backend/mir_interpreter.rs b/src/backend/mir_interpreter.rs index ee5b7301..2cecf0b5 100644 --- a/src/backend/mir_interpreter.rs +++ b/src/backend/mir_interpreter.rs @@ -8,10 +8,12 @@ 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::mir::{MirModule, MirFunction, MirInstruction, ConstValue, BinaryOp, CompareOp, ValueId, BasicBlockId}; -use crate::backend::abi_util::{to_bool_vm, eq_vm}; +use crate::mir::{ + BasicBlockId, BinaryOp, CompareOp, ConstValue, MirFunction, MirInstruction, MirModule, ValueId, +}; pub struct MirInterpreter { // SSA value table @@ -23,11 +25,20 @@ pub struct 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 pub fn execute_module(&mut self, module: &MirModule) -> Result, 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)?; Ok(ret.to_nyash_box()) } @@ -36,7 +47,10 @@ impl MirInterpreter { let mut cur = func.entry_block; let mut last_pred: Option = None; 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 for inst in &block.instructions { if let MirInstruction::Phi { dst, inputs } = inst { @@ -67,17 +81,34 @@ impl MirInterpreter { }; self.regs.insert(*dst, v); } - MirInstruction::NewBox { dst, box_type, args } => { + MirInstruction::NewBox { + dst, + box_type, + args, + } => { // Build arg boxes let mut a: Vec> = 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) let reg = crate::runtime::unified_registry::get_global_unified_registry(); - let created = reg.lock().unwrap().create_box(box_type, &a) - .map_err(|e| VMError::InvalidInstruction(format!("NewBox {} failed: {}", box_type, e)))?; + let created = + 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)); } - MirInstruction::PluginInvoke { dst, box_val, method, args, .. } => { + MirInstruction::PluginInvoke { + dst, + box_val, + method, + args, + .. + } => { // Resolve receiver let recv = self.reg_load(*box_val)?; let recv_box: Box = match recv.clone() { @@ -85,40 +116,101 @@ impl MirInterpreter { other => other.to_nyash_box(), }; // If PluginBoxV2 → invoke via unified plugin host - if let Some(p) = recv_box.as_any().downcast_ref::() { - let host = crate::runtime::plugin_loader_unified::get_global_plugin_host(); + if let Some(p) = recv_box + .as_any() + .downcast_ref::( + ) { + let host = + crate::runtime::plugin_loader_unified::get_global_plugin_host(); let host = host.read().unwrap(); let mut argv: Vec> = Vec::new(); - for a in args { argv.push(self.reg_load(*a)?.to_nyash_box()); } - match host.invoke_instance_method(&p.box_type, method, p.inner.instance_id, &argv) { + for a in args { + argv.push(self.reg_load(*a)?.to_nyash_box()); + } + 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)); } + 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 { // Minimal fallback: 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 { - 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 if method == "getField" { - if args.len() != 1 { return Err(VMError::InvalidInstruction("getField expects 1 arg".into())); } - 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); } + if args.len() != 1 { + return Err(VMError::InvalidInstruction( + "getField expects 1 arg".into(), + )); + } + 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; } else if method == "setField" { - if args.len() != 2 { 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() }; + if args.len() != 2 { + 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())?; - self.obj_fields.entry(*box_val).or_default().insert(fname, valv); + self.obj_fields + .entry(*box_val) + .or_default() + .insert(fname, valv); continue; } // Fallback: treat like PluginInvoke for plugin-backed boxes @@ -127,7 +219,10 @@ impl MirInterpreter { VMValue::BoxRef(b) => b.share_box(), other => other.to_nyash_box(), }; - if let Some(p) = recv_box.as_any().downcast_ref::() { + if let Some(p) = recv_box + .as_any() + .downcast_ref::( + ) { // Special-case: ConsoleBox.readLine → stdin fallback if not provided by plugin if p.box_type == "ConsoleBox" && method == "readLine" { use std::io::{self, Read}; @@ -136,64 +231,145 @@ impl MirInterpreter { // Read a single line (blocking) let mut buf = [0u8; 1]; while let Ok(n) = stdin.read(&mut buf) { - if n == 0 { break; } + if n == 0 { + break; + } let ch = buf[0] as char; - if ch == '\n' { break; } + if ch == '\n' { + break; + } s.push(ch); - if s.len() > 1_000_000 { break; } + 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; } - 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 mut argv: Vec> = Vec::new(); - for a in args { argv.push(self.reg_load(*a)?.to_nyash_box()); } - 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!("BoxCall {}.{} failed: {:?}", p.box_type, method, e))); } + for a in args { + argv.push(self.reg_load(*a)?.to_nyash_box()); + } + 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!( + "BoxCall {}.{} failed: {:?}", + p.box_type, method, e + ))); + } } } 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 if iface_name == "env.console" && method_name == "log" { if let Some(a0) = args.get(0) { let v = self.reg_load(*a0)?; 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 { - 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)?; - 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 } => { - let v = self.obj_fields.get(reference).and_then(|m| m.get(field)).cloned().unwrap_or(VMValue::Void); + MirInstruction::RefGet { + 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); } 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)?; self.regs.insert(*dst, v); } MirInstruction::UnaryOp { dst, op, operand } => { let x = self.reg_load(*operand)?; 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::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))) }, + 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::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); } 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)?; self.regs.insert(*dst, VMValue::Bool(v)); } @@ -206,7 +382,8 @@ impl MirInterpreter { self.mem.insert(*ptr, v); } 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 } => { let v = self.reg_load(*value)?; @@ -215,38 +392,75 @@ impl MirInterpreter { } } 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 - 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 - other => return Err(VMError::InvalidInstruction(format!("MIR interp: unimplemented instruction: {:?}", other))), + other => { + return Err(VMError::InvalidInstruction(format!( + "MIR interp: unimplemented instruction: {:?}", + other + ))) + } } } // Handle terminator match &block.terminator { 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 }) => { - last_pred = Some(block.id); cur = *target; continue; + last_pred = Some(block.id); + cur = *target; + continue; } - Some(MirInstruction::Branch { condition, 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; + Some(MirInstruction::Branch { + condition, + 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 { - 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 { - use BinaryOp::*; use VMValue::*; + use BinaryOp::*; + use VMValue::*; Ok(match (op, a, b) { (Add, Integer(x), Integer(y)) => Integer(x + y), // String concat: ifいずれかがStringなら文字列連結 @@ -277,12 +491,18 @@ impl MirInterpreter { (Shl, Integer(x), Integer(y)) => Integer(x.wrapping_shl(y as u32)), (Shr, Integer(x), Integer(y)) => Integer(x.wrapping_shr(y as u32)), // 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 { - use CompareOp::*; use VMValue::*; + use CompareOp::*; + use VMValue::*; Ok(match (op, &a, &b) { (Eq, _, _) => eq_vm(&a, &b), (Ne, _, _) => !eq_vm(&a, &b), @@ -294,7 +514,12 @@ impl MirInterpreter { (Le, Float(x), Float(y)) => x <= y, (Gt, 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 + ))) + } }) } } diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 550bcfaa..0ec60ca2 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -3,49 +3,54 @@ */ 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_instructions; +pub mod vm_phi; pub mod vm_stats; +pub mod vm_types; +pub mod vm_values; // Phase 9.78h: VM split scaffolding (control_flow/dispatch/frame) +pub mod abi_util; // Shared ABI/utility helpers pub mod control_flow; pub mod dispatch; pub mod frame; pub mod gc_helpers; +pub mod mir_interpreter; pub mod vm_control_flow; -mod vm_gc; // A3: GC roots & diagnostics 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 -pub mod abi_util; // Shared ABI/utility helpers -pub mod mir_interpreter; // Lightweight MIR interpreter +mod vm_state; // A3: state & basic helpers extracted // Lightweight MIR interpreter -#[cfg(feature = "wasm-backend")] -pub mod wasm; #[cfg(feature = "wasm-backend")] pub mod aot; #[cfg(feature = "wasm-backend")] +pub mod wasm; +#[cfg(feature = "wasm-backend")] pub mod wasm_v2; #[cfg(feature = "llvm-inkwell-legacy")] pub mod llvm_legacy; // Back-compat shim so existing paths crate::backend::llvm::* keep working -#[cfg(feature = "llvm-inkwell-legacy")] -pub mod llvm; #[cfg(feature = "cranelift-jit")] pub mod cranelift; +#[cfg(feature = "llvm-inkwell-legacy")] +pub mod llvm; -pub use vm::{VM, VMError, VMValue}; 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")] 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")] -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, +}; diff --git a/src/backend/vm.rs b/src/backend/vm.rs index 24793a73..9bc3a827 100644 --- a/src/backend/vm.rs +++ b/src/backend/vm.rs @@ -7,14 +7,14 @@ * Typical Callers: runner (VM backend), instruction handlers (vm_instructions) */ -use crate::mir::{ValueId, BasicBlockId, MirModule}; -use std::collections::HashMap; +use crate::mir::{BasicBlockId, MirModule, ValueId}; use crate::runtime::NyashRuntime; use crate::scope_tracker::ScopeTracker; +use std::collections::HashMap; // MirModule is already imported via crate::mir at top +use super::frame::ExecutionFrame; use super::vm_phi::LoopExecutor; use std::time::Instant; -use super::frame::ExecutionFrame; // Phase 9.78a: Import necessary components for unified Box handling // TODO: Re-enable when interpreter refactoring is complete @@ -90,22 +90,21 @@ pub struct 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 /* /// Create a new VM instance with Box registry and declarations pub fn new_with_registry( - box_registry: Arc, + box_registry: Arc, box_declarations: Arc>> ) -> Self { // Implementation pending interpreter refactoring unimplemented!() } - + /// Phase 9.78a: Create VM with plugin support #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] pub fn new_with_plugins( @@ -117,428 +116,411 @@ impl VM { unimplemented!() } */ - - - - - - - - - - - - - - - - // Call a method on a Box - moved to vm_methods.rs (wrapper now in vm_methods) // removed: old inline implementation - /* - // ResultBox (NyashResultBox - new) - if let Some(result_box) = box_value.as_any().downcast_ref::() { - match method { - // Rust側の公開APIメソッド名に合わせたバリアントを許容 - "is_ok" | "isOk" => { - return Ok(result_box.is_ok()); - } - "get_value" | "getValue" => { - return Ok(result_box.get_value()); - } - "get_error" | "getError" => { - return Ok(result_box.get_error()); - } - _ => return Ok(Box::new(VoidBox::new())), + /* + // ResultBox (NyashResultBox - new) + if let Some(result_box) = box_value.as_any().downcast_ref::() { + match method { + // Rust側の公開APIメソッド名に合わせたバリアントを許容 + "is_ok" | "isOk" => { + return Ok(result_box.is_ok()); } - } - - // Legacy box_trait::ResultBox is no longer handled here (migration complete) - - // InstanceBox field access unification: getField/setField - if let Some(instance) = box_value.as_any().downcast_ref::() { - match method { - "getField" => { - if _args.len() != 1 { return Ok(Box::new(StringBox::new("getField(name) requires 1 arg"))); } - let name = _args[0].to_string_box().value; - if let Some(shared) = instance.get_field(&name) { - return Ok(shared.clone_box()); - } - return Ok(Box::new(VoidBox::new())); - } - "setField" => { - if _args.len() != 2 { return Ok(Box::new(StringBox::new("setField(name, value) requires 2 args"))); } - let name = _args[0].to_string_box().value; - let val_arc: crate::box_trait::SharedNyashBox = std::sync::Arc::from(_args[1].clone_or_share()); - let _ = instance.set_field(&name, val_arc); - return Ok(Box::new(VoidBox::new())); - } - _ => {} + "get_value" | "getValue" => { + return Ok(result_box.get_value()); } + "get_error" | "getError" => { + return Ok(result_box.get_error()); + } + _ => return Ok(Box::new(VoidBox::new())), } + } - // JitStatsBox methods (process-local JIT counters) - if let Some(_jsb) = box_value.as_any().downcast_ref::() { - match method { - "toJson" | "toJSON" => { - return Ok(crate::boxes::jit_stats_box::JitStatsBox::new().to_json()); + // Legacy box_trait::ResultBox is no longer handled here (migration complete) + + // InstanceBox field access unification: getField/setField + if let Some(instance) = box_value.as_any().downcast_ref::() { + match method { + "getField" => { + if _args.len() != 1 { return Ok(Box::new(StringBox::new("getField(name) requires 1 arg"))); } + let name = _args[0].to_string_box().value; + if let Some(shared) = instance.get_field(&name) { + return Ok(shared.clone_box()); } - // Return detailed per-function stats as JSON array - // Each item: { name, phi_total, phi_b1, ret_bool_hint, hits, compiled, handle } - "perFunction" | "per_function" => { - if let Some(jm) = &self.jit_manager { - let v = jm.per_function_stats(); - let arr: Vec = v.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 - }) - }).collect(); - let s = serde_json::to_string(&arr).unwrap_or_else(|_| "[]".to_string()); - return Ok(Box::new(crate::box_trait::StringBox::new(s))); - } - return Ok(Box::new(crate::box_trait::StringBox::new("[]"))); - } - "top5" => { - if let Some(jm) = &self.jit_manager { - let v = jm.top_hits(5); - let arr: Vec = v.into_iter().map(|(name, hits, compiled, handle)| { - serde_json::json!({ - "name": name, - "hits": hits, - "compiled": compiled, - "handle": handle - }) - }).collect(); - let s = serde_json::to_string(&arr).unwrap_or_else(|_| "[]".to_string()); - return Ok(Box::new(crate::box_trait::StringBox::new(s))); - } - return Ok(Box::new(crate::box_trait::StringBox::new("[]"))); - } - "summary" => { - let cfg = crate::jit::config::current(); - 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 b1_norm = crate::jit::rt::b1_norm_get(); - let ret_b1 = crate::jit::rt::ret_bool_hint_get(); - let mut payload = serde_json::json!({ - "abi_mode": abi_mode, - "abi_b1_enabled": cfg.native_bool_abi, - "abi_b1_supported": caps.supports_b1_sig, - "b1_norm_count": b1_norm, - "ret_bool_hint_count": ret_b1, - "top5": [] - }); - if let Some(jm) = &self.jit_manager { - let v = jm.top_hits(5); - let top5: Vec = v.into_iter().map(|(name, hits, compiled, handle)| serde_json::json!({ - "name": name, "hits": hits, "compiled": compiled, "handle": handle - })).collect(); - if let Some(obj) = payload.as_object_mut() { obj.insert("top5".to_string(), serde_json::Value::Array(top5)); } - } - let s = serde_json::to_string_pretty(&payload).unwrap_or_else(|_| "{}".to_string()); + return Ok(Box::new(VoidBox::new())); + } + "setField" => { + if _args.len() != 2 { return Ok(Box::new(StringBox::new("setField(name, value) requires 2 args"))); } + let name = _args[0].to_string_box().value; + let val_arc: crate::box_trait::SharedNyashBox = std::sync::Arc::from(_args[1].clone_or_share()); + let _ = instance.set_field(&name, val_arc); + return Ok(Box::new(VoidBox::new())); + } + _ => {} + } + } + + // JitStatsBox methods (process-local JIT counters) + if let Some(_jsb) = box_value.as_any().downcast_ref::() { + match method { + "toJson" | "toJSON" => { + return Ok(crate::boxes::jit_stats_box::JitStatsBox::new().to_json()); + } + // Return detailed per-function stats as JSON array + // Each item: { name, phi_total, phi_b1, ret_bool_hint, hits, compiled, handle } + "perFunction" | "per_function" => { + if let Some(jm) = &self.jit_manager { + let v = jm.per_function_stats(); + let arr: Vec = v.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 + }) + }).collect(); + let s = serde_json::to_string(&arr).unwrap_or_else(|_| "[]".to_string()); return Ok(Box::new(crate::box_trait::StringBox::new(s))); } - _ => return Ok(Box::new(crate::box_trait::VoidBox::new())), + return Ok(Box::new(crate::box_trait::StringBox::new("[]"))); } + "top5" => { + if let Some(jm) = &self.jit_manager { + let v = jm.top_hits(5); + let arr: Vec = v.into_iter().map(|(name, hits, compiled, handle)| { + serde_json::json!({ + "name": name, + "hits": hits, + "compiled": compiled, + "handle": handle + }) + }).collect(); + let s = serde_json::to_string(&arr).unwrap_or_else(|_| "[]".to_string()); + return Ok(Box::new(crate::box_trait::StringBox::new(s))); + } + return Ok(Box::new(crate::box_trait::StringBox::new("[]"))); + } + "summary" => { + let cfg = crate::jit::config::current(); + 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 b1_norm = crate::jit::rt::b1_norm_get(); + let ret_b1 = crate::jit::rt::ret_bool_hint_get(); + let mut payload = serde_json::json!({ + "abi_mode": abi_mode, + "abi_b1_enabled": cfg.native_bool_abi, + "abi_b1_supported": caps.supports_b1_sig, + "b1_norm_count": b1_norm, + "ret_bool_hint_count": ret_b1, + "top5": [] + }); + if let Some(jm) = &self.jit_manager { + let v = jm.top_hits(5); + let top5: Vec = v.into_iter().map(|(name, hits, compiled, handle)| serde_json::json!({ + "name": name, "hits": hits, "compiled": compiled, "handle": handle + })).collect(); + if let Some(obj) = payload.as_object_mut() { obj.insert("top5".to_string(), serde_json::Value::Array(top5)); } + } + let s = serde_json::to_string_pretty(&payload).unwrap_or_else(|_| "{}".to_string()); + return Ok(Box::new(crate::box_trait::StringBox::new(s))); + } + _ => return Ok(Box::new(crate::box_trait::VoidBox::new())), } + } - // StringBox methods - if let Some(string_box) = box_value.as_any().downcast_ref::() { - match method { - "length" | "len" => { - 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(start, end) - simplified implementation - if _args.len() >= 2 { - if let (Some(start_box), Some(end_box)) = (_args.get(0), _args.get(1)) { - if let (Some(start_int), Some(end_int)) = ( - start_box.as_any().downcast_ref::(), - end_box.as_any().downcast_ref::() - ) { - let start = start_int.value.max(0) as usize; - let end = end_int.value.max(0) as usize; - let len = string_box.value.len(); - - if start <= len { - let end_idx = end.min(len); - if start <= end_idx { - let substr = &string_box.value[start..end_idx]; - return Ok(Box::new(StringBox::new(substr))); - } + // StringBox methods + if let Some(string_box) = box_value.as_any().downcast_ref::() { + match method { + "length" | "len" => { + 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(start, end) - simplified implementation + if _args.len() >= 2 { + if let (Some(start_box), Some(end_box)) = (_args.get(0), _args.get(1)) { + if let (Some(start_int), Some(end_int)) = ( + start_box.as_any().downcast_ref::(), + end_box.as_any().downcast_ref::() + ) { + let start = start_int.value.max(0) as usize; + let end = end_int.value.max(0) as usize; + let len = string_box.value.len(); + + if start <= len { + let end_idx = end.min(len); + if start <= end_idx { + let substr = &string_box.value[start..end_idx]; + return Ok(Box::new(StringBox::new(substr))); } } } } - return Ok(Box::new(StringBox::new(""))); // Return empty string on error - }, - "concat" => { - // concat(other) - concatenate with another string - if let Some(other_box) = _args.get(0) { - let other_str = other_box.to_string_box().value; - let result = string_box.value.clone() + &other_str; - return Ok(Box::new(StringBox::new(result))); - } - return Ok(Box::new(StringBox::new(string_box.value.clone()))); - }, - _ => return Ok(Box::new(VoidBox::new())), // Unsupported method - } - } - - // ArrayBox methods (minimal set) - if let Some(array_box) = box_value.as_any().downcast_ref::() { - 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"))); - }, - "pop" => { return Ok(array_box.pop()); }, - "length" | "len" => { 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(StringBox::new(""))); // Return empty string on error + }, + "concat" => { + // concat(other) - concatenate with another string + if let Some(other_box) = _args.get(0) { + let other_str = other_box.to_string_box().value; + let result = string_box.value.clone() + &other_str; + return Ok(Box::new(StringBox::new(result))); + } + return Ok(Box::new(StringBox::new(string_box.value.clone()))); + }, + _ => return Ok(Box::new(VoidBox::new())), // Unsupported method } + } - // MapBox methods (minimal set) - if let Some(map_box) = box_value.as_any().downcast_ref::() { - 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"))); - }, - "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())), - } + // ArrayBox methods (minimal set) + if let Some(array_box) = box_value.as_any().downcast_ref::() { + 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"))); + }, + "pop" => { return Ok(array_box.pop()); }, + "length" | "len" => { 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())), } + } - // MathBox methods (minimal set) - if let Some(math_box) = box_value.as_any().downcast_ref::() { - // Coerce numeric-like StringBox to FloatBox for function-style lowering path - let mut coerce_num = |b: &Box| -> Box { - if let Some(sb) = b.as_any().downcast_ref::() { - let s = sb.value.trim(); - if let Ok(f) = s.parse::() { return Box::new(crate::boxes::FloatBox::new(f)); } - if let Ok(i) = s.parse::() { return Box::new(IntegerBox::new(i)); } - } - b.clone_or_share() - }; - match method { - "min" => { - if _args.len() >= 2 { - let a = coerce_num(&_args[0]); - let b = coerce_num(&_args[1]); - return Ok(math_box.min(a, b)); - } - return Ok(Box::new(StringBox::new("Error: min(a,b) requires 2 args"))); - } - "max" => { - if _args.len() >= 2 { - let a = coerce_num(&_args[0]); - let b = coerce_num(&_args[1]); - return Ok(math_box.max(a, b)); - } - return Ok(Box::new(StringBox::new("Error: max(a,b) requires 2 args"))); - } - "abs" => { - if let Some(v) = _args.get(0) { return Ok(math_box.abs(coerce_num(v))); } - return Ok(Box::new(StringBox::new("Error: abs(x) requires 1 arg"))); - } - "sin" => { - if let Some(v) = _args.get(0) { return Ok(math_box.sin(coerce_num(v))); } - return Ok(Box::new(StringBox::new("Error: sin(x) requires 1 arg"))); - } - "cos" => { - if let Some(v) = _args.get(0) { return Ok(math_box.cos(coerce_num(v))); } - return Ok(Box::new(StringBox::new("Error: cos(x) requires 1 arg"))); - } - _ => return Ok(Box::new(VoidBox::new())), - } + // MapBox methods (minimal set) + if let Some(map_box) = box_value.as_any().downcast_ref::() { + 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"))); + }, + "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())), } + } - // SocketBox methods (minimal set + timeout variants) - if let Some(sock) = box_value.as_any().downcast_ref::() { - 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"))); - }, - "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"))); - }, - "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())), + // MathBox methods (minimal set) + if let Some(math_box) = box_value.as_any().downcast_ref::() { + // Coerce numeric-like StringBox to FloatBox for function-style lowering path + let mut coerce_num = |b: &Box| -> Box { + if let Some(sb) = b.as_any().downcast_ref::() { + let s = sb.value.trim(); + if let Ok(f) = s.parse::() { return Box::new(crate::boxes::FloatBox::new(f)); } + if let Ok(i) = s.parse::() { return Box::new(IntegerBox::new(i)); } } - } - - // IntegerBox methods - if let Some(integer_box) = box_value.as_any().downcast_ref::() { - 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())), // Unsupported method + b.clone_or_share() + }; + match method { + "min" => { + if _args.len() >= 2 { + let a = coerce_num(&_args[0]); + let b = coerce_num(&_args[1]); + return Ok(math_box.min(a, b)); + } + return Ok(Box::new(StringBox::new("Error: min(a,b) requires 2 args"))); } - } - - // BoolBox methods - if let Some(bool_box) = box_value.as_any().downcast_ref::() { - match method { - "toString" => { - return Ok(Box::new(StringBox::new(bool_box.value.to_string()))); - }, - _ => return Ok(Box::new(VoidBox::new())), // Unsupported method + "max" => { + if _args.len() >= 2 { + let a = coerce_num(&_args[0]); + let b = coerce_num(&_args[1]); + return Ok(math_box.max(a, b)); + } + return Ok(Box::new(StringBox::new("Error: max(a,b) requires 2 args"))); } + "abs" => { + if let Some(v) = _args.get(0) { return Ok(math_box.abs(coerce_num(v))); } + return Ok(Box::new(StringBox::new("Error: abs(x) requires 1 arg"))); + } + "sin" => { + if let Some(v) = _args.get(0) { return Ok(math_box.sin(coerce_num(v))); } + return Ok(Box::new(StringBox::new("Error: sin(x) requires 1 arg"))); + } + "cos" => { + if let Some(v) = _args.get(0) { return Ok(math_box.cos(coerce_num(v))); } + return Ok(Box::new(StringBox::new("Error: cos(x) requires 1 arg"))); + } + _ => return Ok(Box::new(VoidBox::new())), } - - // ArrayBox methods - needed for kilo editor - if let Some(array_box) = box_value.as_any().downcast_ref::() { - match method { - "length" | "len" => { - let items = array_box.items.read().unwrap(); - return Ok(Box::new(IntegerBox::new(items.len() as i64))); - }, - "get" => { - // get(index) - get element at index - if let Some(index_box) = _args.get(0) { - if let Some(index_int) = index_box.as_any().downcast_ref::() { - let items = array_box.items.read().unwrap(); - let index = index_int.value as usize; - if index < items.len() { - return Ok(items[index].clone_box()); - } + } + + // SocketBox methods (minimal set + timeout variants) + if let Some(sock) = box_value.as_any().downcast_ref::() { + 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"))); + }, + "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"))); + }, + "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())), + } + } + + // IntegerBox methods + if let Some(integer_box) = box_value.as_any().downcast_ref::() { + 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())), // Unsupported method + } + } + + // BoolBox methods + if let Some(bool_box) = box_value.as_any().downcast_ref::() { + match method { + "toString" => { + return Ok(Box::new(StringBox::new(bool_box.value.to_string()))); + }, + _ => return Ok(Box::new(VoidBox::new())), // Unsupported method + } + } + + // ArrayBox methods - needed for kilo editor + if let Some(array_box) = box_value.as_any().downcast_ref::() { + match method { + "length" | "len" => { + let items = array_box.items.read().unwrap(); + return Ok(Box::new(IntegerBox::new(items.len() as i64))); + }, + "get" => { + // get(index) - get element at index + if let Some(index_box) = _args.get(0) { + if let Some(index_int) = index_box.as_any().downcast_ref::() { + let items = array_box.items.read().unwrap(); + let index = index_int.value as usize; + if index < items.len() { + return Ok(items[index].clone_box()); } } - return Ok(Box::new(VoidBox::new())); // Return void for out of bounds - }, - "set" => { - // set(index, value) - simplified implementation - // Note: This is a read-only operation in the VM for now - // In a real implementation, we'd need mutable access - return Ok(Box::new(VoidBox::new())); - }, - "push" => { - // push(value) - simplified implementation - // Note: This is a read-only operation in the VM for now - return Ok(Box::new(VoidBox::new())); - }, - "insert" => { - // insert(index, value) - simplified implementation - // Note: This is a read-only operation in the VM for now - return Ok(Box::new(VoidBox::new())); - }, - _ => return Ok(Box::new(VoidBox::new())), // Unsupported method - } + } + return Ok(Box::new(VoidBox::new())); // Return void for out of bounds + }, + "set" => { + // set(index, value) - simplified implementation + // Note: This is a read-only operation in the VM for now + // In a real implementation, we'd need mutable access + return Ok(Box::new(VoidBox::new())); + }, + "push" => { + // push(value) - simplified implementation + // Note: This is a read-only operation in the VM for now + return Ok(Box::new(VoidBox::new())); + }, + "insert" => { + // insert(index, value) - simplified implementation + // Note: This is a read-only operation in the VM for now + return Ok(Box::new(VoidBox::new())); + }, + _ => return Ok(Box::new(VoidBox::new())), // Unsupported method } - - // PluginBoxV2 support - if let Some(plugin_box) = box_value.as_any().downcast_ref::() { - // For toString on plugins, return a descriptive string - if method == "toString" { - return Ok(Box::new(StringBox::new(format!("{}(id={})", plugin_box.box_type, plugin_box.inner.instance_id)))); - } + } - // Name-based fallback: delegate to unified PluginHost to invoke by (box_type, method) - // This preserves existing semantics but actually performs the plugin call instead of no-op. - let host = crate::runtime::get_global_plugin_host(); - if let Ok(h) = host.read() { - // Prepare args as NyashBox list; they are already Box - let nyash_args: Vec> = _args.into_iter().map(|b| b).collect(); - match h.invoke_instance_method(&plugin_box.box_type, method, plugin_box.inner.instance_id, &nyash_args) { - Ok(Some(ret)) => return Ok(ret), - Ok(None) => return Ok(Box::new(VoidBox::new())), - Err(e) => { - eprintln!("[VM] Plugin invoke error: {}.{} -> {}", plugin_box.box_type, method, e.message()); - return Ok(Box::new(VoidBox::new())); - } + // PluginBoxV2 support + if let Some(plugin_box) = box_value.as_any().downcast_ref::() { + // For toString on plugins, return a descriptive string + if method == "toString" { + return Ok(Box::new(StringBox::new(format!("{}(id={})", plugin_box.box_type, plugin_box.inner.instance_id)))); + } + + // Name-based fallback: delegate to unified PluginHost to invoke by (box_type, method) + // This preserves existing semantics but actually performs the plugin call instead of no-op. + let host = crate::runtime::get_global_plugin_host(); + if let Ok(h) = host.read() { + // Prepare args as NyashBox list; they are already Box + let nyash_args: Vec> = _args.into_iter().map(|b| b).collect(); + match h.invoke_instance_method(&plugin_box.box_type, method, plugin_box.inner.instance_id, &nyash_args) { + Ok(Some(ret)) => return Ok(ret), + Ok(None) => return Ok(Box::new(VoidBox::new())), + Err(e) => { + eprintln!("[VM] Plugin invoke error: {}.{} -> {}", plugin_box.box_type, method, e.message()); + return Ok(Box::new(VoidBox::new())); } } - return Ok(Box::new(VoidBox::new())); } - */ + return Ok(Box::new(VoidBox::new())); + } + */ } /// RAII guard for GC root regions // Root region guard removed in favor of explicit enter/leave to avoid borrow conflicts - pub(super) use crate::backend::vm_control_flow::ControlFlow; impl Default for VM { @@ -550,40 +532,42 @@ impl Default for VM { #[cfg(test)] mod tests { use super::*; - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirType, EffectMask, BasicBlock, BinaryOp}; - use crate::parser::NyashParser; - use crate::runtime::NyashRuntime; + use crate::box_factory::user_defined::UserDefinedBoxFactory; use crate::core::model::BoxDeclaration as CoreBoxDecl; use crate::interpreter::SharedState; - use crate::box_factory::user_defined::UserDefinedBoxFactory; - use std::sync::Arc; + use crate::mir::{ + BasicBlock, BinaryOp, EffectMask, FunctionSignature, MirFunction, MirModule, MirType, + }; + use crate::parser::NyashParser; + use crate::runtime::NyashRuntime; use std::collections::HashMap; - + use std::sync::Arc; + #[test] fn test_basic_vm_execution() { let mut vm = VM::new(); - + // Test constant loading let const_instr = MirInstruction::Const { dst: ValueId(1), value: ConstValue::Integer(42), }; - + let result = vm.execute_instruction(&const_instr); assert!(result.is_ok()); - + let value = vm.get_value(ValueId(1)).unwrap(); assert_eq!(value.as_integer().unwrap(), 42); } - + #[test] fn test_binary_operations() { let mut vm = VM::new(); - + // Load constants vm.set_value(ValueId(1), VMValue::Integer(10)); vm.set_value(ValueId(2), VMValue::Integer(32)); - + // Test addition let add_instr = MirInstruction::BinOp { dst: ValueId(3), @@ -591,10 +575,10 @@ mod tests { lhs: ValueId(1), rhs: ValueId(2), }; - + let result = vm.execute_instruction(&add_instr); assert!(result.is_ok()); - + let value = vm.get_value(ValueId(3)).unwrap(); assert_eq!(value.as_integer().unwrap(), 42); } @@ -603,9 +587,25 @@ mod tests { fn walk(node: &crate::ast::ASTNode, runtime: &NyashRuntime) { match node { 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 { name: name.clone(), fields: fields.clone(), @@ -657,7 +657,9 @@ return new Person("Alice").greet() let mut shared = SharedState::new(); shared.box_declarations = rt.box_declarations.clone(); 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 }; @@ -673,7 +675,9 @@ return new Person("Alice").greet() // Execute with VM 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"); } @@ -700,13 +704,17 @@ c.get() let mut shared = SharedState::new(); shared.box_declarations = rt.box_declarations.clone(); 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 }; let mut compiler = crate::mir::MirCompiler::new(); let compile_result = compiler.compile(ast).expect("mir compile failed"); 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"); } @@ -721,7 +729,9 @@ console.log("ok") let mut compiler = crate::mir::MirCompiler::new(); let compile_result = compiler.compile(ast).expect("mir compile failed"); 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"); } @@ -751,12 +761,33 @@ g.greet() let runtime = { let rt = crate::runtime::NyashRuntime::new(); // 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; fn walk(node: &crate::ast::ASTNode, runtime: &crate::runtime::NyashRuntime) { match node { - crate::ast::ASTNode::Program { statements, .. } => 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::Program { statements, .. } => { + 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 { name: name.clone(), fields: fields.clone(), @@ -771,7 +802,9 @@ g.greet() implements: implements.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 let mut shared = crate::interpreter::SharedState::new(); shared.box_declarations = rt.box_declarations.clone(); - let udf = std::sync::Arc::new(crate::box_factory::user_defined::UserDefinedBoxFactory::new(shared)); - if let Ok(mut reg) = rt.box_registry.lock() { reg.register(udf); } + let udf = std::sync::Arc::new( + crate::box_factory::user_defined::UserDefinedBoxFactory::new(shared), + ); + if let Ok(mut reg) = rt.box_registry.lock() { + reg.register(udf); + } rt }; @@ -791,7 +828,9 @@ g.greet() let mut compiler = crate::mir::MirCompiler::new(); let compile_result = compiler.compile(ast).expect("mir compile failed"); 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"); } @@ -808,7 +847,9 @@ return (new ArrayBox()).type() let mut compiler = crate::mir::MirCompiler::new(); let compile_result = compiler.compile(ast).expect("mir compile failed"); 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"); // 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 compile_result = compiler.compile(ast).expect("mir compile failed"); 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"); } } diff --git a/src/backend/vm_boxcall.rs b/src/backend/vm_boxcall.rs index 7ba21886..33efbb70 100644 --- a/src/backend/vm_boxcall.rs +++ b/src/backend/vm_boxcall.rs @@ -7,60 +7,109 @@ * Typical Callers: vm_instructions::execute_boxcall / VM::call_unified_method */ -use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox}; -use super::vm::{VM, VMError, VMValue}; +use super::vm::{VMError, VMValue, VM}; +use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox}; impl VM { /// Call a method on a Box - simplified version of interpreter method dispatch - pub(super) fn call_box_method_impl(&self, box_value: Box, method: &str, _args: Vec>) -> Result, VMError> { + pub(super) fn call_box_method_impl( + &self, + box_value: Box, + method: &str, + _args: Vec>, + ) -> Result, VMError> { // PluginBoxV2: delegate to unified plugin host (BID-FFI v1) - if let Some(pbox) = box_value.as_any().downcast_ref::() { + if let Some(pbox) = box_value + .as_any() + .downcast_ref::() + { 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 = host.read().unwrap(); - match host.invoke_instance_method(&pbox.box_type, method, pbox.inner.instance_id, &_args) { - Ok(Some(val)) => { return Ok(val); } - Ok(None) => { return Ok(Box::new(crate::box_trait::VoidBox::new())); } + match host.invoke_instance_method( + &pbox.box_type, + 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) => { - 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) - if let Some(math) = box_value.as_any().downcast_ref::() { + if let Some(math) = box_value + .as_any() + .downcast_ref::() + { match method { "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"))); } "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"))); } "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"))); } "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"))); } "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(VoidBox::new())); } + _ => { + return Ok(Box::new(VoidBox::new())); + } } } // ResultBox (NyashResultBox - new) - if let Some(result_box) = box_value.as_any().downcast_ref::() { + if let Some(result_box) = box_value + .as_any() + .downcast_ref::() + { match method { - "is_ok" | "isOk" => { return Ok(result_box.is_ok()); } - "get_value" | "getValue" => { return Ok(result_box.get_value()); } - "get_error" | "getError" => { return Ok(result_box.get_error()); } + "is_ok" | "isOk" => { + return Ok(result_box.is_ok()); + } + "get_value" | "getValue" => { + return Ok(result_box.get_value()); + } + "get_error" | "getError" => { + return Ok(result_box.get_error()); + } _ => return Ok(Box::new(VoidBox::new())), } } @@ -73,10 +122,17 @@ impl VM { } // JitConfigBox methods - if let Some(jcb) = box_value.as_any().downcast_ref::() { + if let Some(jcb) = box_value + .as_any() + .downcast_ref::() + { match method { "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"))); } "set" => { @@ -84,42 +140,73 @@ impl VM { let k = _args[0].to_string_box().value; let v = _args[1].to_string_box().value; 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"))); } - "getThreshold" => { return Ok(jcb.get_threshold()); } + "getThreshold" => { + return Ok(jcb.get_threshold()); + } "setThreshold" => { - if let Some(v) = _args.get(0) { + if let Some(v) = _args.get(0) { let iv = v.to_string_box().value.parse::().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"))); } - "apply" => { return Ok(jcb.apply()); } - "toJson" => { return Ok(jcb.to_json()); } + "apply" => { + return Ok(jcb.apply()); + } + "toJson" => { + return Ok(jcb.to_json()); + } "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"))); } "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"))); } "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"))); } - "summary" => { return Ok(jcb.summary()); } - _ => { return Ok(Box::new(VoidBox::new())); } + "summary" => { + return Ok(jcb.summary()); + } + _ => { + return Ok(Box::new(VoidBox::new())); + } } } // JitPolicyBox methods - if let Some(jpb) = box_value.as_any().downcast_ref::() { + if let Some(jpb) = box_value + .as_any() + .downcast_ref::() + { match method { "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"))); } "set" => { @@ -132,37 +219,61 @@ impl VM { return Ok(Box::new(StringBox::new("set(name, bool) requires 2 args"))); } "setWhitelistCsv" | "set_whitelist_csv" => { - if let Some(s) = _args.get(0) { return Ok(jpb.set_whitelist_csv(&s.to_string_box().value)); } - return Ok(Box::new(StringBox::new("setWhitelistCsv(csv) requires 1 arg"))); + if let Some(s) = _args.get(0) { + 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" => { - if let Some(s) = _args.get(0) { return Ok(jpb.add_whitelist(&s.to_string_box().value)); } - return Ok(Box::new(StringBox::new("addWhitelist(name) requires 1 arg"))); + if let Some(s) = _args.get(0) { + 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" => { - if let Some(s) = _args.get(0) { return Ok(jpb.enable_preset(&s.to_string_box().value)); } - return Ok(Box::new(StringBox::new("enablePreset(name) requires 1 arg"))); + if let Some(s) = _args.get(0) { + 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 - if let Some(jsb) = box_value.as_any().downcast_ref::() { + if let Some(jsb) = box_value + .as_any() + .downcast_ref::() + { match method { - "toJson" => { return Ok(jsb.to_json()); } + "toJson" => { + return Ok(jsb.to_json()); + } "top5" => { if let Some(jm) = &self.jit_manager { let v = jm.top_hits(5); - let arr: Vec = v.into_iter().map(|(name, hits, compiled, handle)| { - serde_json::json!({ - "name": name, - "hits": hits, - "compiled": compiled, - "handle": handle + let arr: Vec = v + .into_iter() + .map(|(name, hits, compiled, handle)| { + serde_json::json!({ + "name": name, + "hits": hits, + "compiled": compiled, + "handle": handle + }) }) - }).collect(); + .collect(); let s = serde_json::to_string(&arr).unwrap_or_else(|_| "[]".to_string()); return Ok(Box::new(StringBox::new(s))); } @@ -171,7 +282,11 @@ impl VM { "summary" => { let cfg = crate::jit::config::current(); 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 ret_b1 = crate::jit::rt::ret_bool_hint_get(); let mut payload = serde_json::json!({ @@ -192,41 +307,66 @@ impl VM { let per_arr: Vec = 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 })).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))); } "perFunction" | "per_function" => { if let Some(jm) = &self.jit_manager { let v = jm.per_function_stats(); - let arr: Vec = v.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, - })).collect(); - let s = serde_json::to_string_pretty(&arr).unwrap_or_else(|_| "[]".to_string()); + let arr: Vec = v + .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, + }) + }) + .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("[]"))); } - _ => { return Ok(Box::new(VoidBox::new())); } + _ => { + return Ok(Box::new(VoidBox::new())); + } } } // StringBox methods if let Some(string_box) = box_value.as_any().downcast_ref::() { match method { - "length" | "len" => { return Ok(Box::new(IntegerBox::new(string_box.value.len() as i64))); } - "toString" => { return Ok(Box::new(StringBox::new(string_box.value.clone()))); } + "length" | "len" => { + return Ok(Box::new(IntegerBox::new(string_box.value.len() as i64))); + } + "toString" => { + return Ok(Box::new(StringBox::new(string_box.value.clone()))); + } "substring" => { if _args.len() >= 2 { - let s = match _args[0].to_string_box().value.parse::() { Ok(v) => v.max(0) as usize, Err(_) => 0 }; - let e = match _args[1].to_string_box().value.parse::() { Ok(v) => v.max(0) as usize, Err(_) => string_box.value.chars().count() }; + let s = match _args[0].to_string_box().value.parse::() { + Ok(v) => v.max(0) as usize, + Err(_) => 0, + }; + let e = match _args[1].to_string_box().value.parse::() { + Ok(v) => v.max(0) as usize, + Err(_) => string_box.value.chars().count(), + }; return Ok(string_box.substring(s, e)); } return Ok(Box::new(VoidBox::new())); @@ -243,115 +383,345 @@ impl VM { } // ArrayBox methods (minimal set) - if let Some(array_box) = box_value.as_any().downcast_ref::() { + if let Some(array_box) = box_value + .as_any() + .downcast_ref::() + { 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"))); } - "pop" => { return Ok(array_box.pop()); }, - "length" | "len" => { 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"))); } + "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", + ))); + } + "pop" => { + return Ok(array_box.pop()); + } + "length" | "len" => { + 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())), } } // MapBox methods (minimal set) - if let Some(map_box) = box_value.as_any().downcast_ref::() { + if let Some(map_box) = box_value + .as_any() + .downcast_ref::() + { 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"))); } - "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()); }, + "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", + ))); + } + "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())), } } // TaskGroupBox methods (scaffold → instance内の所有Futureに対して実行) - if box_value.as_any().downcast_ref::().is_some() { + if box_value + .as_any() + .downcast_ref::() + .is_some() + { let mut owned = box_value; - if let Some(tg) = (&mut *owned).as_any_mut().downcast_mut::() { + if let Some(tg) = (&mut *owned) + .as_any_mut() + .downcast_mut::() + { match method { - "cancelAll" | "cancel_all" => { return Ok(tg.cancelAll()); } + "cancelAll" | "cancel_all" => { + return Ok(tg.cancelAll()); + } "joinAll" | "join_all" => { - let ms = _args.get(0).map(|a| a.to_string_box().value.parse::().unwrap_or(2000)); + let ms = _args + .get(0) + .map(|a| a.to_string_box().value.parse::().unwrap_or(2000)); return Ok(tg.joinAll(ms)); } - _ => { return Ok(Box::new(VoidBox::new())); } + _ => { + return Ok(Box::new(VoidBox::new())); + } } } return Ok(Box::new(VoidBox::new())); } // P2PBox methods (minimal) - if let Some(p2p) = box_value.as_any().downcast_ref::() { + if let Some(p2p) = box_value + .as_any() + .downcast_ref::() + { match method { "send" => { - if _args.len() >= 2 { 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"))); + if _args.len() >= 2 { + 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" => { - if _args.len() >= 2 { 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"))); + if _args.len() >= 2 { + 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" => { - if _args.len() >= 2 { 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"))); + if _args.len() >= 2 { + 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" => { - if _args.len() >= 1 { return Ok(p2p.off(_args[0].clone_or_share())); } - return Ok(Box::new(StringBox::new("Error: off(intent) requires 1 arg"))); + if _args.len() >= 1 { + 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())), } } // SocketBox methods (minimal + timeouts) - if let Some(sock) = box_value.as_any().downcast_ref::() { + if let Some(sock) = box_value + .as_any() + .downcast_ref::() + { 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"))); } - "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"))); } - "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()); }, + "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", + ))); + } + "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", + ))); + } + "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())), } } // IntegerBox methods - if let Some(integer_box) = box_value.as_any().downcast_ref::() { 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::() { + 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 - if let Some(bool_box) = box_value.as_any().downcast_ref::() { 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::() { + 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 Ok(Box::new(VoidBox::new())) } /// 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], stage: &str, result: Option<&VMValue>) { + pub(super) fn debug_log_boxcall( + &self, + recv: &VMValue, + method: &str, + args: &[Box], + stage: &str, + result: Option<&VMValue>, + ) { if std::env::var("NYASH_VM_DEBUG_BOXCALL").ok().as_deref() == Some("1") { let recv_ty = match recv { VMValue::BoxRef(arc) => arc.type_name().to_string(), @@ -373,9 +743,24 @@ impl VM { VMValue::Future(_) => "Future".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 { - 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 + ); } } } diff --git a/src/backend/vm_control_flow.rs b/src/backend/vm_control_flow.rs index bc7a08e6..db223113 100644 --- a/src/backend/vm_control_flow.rs +++ b/src/backend/vm_control_flow.rs @@ -1,5 +1,5 @@ -use crate::mir::BasicBlockId; use crate::backend::vm::VMValue; +use crate::mir::BasicBlockId; /// Control flow result from instruction execution pub(crate) enum ControlFlow { diff --git a/src/backend/vm_exec.rs b/src/backend/vm_exec.rs index 02b7c464..9edb87e9 100644 --- a/src/backend/vm_exec.rs +++ b/src/backend/vm_exec.rs @@ -11,10 +11,10 @@ * Behavior and public APIs are preserved. This is a pure move/refactor. */ -use crate::mir::{MirModule, MirFunction, MirInstruction, BasicBlockId}; -use crate::box_trait::NyashBox; -use super::{vm::VM, vm::VMError, vm::VMValue}; +use super::{vm::VMError, vm::VMValue, vm::VM}; use crate::backend::vm_control_flow::ControlFlow; +use crate::box_trait::NyashBox; +use crate::mir::{BasicBlockId, MirFunction, MirInstruction, MirModule}; impl VM { /// Execute a MIR module @@ -34,12 +34,17 @@ impl VM { if crate::config::env::vm_pic_stats() { 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(); if lvl > 0 { 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 root_regions = self.scope_tracker.root_regions(); @@ -48,8 +53,12 @@ impl VM { "[GC] mock_mark: roots_total={} regions={} object_field_slots={}", roots_total, root_regions, field_slots ); - if lvl >= 2 { self.gc_print_roots_breakdown(); } - if lvl >= 3 { self.gc_print_reachability_depth2(); } + if lvl >= 2 { + self.gc_print_roots_breakdown(); + } + if lvl >= 3 { + self.gc_print_reachability_depth2(); + } } } Ok(result.to_nyash_box()) @@ -97,9 +106,9 @@ impl VM { .module .as_ref() .ok_or_else(|| VMError::InvalidInstruction("No active module".to_string()))?; - let function_ref = module_ref - .get_function(func_name) - .ok_or_else(|| VMError::InvalidInstruction(format!("Function '{}' not found", func_name)))?; + let function_ref = module_ref.get_function(func_name).ok_or_else(|| { + VMError::InvalidInstruction(format!("Function '{}' not found", func_name)) + })?; let function = function_ref.clone(); let saved_values = std::mem::take(&mut self.values); @@ -143,7 +152,11 @@ impl VM { self.current_function = Some(function.signature.name.clone()); if let Some(jm) = &mut self.jit_manager { if let Ok(s) = std::env::var("NYASH_JIT_THRESHOLD") { - if let Ok(t) = s.parse::() { if t > 0 { jm.set_threshold(t); } } + if let Ok(t) = s.parse::() { + if t > 0 { + jm.set_threshold(t); + } + } } jm.record_entry(&function.signature.name); 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") || 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 { self.leave_root_region(); self.scope_tracker.pop_scope(); @@ -285,9 +301,13 @@ impl VM { if should_return.is_none() && next_block.is_none() { if let Some(term) = &block.terminator { match self.execute_instruction(term)? { - ControlFlow::Continue => {}, - ControlFlow::Jump(target) => { next_block = Some(target); }, - ControlFlow::Return(value) => { should_return = Some(value); }, + ControlFlow::Continue => {} + ControlFlow::Jump(target) => { + next_block = Some(target); + } + ControlFlow::Return(value) => { + should_return = Some(value); + } } } } @@ -320,10 +340,16 @@ impl VM { } /// Execute a single instruction - pub(super) fn execute_instruction(&mut self, instruction: &MirInstruction) -> Result { + pub(super) fn execute_instruction( + &mut self, + instruction: &MirInstruction, + ) -> Result { 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"); - if debug_exec { eprintln!("[VM] execute_instruction: {:?}", instruction); } + let debug_exec = + 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); super::dispatch::execute_instruction(self, instruction, debug_global) } diff --git a/src/backend/vm_gc.rs b/src/backend/vm_gc.rs index ea902f13..4242c9f8 100644 --- a/src/backend/vm_gc.rs +++ b/src/backend/vm_gc.rs @@ -6,7 +6,7 @@ * - Debug prints for roots snapshot and shallow reachability */ -use super::vm::{VM, VMValue}; +use super::vm::{VMValue, VM}; impl VM { /// Enter a GC root region and return a guard that leaves on drop @@ -22,11 +22,17 @@ impl VM { } /// 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) pub(super) fn gc_site_info(&self) -> (String, i64, i64) { - let func = self.current_function.as_deref().unwrap_or("").to_string(); + let func = self + .current_function + .as_deref() + .unwrap_or("") + .to_string(); let bb = self.frame.current_block.map(|b| b.0 as i64).unwrap_or(-1); let pc = self.frame.pc as i64; (func, bb, pc) @@ -78,7 +84,10 @@ impl VM { } if let Some(map) = b.as_any().downcast_ref::() { let vals = map.values(); - if let Some(arr2) = vals.as_any().downcast_ref::() { + if let Some(arr2) = vals + .as_any() + .downcast_ref::() + { if let Ok(items) = arr2.items.read() { for item in items.iter() { let tn = item.type_name().to_string(); diff --git a/src/backend/vm_instructions/boxcall.rs b/src/backend/vm_instructions/boxcall.rs index 7cc0f011..346e75a6 100644 --- a/src/backend/vm_instructions/boxcall.rs +++ b/src/backend/vm_instructions/boxcall.rs @@ -1,12 +1,12 @@ -use crate::mir::ValueId; use crate::backend::vm::ControlFlow; -use crate::backend::{VM, VMError, VMValue}; +use crate::backend::{VMError, VMValue, VM}; +use crate::mir::ValueId; impl VM { /// Small helpers to reduce duplication in vtable stub paths. #[inline] fn vmvalue_to_box(val: &VMValue) -> Box { - use crate::box_trait::{StringBox as SBox, IntegerBox as IBox, BoolBox as BBox}; + use crate::box_trait::{BoolBox as BBox, IntegerBox as IBox, StringBox as SBox}; match val { VMValue::Integer(i) => Box::new(IBox::new(*i)), VMValue::String(s) => Box::new(SBox::new(s)), @@ -19,13 +19,26 @@ impl VM { } #[inline] - fn arg_as_box(&mut self, id: crate::mir::ValueId) -> Result, VMError> { + fn arg_as_box( + &mut self, + id: crate::mir::ValueId, + ) -> Result, VMError> { let v = self.get_value(id)?; Ok(Self::vmvalue_to_box(&v)) } #[inline] - fn two_args_as_box(&mut self, a0: crate::mir::ValueId, a1: crate::mir::ValueId) -> Result<(Box, Box), VMError> { + fn two_args_as_box( + &mut self, + a0: crate::mir::ValueId, + a1: crate::mir::ValueId, + ) -> Result< + ( + Box, + Box, + ), + VMError, + > { Ok((self.arg_as_box(a0)?, self.arg_as_box(a1)?)) } @@ -36,28 +49,64 @@ impl VM { } #[inline] - fn arg_to_usize_or(&mut self, id: crate::mir::ValueId, default: usize) -> Result { + fn arg_to_usize_or( + &mut self, + id: crate::mir::ValueId, + default: usize, + ) -> Result { let v = self.get_value(id)?; match v { VMValue::Integer(i) => Ok((i.max(0)) as usize), - VMValue::String(ref s) => Ok(s.trim().parse::().map(|iv| iv.max(0) as usize).unwrap_or(default)), - _ => Ok(v.to_string().trim().parse::().map(|iv| iv.max(0) as usize).unwrap_or(default)), + VMValue::String(ref s) => Ok(s + .trim() + .parse::() + .map(|iv| iv.max(0) as usize) + .unwrap_or(default)), + _ => Ok(v + .to_string() + .trim() + .parse::() + .map(|iv| iv.max(0) as usize) + .unwrap_or(default)), } } /// Execute BoxCall instruction - pub(crate) fn execute_boxcall(&mut self, dst: Option, box_val: ValueId, method: &str, method_id: Option, args: &[ValueId]) -> Result { + pub(crate) fn execute_boxcall( + &mut self, + dst: Option, + box_val: ValueId, + method: &str, + method_id: Option, + args: &[ValueId], + ) -> Result { let recv = self.get_value(box_val)?; // Debug logging if enabled let debug_boxcall = std::env::var("NYASH_VM_DEBUG_BOXCALL").is_ok(); // Super-early fast-path: ArrayBox len/length (avoid competing branches) if let VMValue::BoxRef(arc_box) = &recv { - if arc_box.as_any().downcast_ref::().is_some() { - if method == "len" || method == "length" || (method_id.is_some() && method_id == crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "len")) { - if let Some(arr) = arc_box.as_any().downcast_ref::() { + if arc_box + .as_any() + .downcast_ref::() + .is_some() + { + if method == "len" + || method == "length" + || (method_id.is_some() + && method_id + == crate::mir::slot_registry::resolve_slot_by_type_name( + "ArrayBox", "len", + )) + { + if let Some(arr) = arc_box + .as_any() + .downcast_ref::() + { let out = arr.length(); - 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)); + } return Ok(ControlFlow::Continue); } } @@ -66,7 +115,10 @@ impl VM { // Fast-path: ConsoleBox.readLine — provide safe stdin fallback with EOF→Void if let VMValue::BoxRef(arc_box) = &recv { - if let Some(p) = arc_box.as_any().downcast_ref::() { + if let Some(p) = arc_box + .as_any() + .downcast_ref::() + { if p.box_type == "ConsoleBox" && method == "readLine" { use std::io::Read; let mut s = String::new(); @@ -75,22 +127,34 @@ impl VM { loop { match stdin.read(&mut buf) { Ok(0) => { - if let Some(dst_id) = dst { let nb = crate::boxes::null_box::NullBox::new(); self.set_value(dst_id, VMValue::from_nyash_box(Box::new(nb))); } + if let Some(dst_id) = dst { + let nb = crate::boxes::null_box::NullBox::new(); + self.set_value(dst_id, VMValue::from_nyash_box(Box::new(nb))); + } return Ok(ControlFlow::Continue); } Ok(_) => { let ch = buf[0] as char; - if ch == '\n' { break; } + if ch == '\n' { + break; + } s.push(ch); - if s.len() > 1_000_000 { break; } + if s.len() > 1_000_000 { + break; + } } Err(_) => { - if let Some(dst_id) = dst { let nb = crate::boxes::null_box::NullBox::new(); self.set_value(dst_id, VMValue::from_nyash_box(Box::new(nb))); } + if let Some(dst_id) = dst { + let nb = crate::boxes::null_box::NullBox::new(); + self.set_value(dst_id, VMValue::from_nyash_box(Box::new(nb))); + } return Ok(ControlFlow::Continue); } } } - if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::String(s)); } + if let Some(dst_id) = dst { + self.set_value(dst_id, VMValue::String(s)); + } return Ok(ControlFlow::Continue); } } @@ -98,7 +162,9 @@ impl VM { // VTable-first stub path (optional) if crate::config::env::abi_vtable() { - if let Some(res) = self.try_boxcall_vtable_stub(dst, &recv, method, method_id, args) { return res; } + if let Some(res) = self.try_boxcall_vtable_stub(dst, &recv, method, method_id, args) { + return res; + } } // Record PIC hit (per-receiver-type × method) @@ -108,45 +174,78 @@ impl VM { // Explicit fast-paths if let VMValue::BoxRef(arc_box) = &recv { // ArrayBox get/set/length - if arc_box.as_any().downcast_ref::().is_some() { - let get_slot = crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "get"); - let set_slot = crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "set"); - let len_slot = crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "len"); + if arc_box + .as_any() + .downcast_ref::() + .is_some() + { + let get_slot = + crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "get"); + let set_slot = + crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "set"); + let len_slot = + crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "len"); let is_get = (method_id.is_some() && method_id == get_slot) || method == "get"; let is_set = (method_id.is_some() && method_id == set_slot) || method == "set"; - let is_len = (method_id.is_some() && method_id == len_slot) || method == "len" || method == "length"; + let is_len = (method_id.is_some() && method_id == len_slot) + || method == "len" + || method == "length"; if is_len { - let arr = arc_box.as_any().downcast_ref::().unwrap(); + let arr = arc_box + .as_any() + .downcast_ref::() + .unwrap(); let out = arr.length(); - 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)); + } return Ok(ControlFlow::Continue); } if is_get && args.len() >= 1 { let idx_val = self.get_value(args[0])?; let idx_box = idx_val.to_nyash_box(); - let arr = arc_box.as_any().downcast_ref::().unwrap(); + let arr = arc_box + .as_any() + .downcast_ref::() + .unwrap(); let out = arr.get(idx_box); - 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)); + } return Ok(ControlFlow::Continue); } else if is_set && args.len() >= 2 { - crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "BoxCall.fastpath.Array.set"); + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "BoxCall.fastpath.Array.set", + ); let idx_val = self.get_value(args[0])?; let val_val = self.get_value(args[1])?; let idx_box = idx_val.to_nyash_box(); let val_box = val_val.to_nyash_box(); - let arr = arc_box.as_any().downcast_ref::().unwrap(); + let arr = arc_box + .as_any() + .downcast_ref::() + .unwrap(); let out = arr.set(idx_box, val_box); - 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)); + } return Ok(ControlFlow::Continue); } } // InstanceBox getField/setField by name - if let Some(inst) = arc_box.as_any().downcast_ref::() { + if let Some(inst) = arc_box + .as_any() + .downcast_ref::() + { let is_getf = method == "getField"; let is_setf = method == "setField"; if (is_getf && args.len() >= 1) || (is_setf && args.len() >= 2) { let name_val = self.get_value(args[0])?; - let field_name = match &name_val { VMValue::String(s) => s.clone(), _ => name_val.to_string(), }; + let field_name = match &name_val { + VMValue::String(s) => s.clone(), + _ => name_val.to_string(), + }; if is_getf { let out_opt = inst.get_field_unified(&field_name); let out_vm = match out_opt { @@ -155,17 +254,30 @@ impl VM { crate::value::NyashValue::Float(f) => VMValue::Float(f), crate::value::NyashValue::Bool(b) => VMValue::Bool(b), crate::value::NyashValue::String(s) => VMValue::String(s), - crate::value::NyashValue::Void | crate::value::NyashValue::Null => VMValue::Void, - crate::value::NyashValue::Box(b) => { if let Ok(g) = b.lock() { VMValue::from_nyash_box(g.share_box()) } else { VMValue::Void } } + crate::value::NyashValue::Void | crate::value::NyashValue::Null => { + VMValue::Void + } + crate::value::NyashValue::Box(b) => { + if let Ok(g) = b.lock() { + VMValue::from_nyash_box(g.share_box()) + } else { + VMValue::Void + } + } _ => VMValue::Void, }, None => VMValue::Void, }; - if let Some(dst_id) = dst { self.set_value(dst_id, out_vm); } + if let Some(dst_id) = dst { + self.set_value(dst_id, out_vm); + } return Ok(ControlFlow::Continue); } else { // setField - crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "BoxCall.fastpath.Instance.setField"); + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "BoxCall.fastpath.Instance.setField", + ); let val_vm = self.get_value(args[1])?; let nv_opt = match val_vm.clone() { VMValue::Integer(i) => Some(crate::value::NyashValue::Integer(i)), @@ -175,7 +287,13 @@ impl VM { VMValue::Void => Some(crate::value::NyashValue::Void), _ => None, }; - if let Some(nv) = nv_opt { let _ = inst.set_field_unified(field_name, nv); if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Void); } return Ok(ControlFlow::Continue); } + if let Some(nv) = nv_opt { + let _ = inst.set_field_unified(field_name, nv); + if let Some(dst_id) = dst { + self.set_value(dst_id, VMValue::Void); + } + return Ok(ControlFlow::Continue); + } } } } @@ -187,31 +305,60 @@ impl VM { let mut is_instance = false; let mut is_plugin = false; let mut is_builtin = false; - if let Some(inst) = arc_box.as_any().downcast_ref::() { class_label = Some(inst.class_name.clone()); is_instance = true; } - else if let Some(p) = arc_box.as_any().downcast_ref::() { class_label = Some(p.box_type.clone()); is_plugin = true; } - else { class_label = Some(arc_box.type_name().to_string()); is_builtin = true; } + if let Some(inst) = arc_box + .as_any() + .downcast_ref::() + { + class_label = Some(inst.class_name.clone()); + is_instance = true; + } else if let Some(p) = arc_box + .as_any() + .downcast_ref::() + { + class_label = Some(p.box_type.clone()); + is_plugin = true; + } else { + class_label = Some(arc_box.type_name().to_string()); + is_builtin = true; + } if let Some(label) = class_label { let tm = crate::runtime::type_meta::get_or_create_type_meta(&label); if let Some(th) = tm.get_thunk(mid as usize) { if let Some(target) = th.get_target() { match target { crate::runtime::type_meta::ThunkTarget::MirFunction(func_name) => { - if std::env::var("NYASH_VM_VT_STATS").ok().as_deref() == Some("1") { eprintln!("[VT] hit class={} slot={} -> {}", label, mid, func_name); } + if std::env::var("NYASH_VM_VT_STATS").ok().as_deref() == Some("1") { + eprintln!( + "[VT] hit class={} slot={} -> {}", + label, mid, func_name + ); + } let mut vm_args = Vec::with_capacity(1 + args.len()); vm_args.push(recv.clone()); - for a in args { vm_args.push(self.get_value(*a)?); } + for a in args { + vm_args.push(self.get_value(*a)?); + } let res = self.call_function_by_name(&func_name, vm_args)?; self.record_poly_pic(&pic_key, &recv, &func_name); let threshold = crate::config::env::vm_pic_threshold(); - if self.pic_hits(&pic_key) >= threshold { self.boxcall_pic_funcname.insert(pic_key.clone(), func_name.clone()); } + if self.pic_hits(&pic_key) >= threshold { + self.boxcall_pic_funcname + .insert(pic_key.clone(), func_name.clone()); + } if is_instance { let vkey = self.build_vtable_key(&label, mid, args.len()); - self.boxcall_vtable_funcname.entry(vkey).or_insert(func_name.clone()); + self.boxcall_vtable_funcname + .entry(vkey) + .or_insert(func_name.clone()); + } + if let Some(dst_id) = dst { + self.set_value(dst_id, res); } - if let Some(dst_id) = dst { self.set_value(dst_id, res); } return Ok(ControlFlow::Continue); } - crate::runtime::type_meta::ThunkTarget::PluginInvoke { method_id: mid2 } => { + crate::runtime::type_meta::ThunkTarget::PluginInvoke { + method_id: mid2, + } => { if is_plugin { if let Some(p) = arc_box.as_any().downcast_ref::() { self.enter_root_region(); @@ -253,16 +400,38 @@ impl VM { } } } - crate::runtime::type_meta::ThunkTarget::BuiltinCall { method: ref m } => { + crate::runtime::type_meta::ThunkTarget::BuiltinCall { + method: ref m, + } => { if is_builtin { - let nyash_args: Vec> = args.iter().map(|arg| { let val = self.get_value(*arg)?; Ok(val.to_nyash_box()) }).collect::, VMError>>()?; - if crate::backend::gc_helpers::is_mutating_builtin_call(&recv, m) { crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "BoxCall.builtin"); } - else if m == "setField" { crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "BoxCall.setField"); } + let nyash_args: Vec> = args + .iter() + .map(|arg| { + let val = self.get_value(*arg)?; + Ok(val.to_nyash_box()) + }) + .collect::, VMError>>()?; + if crate::backend::gc_helpers::is_mutating_builtin_call( + &recv, m, + ) { + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "BoxCall.builtin", + ); + } else if m == "setField" { + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "BoxCall.setField", + ); + } let cloned_box = arc_box.share_box(); - self.boxcall_hits_generic = self.boxcall_hits_generic.saturating_add(1); + self.boxcall_hits_generic = + self.boxcall_hits_generic.saturating_add(1); let out = self.call_box_method(cloned_box, m, nyash_args)?; let vm_out = VMValue::from_nyash_box(out); - 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); } } @@ -271,14 +440,21 @@ impl VM { } // Legacy vtable cache for InstanceBox if is_instance { - let inst = arc_box.as_any().downcast_ref::().unwrap(); + let inst = arc_box + .as_any() + .downcast_ref::() + .unwrap(); let vkey = self.build_vtable_key(&inst.class_name, mid, args.len()); if let Some(func_name) = self.boxcall_vtable_funcname.get(&vkey).cloned() { let mut vm_args = Vec::with_capacity(1 + args.len()); vm_args.push(recv.clone()); - for a in args { vm_args.push(self.get_value(*a)?); } + for a in args { + vm_args.push(self.get_value(*a)?); + } let res = self.call_function_by_name(&func_name, vm_args)?; - if let Some(dst_id) = dst { self.set_value(dst_id, res); } + if let Some(dst_id) = dst { + self.set_value(dst_id, res); + } return Ok(ControlFlow::Continue); } } @@ -287,25 +463,41 @@ impl VM { // Poly-PIC direct call to lowered function if present if let VMValue::BoxRef(arc_box) = &recv { - if arc_box.as_any().downcast_ref::().is_some() { + if arc_box + .as_any() + .downcast_ref::() + .is_some() + { if let Some(func_name) = self.try_poly_pic(&pic_key, &recv) { - if crate::config::env::vm_pic_trace() { eprintln!("[PIC] poly hit {}", pic_key); } + if crate::config::env::vm_pic_trace() { + eprintln!("[PIC] poly hit {}", pic_key); + } self.boxcall_hits_poly_pic = self.boxcall_hits_poly_pic.saturating_add(1); let mut vm_args = Vec::with_capacity(1 + args.len()); vm_args.push(recv.clone()); - for a in args { vm_args.push(self.get_value(*a)?); } + for a in args { + vm_args.push(self.get_value(*a)?); + } let res = self.call_function_by_name(&func_name, vm_args)?; - if let Some(dst_id) = dst { self.set_value(dst_id, res); } + if let Some(dst_id) = dst { + self.set_value(dst_id, res); + } return Ok(ControlFlow::Continue); } if let Some(func_name) = self.boxcall_pic_funcname.get(&pic_key).cloned() { - if crate::config::env::vm_pic_trace() { eprintln!("[PIC] mono hit {}", pic_key); } + if crate::config::env::vm_pic_trace() { + eprintln!("[PIC] mono hit {}", pic_key); + } self.boxcall_hits_mono_pic = self.boxcall_hits_mono_pic.saturating_add(1); let mut vm_args = Vec::with_capacity(1 + args.len()); vm_args.push(recv.clone()); - for a in args { vm_args.push(self.get_value(*a)?); } + for a in args { + vm_args.push(self.get_value(*a)?); + } let res = self.call_function_by_name(&func_name, vm_args)?; - if let Some(dst_id) = dst { self.set_value(dst_id, res); } + if let Some(dst_id) = dst { + self.set_value(dst_id, res); + } return Ok(ControlFlow::Continue); } } @@ -313,53 +505,146 @@ impl VM { // Fast universal slots (0..3) if let Some(mid) = method_id { - if let Some(fast_res) = self.try_fast_universal(mid, &recv, args)? { if let Some(dst_id) = dst { self.set_value(dst_id, fast_res); } return Ok(ControlFlow::Continue); } + if let Some(fast_res) = self.try_fast_universal(mid, &recv, args)? { + if let Some(dst_id) = dst { + self.set_value(dst_id, fast_res); + } + return Ok(ControlFlow::Continue); + } } // Generic path: convert args to NyashBox and call - let nyash_args: Vec> = args.iter().map(|arg| { let val = self.get_value(*arg)?; Ok(val.to_nyash_box()) }).collect::, VMError>>()?; + let nyash_args: Vec> = args + .iter() + .map(|arg| { + let val = self.get_value(*arg)?; + Ok(val.to_nyash_box()) + }) + .collect::, VMError>>()?; // PluginBoxV2 fast-path via direct invoke_fn when method_id present if let (Some(mid), VMValue::BoxRef(arc_box)) = (method_id, &recv) { - if let Some(p) = arc_box.as_any().downcast_ref::() { + if let Some(p) = arc_box + .as_any() + .downcast_ref::() + { self.enter_root_region(); - let mut tlv = crate::runtime::plugin_ffi_common::encode_tlv_header(nyash_args.len() as u16); + let mut tlv = + crate::runtime::plugin_ffi_common::encode_tlv_header(nyash_args.len() as u16); let mut enc_failed = false; for a in &nyash_args { - if let Some(buf) = a.as_any().downcast_ref::() { let snapshot = buf.to_vec(); crate::runtime::plugin_ffi_common::encode::bytes(&mut tlv, &snapshot); continue; } - if let Some(s) = a.as_any().downcast_ref::() { crate::runtime::plugin_ffi_common::encode::string(&mut tlv, &s.value); } - else if let Some(i) = a.as_any().downcast_ref::() { if i.value >= i32::MIN as i64 && i.value <= i32::MAX as i64 { crate::runtime::plugin_ffi_common::encode::i32(&mut tlv, i.value as i32); } else { crate::runtime::plugin_ffi_common::encode::i64(&mut tlv, i.value as i64); } } - else if let Some(b) = a.as_any().downcast_ref::() { crate::runtime::plugin_ffi_common::encode::bool(&mut tlv, b.value); } - else if let Some(f) = a.as_any().downcast_ref::() { crate::runtime::plugin_ffi_common::encode::f64(&mut tlv, f.value); } - else if let Some(arr) = a.as_any().downcast_ref::() { + if let Some(buf) = a.as_any().downcast_ref::() + { + let snapshot = buf.to_vec(); + crate::runtime::plugin_ffi_common::encode::bytes(&mut tlv, &snapshot); + continue; + } + if let Some(s) = a.as_any().downcast_ref::() { + crate::runtime::plugin_ffi_common::encode::string(&mut tlv, &s.value); + } else if let Some(i) = + a.as_any().downcast_ref::() + { + if i.value >= i32::MIN as i64 && i.value <= i32::MAX as i64 { + crate::runtime::plugin_ffi_common::encode::i32( + &mut tlv, + i.value as i32, + ); + } else { + crate::runtime::plugin_ffi_common::encode::i64( + &mut tlv, + i.value as i64, + ); + } + } else if let Some(b) = a.as_any().downcast_ref::() { + crate::runtime::plugin_ffi_common::encode::bool(&mut tlv, b.value); + } else if let Some(f) = a + .as_any() + .downcast_ref::() + { + crate::runtime::plugin_ffi_common::encode::f64(&mut tlv, f.value); + } else if let Some(arr) = + a.as_any().downcast_ref::() + { let items = arr.items.read().unwrap(); let mut tmp = Vec::with_capacity(items.len()); let mut ok = true; for item in items.iter() { - if let Some(intb) = item.as_any().downcast_ref::() { if intb.value >= 0 && intb.value <= 255 { tmp.push(intb.value as u8); } else { ok = false; break; } } else { ok = false; break; } + if let Some(intb) = + item.as_any().downcast_ref::() + { + if intb.value >= 0 && intb.value <= 255 { + tmp.push(intb.value as u8); + } else { + ok = false; + break; + } + } else { + ok = false; + break; + } } - if ok { crate::runtime::plugin_ffi_common::encode::bytes(&mut tlv, &tmp); } else { enc_failed = true; break; } - } else if let Some(h) = a.as_any().downcast_ref::() { crate::runtime::plugin_ffi_common::encode::plugin_handle(&mut tlv, h.inner.type_id, h.inner.instance_id); } - else { enc_failed = true; break; } + if ok { + crate::runtime::plugin_ffi_common::encode::bytes(&mut tlv, &tmp); + } else { + enc_failed = true; + break; + } + } else if let Some(h) = a + .as_any() + .downcast_ref::() + { + crate::runtime::plugin_ffi_common::encode::plugin_handle( + &mut tlv, + h.inner.type_id, + h.inner.instance_id, + ); + } else { + enc_failed = true; + break; + } } if !enc_failed { let mut out = vec![0u8; 4096]; let mut out_len: usize = out.len(); - let code = unsafe { (p.inner.invoke_fn)(p.inner.type_id, mid as u32, p.inner.instance_id, tlv.as_ptr(), tlv.len(), out.as_mut_ptr(), &mut out_len) }; + let code = unsafe { + (p.inner.invoke_fn)( + p.inner.type_id, + mid as u32, + p.inner.instance_id, + tlv.as_ptr(), + tlv.len(), + out.as_mut_ptr(), + &mut out_len, + ) + }; if code == 0 { // Record TypeMeta thunk for plugin invoke so next time VT path can hit let tm = crate::runtime::type_meta::get_or_create_type_meta(&p.box_type); tm.set_thunk_plugin_invoke(mid as usize, mid as u16); - let vm_out = if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) { + let vm_out = if let Some((tag, _sz, payload)) = + crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) + { match tag { - 1 => crate::runtime::plugin_ffi_common::decode::bool(payload).map(VMValue::Bool).unwrap_or(VMValue::Void), - 2 => crate::runtime::plugin_ffi_common::decode::i32(payload).map(|v| VMValue::Integer(v as i64)).unwrap_or(VMValue::Void), - 5 => crate::runtime::plugin_ffi_common::decode::f64(payload).map(VMValue::Float).unwrap_or(VMValue::Void), - 6 => VMValue::String(crate::runtime::plugin_ffi_common::decode::string(payload)), + 1 => crate::runtime::plugin_ffi_common::decode::bool(payload) + .map(VMValue::Bool) + .unwrap_or(VMValue::Void), + 2 => crate::runtime::plugin_ffi_common::decode::i32(payload) + .map(|v| VMValue::Integer(v as i64)) + .unwrap_or(VMValue::Void), + 5 => crate::runtime::plugin_ffi_common::decode::f64(payload) + .map(VMValue::Float) + .unwrap_or(VMValue::Void), + 6 => VMValue::String( + crate::runtime::plugin_ffi_common::decode::string(payload), + ), _ => VMValue::Void, } - } else { VMValue::Void }; - if let Some(dst_id) = dst { self.set_value(dst_id, vm_out); } + } else { + VMValue::Void + }; + if let Some(dst_id) = dst { + self.set_value(dst_id, vm_out); + } self.leave_root_region(); return Ok(ControlFlow::Continue); } @@ -368,112 +653,219 @@ impl VM { } } - if debug_boxcall { self.debug_log_boxcall(&recv, method, &nyash_args, "START", None); } + if debug_boxcall { + self.debug_log_boxcall(&recv, method, &nyash_args, "START", None); + } // Call the method based on receiver type let result = match &recv { VMValue::BoxRef(arc_box) => { - if let Some(inst) = arc_box.as_any().downcast_ref::() { - let func_name = format!("{}.{}{}", inst.class_name, method, format!("/{}", args.len())); + if let Some(inst) = arc_box + .as_any() + .downcast_ref::() + { + let func_name = format!( + "{}.{}{}", + inst.class_name, + method, + format!("/{}", args.len()) + ); if let Some(mid) = method_id { - let tm = crate::runtime::type_meta::get_or_create_type_meta(&inst.class_name); + let tm = + crate::runtime::type_meta::get_or_create_type_meta(&inst.class_name); tm.set_thunk_mir_target(mid as usize, func_name.clone()); let vkey = self.build_vtable_key(&inst.class_name, mid, args.len()); - self.boxcall_vtable_funcname.entry(vkey).or_insert(func_name.clone()); + self.boxcall_vtable_funcname + .entry(vkey) + .or_insert(func_name.clone()); } self.record_poly_pic(&pic_key, &recv, &func_name); let threshold = crate::config::env::vm_pic_threshold(); - if self.pic_hits(&pic_key) >= threshold { self.boxcall_pic_funcname.insert(pic_key.clone(), func_name.clone()); } - if debug_boxcall { eprintln!("[BoxCall] InstanceBox -> call {}", func_name); } + if self.pic_hits(&pic_key) >= threshold { + self.boxcall_pic_funcname + .insert(pic_key.clone(), func_name.clone()); + } + if debug_boxcall { + eprintln!("[BoxCall] InstanceBox -> call {}", func_name); + } let mut vm_args = Vec::with_capacity(1 + args.len()); vm_args.push(recv.clone()); - for a in args { vm_args.push(self.get_value(*a)?); } + for a in args { + vm_args.push(self.get_value(*a)?); + } let res = self.call_function_by_name(&func_name, vm_args)?; - return { if let Some(dst_id) = dst { self.set_value(dst_id, res); } Ok(ControlFlow::Continue) }; + return { + if let Some(dst_id) = dst { + self.set_value(dst_id, res); + } + Ok(ControlFlow::Continue) + }; + } + if debug_boxcall { + eprintln!("[BoxCall] Taking BoxRef path for method '{}'", method); } - if debug_boxcall { eprintln!("[BoxCall] Taking BoxRef path for method '{}'", method); } if let Some(mid) = method_id { let label = arc_box.type_name().to_string(); let tm = crate::runtime::type_meta::get_or_create_type_meta(&label); tm.set_thunk_builtin(mid as usize, method.to_string()); } - if crate::backend::gc_helpers::is_mutating_builtin_call(&recv, method) { crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "BoxCall"); } - else if method == "setField" { crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "BoxCall.setField"); } + if crate::backend::gc_helpers::is_mutating_builtin_call(&recv, method) { + crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "BoxCall"); + } else if method == "setField" { + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "BoxCall.setField", + ); + } let cloned_box = arc_box.share_box(); self.call_box_method(cloned_box, method, nyash_args)? } _ => { - if debug_boxcall { eprintln!("[BoxCall] Converting primitive to box for method '{}'", method); } + if debug_boxcall { + eprintln!( + "[BoxCall] Converting primitive to box for method '{}'", + method + ); + } let box_value = recv.to_nyash_box(); self.call_box_method(box_value, method, nyash_args)? } }; let result_val = VMValue::from_nyash_box(result); - if debug_boxcall { self.debug_log_boxcall(&recv, method, &[], "END", Some(&result_val)); } - if let Some(dst_id) = dst { self.set_value(dst_id, result_val); } + if debug_boxcall { + self.debug_log_boxcall(&recv, method, &[], "END", Some(&result_val)); + } + if let Some(dst_id) = dst { + self.set_value(dst_id, result_val); + } Ok(ControlFlow::Continue) } /// Phase 12 Tier-0: vtable-first stub for selected types - pub(super) fn try_boxcall_vtable_stub(&mut self, _dst: Option, _recv: &VMValue, _method: &str, _method_id: Option, _args: &[ValueId]) -> Option> { + pub(super) fn try_boxcall_vtable_stub( + &mut self, + _dst: Option, + _recv: &VMValue, + _method: &str, + _method_id: Option, + _args: &[ValueId], + ) -> Option> { if crate::config::env::vm_vt_trace() { - match _recv { VMValue::BoxRef(b) => eprintln!("[VT] probe recv_ty={} method={} argc={}", b.type_name(), _method, _args.len()), other => eprintln!("[VT] probe recv_prim={:?} method={} argc={}", other, _method, _args.len()), } + match _recv { + VMValue::BoxRef(b) => eprintln!( + "[VT] probe recv_ty={} method={} argc={}", + b.type_name(), + _method, + _args.len() + ), + other => eprintln!( + "[VT] probe recv_prim={:?} method={} argc={}", + other, + _method, + _args.len() + ), + } } // Primitive String fast-path using StringBox slots if let VMValue::String(sv) = _recv { if crate::runtime::type_registry::resolve_typebox_by_name("StringBox").is_some() { - let slot = crate::runtime::type_registry::resolve_slot_by_name("StringBox", _method, _args.len()); + let slot = crate::runtime::type_registry::resolve_slot_by_name( + "StringBox", + _method, + _args.len(), + ); match slot { - Some(300) => { // len + Some(300) => { + // len let out = VMValue::Integer(sv.len() as i64); - if let Some(dst_id) = _dst { self.set_value(dst_id, out); } + if let Some(dst_id) = _dst { + self.set_value(dst_id, out); + } return Some(Ok(ControlFlow::Continue)); } - Some(301) => { // substring + Some(301) => { + // substring if _args.len() >= 2 { - if let (Ok(a0), Ok(a1)) = (self.get_value(_args[0]), self.get_value(_args[1])) { + if let (Ok(a0), Ok(a1)) = + (self.get_value(_args[0]), self.get_value(_args[1])) + { let chars: Vec = sv.chars().collect(); - let start = match a0 { VMValue::Integer(i) => i.max(0) as usize, _ => 0 }; - let end = match a1 { VMValue::Integer(i) => i.max(0) as usize, _ => chars.len() }; - let ss: String = chars[start.min(end)..end.min(chars.len())].iter().collect(); - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::String(ss)); } + let start = match a0 { + VMValue::Integer(i) => i.max(0) as usize, + _ => 0, + }; + let end = match a1 { + VMValue::Integer(i) => i.max(0) as usize, + _ => chars.len(), + }; + let ss: String = + chars[start.min(end)..end.min(chars.len())].iter().collect(); + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::String(ss)); + } return Some(Ok(ControlFlow::Continue)); } } } - Some(302) => { // concat - if let Some(a0) = _args.get(0) { if let Ok(v) = self.get_value(*a0) { - let out = format!("{}{}", sv, v.to_string()); - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::String(out)); } - return Some(Ok(ControlFlow::Continue)); - }} + Some(302) => { + // concat + if let Some(a0) = _args.get(0) { + if let Ok(v) = self.get_value(*a0) { + let out = format!("{}{}", sv, v.to_string()); + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::String(out)); + } + return Some(Ok(ControlFlow::Continue)); + } + } } - Some(303) => { // indexOf - if let Some(a0) = _args.get(0) { if let Ok(v) = self.get_value(*a0) { - let needle = v.to_string(); - let pos = sv.find(&needle).map(|p| p as i64).unwrap_or(-1); - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::Integer(pos)); } - return Some(Ok(ControlFlow::Continue)); - }} + Some(303) => { + // indexOf + if let Some(a0) = _args.get(0) { + if let Ok(v) = self.get_value(*a0) { + let needle = v.to_string(); + let pos = sv.find(&needle).map(|p| p as i64).unwrap_or(-1); + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::Integer(pos)); + } + return Some(Ok(ControlFlow::Continue)); + } + } } - Some(304) => { // replace - if _args.len() >= 2 { if let (Ok(a0), Ok(a1)) = (self.get_value(_args[0]), self.get_value(_args[1])) { - let out = sv.replace(&a0.to_string(), &a1.to_string()); - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::String(out)); } - return Some(Ok(ControlFlow::Continue)); - }} + Some(304) => { + // replace + if _args.len() >= 2 { + if let (Ok(a0), Ok(a1)) = + (self.get_value(_args[0]), self.get_value(_args[1])) + { + let out = sv.replace(&a0.to_string(), &a1.to_string()); + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::String(out)); + } + return Some(Ok(ControlFlow::Continue)); + } + } } - Some(305) => { // trim - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::String(sv.trim().to_string())); } + Some(305) => { + // trim + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::String(sv.trim().to_string())); + } return Some(Ok(ControlFlow::Continue)); } - Some(306) => { // toUpper - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::String(sv.to_uppercase())); } + Some(306) => { + // toUpper + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::String(sv.to_uppercase())); + } return Some(Ok(ControlFlow::Continue)); } - Some(307) => { // toLower - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::String(sv.to_lowercase())); } + Some(307) => { + // toLower + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::String(sv.to_lowercase())); + } return Some(Ok(ControlFlow::Continue)); } _ => {} @@ -482,58 +874,136 @@ impl VM { } if let VMValue::BoxRef(b) = _recv { let ty_name = b.type_name(); - let ty_name_for_reg: std::borrow::Cow<'_, str> = if let Some(p) = b.as_any().downcast_ref::() { std::borrow::Cow::Owned(p.box_type.clone()) } else { std::borrow::Cow::Borrowed(ty_name) }; - if let Some(_tb) = crate::runtime::type_registry::resolve_typebox_by_name(&ty_name_for_reg) { - let slot = crate::runtime::type_registry::resolve_slot_by_name(&ty_name_for_reg, _method, _args.len()); - if let Some(p) = b.as_any().downcast_ref::() { - if crate::config::env::vm_vt_trace() { eprintln!("[VT] plugin recv ty={} method={} arity={}", ty_name, _method, _args.len()); } + let ty_name_for_reg: std::borrow::Cow<'_, str> = if let Some(p) = + b.as_any() + .downcast_ref::() + { + std::borrow::Cow::Owned(p.box_type.clone()) + } else { + std::borrow::Cow::Borrowed(ty_name) + }; + if let Some(_tb) = + crate::runtime::type_registry::resolve_typebox_by_name(&ty_name_for_reg) + { + let slot = crate::runtime::type_registry::resolve_slot_by_name( + &ty_name_for_reg, + _method, + _args.len(), + ); + if let Some(p) = b + .as_any() + .downcast_ref::() + { + if crate::config::env::vm_vt_trace() { + eprintln!( + "[VT] plugin recv ty={} method={} arity={}", + ty_name, + _method, + _args.len() + ); + } let mut nyash_args: Vec> = Vec::with_capacity(_args.len()); - for aid in _args.iter() { if let Ok(v) = self.get_value(*aid) { nyash_args.push(v.to_nyash_box()); } else { nyash_args.push(Box::new(crate::box_trait::VoidBox::new())); } } + for aid in _args.iter() { + if let Ok(v) = self.get_value(*aid) { + nyash_args.push(v.to_nyash_box()); + } else { + nyash_args.push(Box::new(crate::box_trait::VoidBox::new())); + } + } match ty_name { - "MapBox" => { - match slot { - Some(200) | Some(201) => { - let host = crate::runtime::get_global_plugin_host(); - let ro = host.read().unwrap(); - if let Ok(val_opt) = ro.invoke_instance_method("MapBox", _method, p.inner.instance_id, &[]) { - if let Some(out) = val_opt { if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } } - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - return Some(Ok(ControlFlow::Continue)); + "MapBox" => match slot { + Some(200) | Some(201) => { + let host = crate::runtime::get_global_plugin_host(); + let ro = host.read().unwrap(); + if let Ok(val_opt) = ro.invoke_instance_method( + "MapBox", + _method, + p.inner.instance_id, + &[], + ) { + if let Some(out) = val_opt { + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::from_nyash_box(out)); + } } + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + return Some(Ok(ControlFlow::Continue)); } - Some(202) | Some(203) | Some(204) => { - if matches!(slot, Some(204)) { crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Plugin.Map.set"); } - let host = crate::runtime::get_global_plugin_host(); - let ro = host.read().unwrap(); - let mut method_eff = _method; - if (matches!(slot, Some(202)) && _args.len() >= 1) || (matches!(slot, Some(203)) && _args.len() >= 1) { - if let Ok(a0v) = self.get_value(_args[0]) { if matches!(a0v, VMValue::String(_)) { method_eff = if matches!(slot, Some(203)) { "getS" } else { "hasS" }; } } - } - if let Ok(val_opt) = ro.invoke_instance_method("MapBox", method_eff, p.inner.instance_id, &nyash_args) { - if let Some(out) = val_opt { if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } } - else if _dst.is_some() { if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::Void); } } - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - return Some(Ok(ControlFlow::Continue)); - } - } - _ => {} } - } - "ArrayBox" => { - match slot { - Some(100) | Some(101) | Some(102) => { - if matches!(slot, Some(101)) { crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Plugin.Array.set"); } - let host = crate::runtime::get_global_plugin_host(); - let ro = host.read().unwrap(); - if let Ok(val_opt) = ro.invoke_instance_method("ArrayBox", _method, p.inner.instance_id, &nyash_args) { - if let Some(out) = val_opt { if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } } - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - return Some(Ok(ControlFlow::Continue)); + Some(202) | Some(203) | Some(204) => { + if matches!(slot, Some(204)) { + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "VTable.Plugin.Map.set", + ); + } + let host = crate::runtime::get_global_plugin_host(); + let ro = host.read().unwrap(); + let mut method_eff = _method; + if (matches!(slot, Some(202)) && _args.len() >= 1) + || (matches!(slot, Some(203)) && _args.len() >= 1) + { + if let Ok(a0v) = self.get_value(_args[0]) { + if matches!(a0v, VMValue::String(_)) { + method_eff = if matches!(slot, Some(203)) { + "getS" + } else { + "hasS" + }; + } } } - _ => {} + if let Ok(val_opt) = ro.invoke_instance_method( + "MapBox", + method_eff, + p.inner.instance_id, + &nyash_args, + ) { + if let Some(out) = val_opt { + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::from_nyash_box(out)); + } + } else if _dst.is_some() { + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::Void); + } + } + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + return Some(Ok(ControlFlow::Continue)); + } } - } + _ => {} + }, + "ArrayBox" => match slot { + Some(100) | Some(101) | Some(102) => { + if matches!(slot, Some(101)) { + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "VTable.Plugin.Array.set", + ); + } + let host = crate::runtime::get_global_plugin_host(); + let ro = host.read().unwrap(); + if let Ok(val_opt) = ro.invoke_instance_method( + "ArrayBox", + _method, + p.inner.instance_id, + &nyash_args, + ) { + if let Some(out) = val_opt { + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::from_nyash_box(out)); + } + } + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + return Some(Ok(ControlFlow::Continue)); + } + } + _ => {} + }, _ => {} } } @@ -541,73 +1011,124 @@ impl VM { if let Some(map) = b.as_any().downcast_ref::() { if matches!(slot, Some(200)) || matches!(slot, Some(201)) { self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] MapBox.size/len"); } + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] MapBox.size/len"); + } let out = map.size(); - 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)); + } return Some(Ok(ControlFlow::Continue)); } if matches!(slot, Some(202)) { - if let Some(a0) = _args.get(0) { if let Ok(key_box) = self.arg_as_box(*a0) { - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] MapBox.has"); } - let out = map.has(key_box); - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } - return Some(Ok(ControlFlow::Continue)); - }} + if let Some(a0) = _args.get(0) { + if let Ok(key_box) = self.arg_as_box(*a0) { + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] MapBox.has"); + } + let out = map.has(key_box); + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::from_nyash_box(out)); + } + return Some(Ok(ControlFlow::Continue)); + } + } } if matches!(slot, Some(203)) { - if let Some(a0) = _args.get(0) { if let Ok(key_box) = self.arg_as_box(*a0) { - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] MapBox.get"); } - let out = map.get(key_box); - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } - return Some(Ok(ControlFlow::Continue)); - }} + if let Some(a0) = _args.get(0) { + if let Ok(key_box) = self.arg_as_box(*a0) { + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] MapBox.get"); + } + let out = map.get(key_box); + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::from_nyash_box(out)); + } + return Some(Ok(ControlFlow::Continue)); + } + } } if matches!(slot, Some(204)) { if _args.len() >= 2 { - if let (Ok(a0), Ok(a1)) = (self.get_value(_args[0]), self.get_value(_args[1])) { + if let (Ok(a0), Ok(a1)) = + (self.get_value(_args[0]), self.get_value(_args[1])) + { if let VMValue::String(ref s) = a0 { let vb = Self::vmvalue_to_box(&a1); - let out = map.set(Box::new(crate::box_trait::StringBox::new(s)), vb); - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } + let out = + map.set(Box::new(crate::box_trait::StringBox::new(s)), vb); + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::from_nyash_box(out)); + } return Some(Ok(ControlFlow::Continue)); } let key_box = Self::vmvalue_to_box(&a0); let val_box = Self::vmvalue_to_box(&a1); - // Barrier: mutation - crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Map.set"); - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] MapBox.set"); } - let out = map.set(key_box, val_box); - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } - return Some(Ok(ControlFlow::Continue)); + // Barrier: mutation + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "VTable.Map.set", + ); + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] MapBox.set"); + } + let out = map.set(key_box, val_box); + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::from_nyash_box(out)); + } + return Some(Ok(ControlFlow::Continue)); } } } - if matches!(slot, Some(205)) { // delete/remove - if let Some(a0) = _args.get(0) { if let Ok(arg_v) = self.get_value(*a0) { - crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Map.delete"); - let key_box = Self::vmvalue_to_box(&arg_v); - let out = map.delete(key_box); - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } - return Some(Ok(ControlFlow::Continue)); - }} + if matches!(slot, Some(205)) { + // delete/remove + if let Some(a0) = _args.get(0) { + if let Ok(arg_v) = self.get_value(*a0) { + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "VTable.Map.delete", + ); + let key_box = Self::vmvalue_to_box(&arg_v); + let out = map.delete(key_box); + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::from_nyash_box(out)); + } + return Some(Ok(ControlFlow::Continue)); + } + } } - if matches!(slot, Some(206)) { // keys + if matches!(slot, Some(206)) { + // keys let out = map.keys(); - 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)); + } return Some(Ok(ControlFlow::Continue)); } - if matches!(slot, Some(207)) { // values + if matches!(slot, Some(207)) { + // values let out = map.values(); - 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)); + } return Some(Ok(ControlFlow::Continue)); } - if matches!(slot, Some(208)) { // clear - crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Map.clear"); + if matches!(slot, Some(208)) { + // clear + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "VTable.Map.clear", + ); let out = map.clear(); - 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)); + } return Some(Ok(ControlFlow::Continue)); } } @@ -615,20 +1136,32 @@ impl VM { if let Some(sb) = b.as_any().downcast_ref::() { if matches!(slot, Some(300)) { self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] StringBox.len"); } + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] StringBox.len"); + } let out = crate::box_trait::IntegerBox::new(sb.value.len() as i64); - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(Box::new(out))); } + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::from_nyash_box(Box::new(out))); + } return Some(Ok(ControlFlow::Continue)); } // substring(start, end) if matches!(slot, Some(301)) { if _args.len() >= 2 { let full = sb.value.chars().count(); - if let (Ok(start), Ok(end)) = (self.arg_to_usize_or(_args[0], 0), self.arg_to_usize_or(_args[1], full)) { - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] StringBox.substring({}, {})", start, end); } + if let (Ok(start), Ok(end)) = ( + self.arg_to_usize_or(_args[0], 0), + self.arg_to_usize_or(_args[1], full), + ) { + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] StringBox.substring({}, {})", start, end); + } let out = sb.substring(start, end); - 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)); + } return Some(Ok(ControlFlow::Continue)); } } @@ -638,10 +1171,18 @@ impl VM { if let Some(a0_id) = _args.get(0) { if let Ok(a0) = self.get_value(*a0_id) { let other = a0.to_string(); - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] StringBox.concat"); } - let out = crate::box_trait::StringBox::new(format!("{}{}", sb.value, other)); - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(Box::new(out))); } + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] StringBox.concat"); + } + let out = crate::box_trait::StringBox::new(format!( + "{}{}", + sb.value, other + )); + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::from_nyash_box(Box::new(out))); + } return Some(Ok(ControlFlow::Continue)); } } @@ -650,9 +1191,12 @@ impl VM { if matches!(slot, Some(303)) { if let Some(a0_id) = _args.get(0) { if let Ok(needle) = self.arg_to_string(*a0_id) { - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); let out = sb.find(&needle); - 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)); + } return Some(Ok(ControlFlow::Continue)); } } @@ -660,10 +1204,15 @@ impl VM { // replace(old, new) if matches!(slot, Some(304)) { if _args.len() >= 2 { - if let (Ok(old), Ok(newv)) = (self.arg_to_string(_args[0]), self.arg_to_string(_args[1])) { - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); + if let (Ok(old), Ok(newv)) = + (self.arg_to_string(_args[0]), self.arg_to_string(_args[1])) + { + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); let out = sb.replace(&old, &newv); - 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)); + } return Some(Ok(ControlFlow::Continue)); } } @@ -672,38 +1221,79 @@ impl VM { if matches!(slot, Some(305)) { self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); let out = sb.trim(); - 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)); + } return Some(Ok(ControlFlow::Continue)); } // toUpper() if matches!(slot, Some(306)) { self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); let out = sb.to_upper(); - 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)); + } return Some(Ok(ControlFlow::Continue)); } // toLower() if matches!(slot, Some(307)) { self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); let out = sb.to_lower(); - 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)); + } return Some(Ok(ControlFlow::Continue)); } } // ConsoleBox: log/warn/error/clear (400-series) - if let Some(console) = b.as_any().downcast_ref::() { + if let Some(console) = b + .as_any() + .downcast_ref::() + { match slot { - Some(400) => { // log - if let Some(a0) = _args.get(0) { if let Ok(msg) = self.arg_to_string(*a0) { console.log(&msg); if let Some(dst) = _dst { self.set_value(dst, VMValue::Void); } return Some(Ok(ControlFlow::Continue)); } } + Some(400) => { + // log + if let Some(a0) = _args.get(0) { + if let Ok(msg) = self.arg_to_string(*a0) { + console.log(&msg); + if let Some(dst) = _dst { + self.set_value(dst, VMValue::Void); + } + return Some(Ok(ControlFlow::Continue)); + } + } } - Some(401) => { // warn - if let Some(a0) = _args.get(0) { if let Ok(msg) = self.arg_to_string(*a0) { console.warn(&msg); if let Some(dst) = _dst { self.set_value(dst, VMValue::Void); } return Some(Ok(ControlFlow::Continue)); } } + Some(401) => { + // warn + if let Some(a0) = _args.get(0) { + if let Ok(msg) = self.arg_to_string(*a0) { + console.warn(&msg); + if let Some(dst) = _dst { + self.set_value(dst, VMValue::Void); + } + return Some(Ok(ControlFlow::Continue)); + } + } } - Some(402) => { // error - if let Some(a0) = _args.get(0) { if let Ok(msg) = self.arg_to_string(*a0) { console.error(&msg); if let Some(dst) = _dst { self.set_value(dst, VMValue::Void); } return Some(Ok(ControlFlow::Continue)); } } + Some(402) => { + // error + if let Some(a0) = _args.get(0) { + if let Ok(msg) = self.arg_to_string(*a0) { + console.error(&msg); + if let Some(dst) = _dst { + self.set_value(dst, VMValue::Void); + } + return Some(Ok(ControlFlow::Continue)); + } + } } - Some(403) => { // clear - console.clear(); if let Some(dst) = _dst { self.set_value(dst, VMValue::Void); } return Some(Ok(ControlFlow::Continue)); + Some(403) => { + // clear + console.clear(); + if let Some(dst) = _dst { + self.set_value(dst, VMValue::Void); + } + return Some(Ok(ControlFlow::Continue)); } _ => {} } @@ -713,21 +1303,42 @@ impl VM { // len/length if matches!(slot, Some(102)) { self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.len"); } + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.len"); + } let out = arr.length(); - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } else if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.len without dst"); } + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::from_nyash_box(out)); + } else if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.len without dst"); + } return Some(Ok(ControlFlow::Continue)); } // get(index) if matches!(slot, Some(100)) { if let Some(a0_id) = _args.get(0) { if let Ok(idx_box) = self.arg_as_box(*a0_id) { - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.get"); } + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.get"); + } let out = arr.get(idx_box); let vm_out = VMValue::from_nyash_box(out); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.get -> {}", vm_out.to_string()); } - if let Some(dst_id) = _dst { if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.get set dst={}", dst_id.to_usize()); } self.set_value(dst_id, vm_out); } else if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.get without dst"); } + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.get -> {}", vm_out.to_string()); + } + if let Some(dst_id) = _dst { + if crate::config::env::vm_vt_trace() { + eprintln!( + "[VT] ArrayBox.get set dst={}", + dst_id.to_usize() + ); + } + self.set_value(dst_id, vm_out); + } else if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.get without dst"); + } return Some(Ok(ControlFlow::Continue)); } } @@ -735,13 +1346,25 @@ impl VM { // set(index, value) if matches!(slot, Some(101)) { if _args.len() >= 2 { - if let (Ok(idx_box), Ok(val_box)) = (self.arg_as_box(_args[0]), self.arg_as_box(_args[1])) { + if let (Ok(idx_box), Ok(val_box)) = + (self.arg_as_box(_args[0]), self.arg_as_box(_args[1])) + { // Mutation barrier - crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Array.set"); - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.set"); } + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "VTable.Array.set", + ); + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.set"); + } let out = arr.set(idx_box, val_box); - if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } else if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.set without dst"); } + if let Some(dst_id) = _dst { + self.set_value(dst_id, VMValue::from_nyash_box(out)); + } else if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.set without dst"); + } return Some(Ok(ControlFlow::Continue)); } } @@ -750,11 +1373,19 @@ impl VM { if matches!(slot, Some(103)) { if let Some(a0_id) = _args.get(0) { if let Ok(val_box) = self.arg_as_box(*a0_id) { - crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Array.push"); - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.push"); } + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "VTable.Array.push", + ); + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.push"); + } let out = arr.push(val_box); - 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)); + } return Some(Ok(ControlFlow::Continue)); } } @@ -762,28 +1393,44 @@ impl VM { // pop() if matches!(slot, Some(104)) { self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.pop"); } + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.pop"); + } let out = arr.pop(); - 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)); + } return Some(Ok(ControlFlow::Continue)); } // clear() if matches!(slot, Some(105)) { - crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Array.clear"); + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "VTable.Array.clear", + ); self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.clear"); } + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.clear"); + } let out = arr.clear(); - 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)); + } return Some(Ok(ControlFlow::Continue)); } // contains(value) if matches!(slot, Some(106)) { if let Some(a0_id) = _args.get(0) { if let Ok(val_box) = self.arg_as_box(*a0_id) { - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.contains"); } + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.contains"); + } let out = arr.contains(val_box); - 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)); + } return Some(Ok(ControlFlow::Continue)); } } @@ -792,10 +1439,15 @@ impl VM { if matches!(slot, Some(107)) { if let Some(a0_id) = _args.get(0) { if let Ok(val_box) = self.arg_as_box(*a0_id) { - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.indexOf"); } + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.indexOf"); + } let out = arr.indexOf(val_box); - 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)); + } return Some(Ok(ControlFlow::Continue)); } } @@ -804,48 +1456,82 @@ impl VM { if matches!(slot, Some(108)) { if let Some(a0_id) = _args.get(0) { if let Ok(sep_box) = self.arg_as_box(*a0_id) { - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.join"); } + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.join"); + } let out = arr.join(sep_box); - 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)); + } return Some(Ok(ControlFlow::Continue)); } } } // sort() if matches!(slot, Some(109)) { - crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Array.sort"); + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "VTable.Array.sort", + ); self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.sort"); } + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.sort"); + } let out = arr.sort(); - 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)); + } return Some(Ok(ControlFlow::Continue)); } // reverse() if matches!(slot, Some(110)) { - crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Array.reverse"); + crate::backend::gc_helpers::gc_write_barrier_site( + &self.runtime, + "VTable.Array.reverse", + ); self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.reverse"); } + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.reverse"); + } let out = arr.reverse(); - 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)); + } return Some(Ok(ControlFlow::Continue)); } // slice(start, end) if matches!(slot, Some(111)) { if _args.len() >= 2 { - if let (Ok(start_box), Ok(end_box)) = (self.arg_as_box(_args[0]), self.arg_as_box(_args[1])) { - self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1); - if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.slice"); } + if let (Ok(start_box), Ok(end_box)) = + (self.arg_as_box(_args[0]), self.arg_as_box(_args[1])) + { + self.boxcall_hits_vtable = + self.boxcall_hits_vtable.saturating_add(1); + if crate::config::env::vm_vt_trace() { + eprintln!("[VT] ArrayBox.slice"); + } let out = arr.slice(start_box, end_box); - 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)); + } return Some(Ok(ControlFlow::Continue)); } } } } if crate::config::env::abi_strict() { - let known = crate::runtime::type_registry::known_methods_for(ty_name).unwrap_or_default().join(", "); - let msg = format!("ABI_STRICT: undefined vtable method {}.{}(arity={}) — known: {}", ty_name, _method, _args.len(), known); + let known = crate::runtime::type_registry::known_methods_for(ty_name) + .unwrap_or_default() + .join(", "); + let msg = format!( + "ABI_STRICT: undefined vtable method {}.{}(arity={}) — known: {}", + ty_name, + _method, + _args.len(), + known + ); return Some(Err(VMError::TypeError(msg))); } } @@ -856,9 +1542,17 @@ impl VM { impl VM { /// Try fast universal-thunk dispatch using reserved method slots 0..3. - fn try_fast_universal(&mut self, method_id: u16, recv: &VMValue, args: &[ValueId]) -> Result, VMError> { + fn try_fast_universal( + &mut self, + method_id: u16, + recv: &VMValue, + args: &[ValueId], + ) -> Result, VMError> { match method_id { - 0 => { let s = recv.to_string(); return Ok(Some(VMValue::String(s))); } + 0 => { + let s = recv.to_string(); + return Ok(Some(VMValue::String(s))); + } 1 => { let t = match recv { VMValue::Integer(_) => "Integer".to_string(), @@ -872,7 +1566,11 @@ impl VM { return Ok(Some(VMValue::String(t))); } 2 => { - let other = if let Some(arg0) = args.get(0) { self.get_value(*arg0)? } else { VMValue::Void }; + let other = if let Some(arg0) = args.get(0) { + self.get_value(*arg0)? + } else { + VMValue::Void + }; let res = match (recv, &other) { (VMValue::Integer(a), VMValue::Integer(b)) => a == b, (VMValue::Bool(a), VMValue::Bool(b)) => a == b, diff --git a/src/backend/vm_instructions/call.rs b/src/backend/vm_instructions/call.rs index c650f7e9..136fb656 100644 --- a/src/backend/vm_instructions/call.rs +++ b/src/backend/vm_instructions/call.rs @@ -1,41 +1,66 @@ +use crate::backend::vm::ControlFlow; +use crate::backend::{VMError, VMValue, VM}; use crate::box_trait::NyashBox; use crate::mir::ValueId; -use crate::backend::vm::ControlFlow; -use crate::backend::{VM, VMError, VMValue}; impl VM { /// Execute Call instruction (supports function name or FunctionBox value) - pub(crate) fn execute_call(&mut self, dst: Option, func: ValueId, args: &[ValueId]) -> Result { + pub(crate) fn execute_call( + &mut self, + dst: Option, + func: ValueId, + args: &[ValueId], + ) -> Result { // Evaluate function value let fval = self.get_value(func)?; match fval { VMValue::String(func_name) => { // Legacy: call function by name - let arg_values: Vec = args.iter().map(|arg| self.get_value(*arg)).collect::, _>>()?; + let arg_values: Vec = args + .iter() + .map(|arg| self.get_value(*arg)) + .collect::, _>>()?; 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) } VMValue::BoxRef(arc_box) => { // FunctionBox call path - if let Some(fun) = arc_box.as_any().downcast_ref::() { + if let Some(fun) = arc_box + .as_any() + .downcast_ref::() + { // Convert args to NyashBox for interpreter helper - let nyash_args: Vec> = args.iter() + let nyash_args: Vec> = args + .iter() .map(|a| self.get_value(*a).map(|v| v.to_nyash_box())) .collect::, VMError>>()?; // Execute via interpreter helper match crate::interpreter::run_function_box(fun, nyash_args) { 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) } - Err(e) => Err(VMError::InvalidInstruction(format!("FunctionBox call failed: {:?}", e))) + Err(e) => Err(VMError::InvalidInstruction(format!( + "FunctionBox call failed: {:?}", + e + ))), } } 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 + ))), } } } diff --git a/src/backend/vm_instructions/core.rs b/src/backend/vm_instructions/core.rs index fcdf577c..b6506c7f 100644 --- a/src/backend/vm_instructions/core.rs +++ b/src/backend/vm_instructions/core.rs @@ -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::boxes::ArrayBox; +use crate::mir::{ + BasicBlockId, BinaryOp, CompareOp, ConstValue, MirType, TypeOpKind, UnaryOp, ValueId, +}; use std::sync::Arc; -use crate::backend::vm::ControlFlow; -use crate::backend::{VM, VMError, VMValue}; impl VM { // ---- Helpers (PIC/VTable bookkeeping) ---- - pub(super) fn build_pic_key(&self, recv: &VMValue, method: &str, method_id: Option) -> String { + pub(super) fn build_pic_key( + &self, + recv: &VMValue, + method: &str, + method_id: Option, + ) -> String { let label = self.cache_label_for_recv(recv); 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) { use std::collections::hash_map::Entry; @@ -19,23 +30,43 @@ impl VM { let v = e.get_mut(); *v = v.saturating_add(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 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 { let label = self.cache_label_for_recv(recv); let ver = self.cache_version_for_label(&label); 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); entries.push(entry.clone()); return Some(entry.2); @@ -50,15 +81,28 @@ impl VM { match self.boxcall_poly_pic.entry(pic_site_key.to_string()) { Entry::Occupied(mut e) => { let v = e.get_mut(); - if let Some(idx) = v.iter().position(|(l, vv, _)| *l == label && *vv == ver) { v.remove(idx); } - if v.len() >= 4 { v.remove(0); } + if let Some(idx) = v.iter().position(|(l, vv, _)| *l == label && *vv == ver) { + v.remove(idx); + } + if v.len() >= 4 { + v.remove(0); + } 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 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()), } } - 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)] - 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 ---- - pub(crate) fn execute_const(&mut self, dst: ValueId, value: &ConstValue) -> Result { + pub(crate) fn execute_const( + &mut self, + dst: ValueId, + value: &ConstValue, + ) -> Result { let vm_value = VMValue::from(value); self.set_value(dst, vm_value); Ok(ControlFlow::Continue) } - pub(crate) fn execute_binop(&mut self, dst: ValueId, op: &BinaryOp, lhs: ValueId, rhs: ValueId) -> Result { + pub(crate) fn execute_binop( + &mut self, + dst: ValueId, + op: &BinaryOp, + lhs: ValueId, + rhs: ValueId, + ) -> Result { match *op { 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 right = self.get_value(rhs)?; let lb = left.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)); Ok(ControlFlow::Continue) } @@ -104,29 +168,63 @@ impl VM { } } } - pub(crate) fn execute_unaryop(&mut self, dst: ValueId, op: &UnaryOp, operand: ValueId) -> Result { + pub(crate) fn execute_unaryop( + &mut self, + dst: ValueId, + op: &UnaryOp, + operand: ValueId, + ) -> Result { let operand_val = self.get_value(operand)?; let result = self.execute_unary_op(op, &operand_val)?; self.set_value(dst, result); Ok(ControlFlow::Continue) } - pub(crate) fn execute_compare(&mut self, dst: ValueId, op: &CompareOp, lhs: ValueId, rhs: ValueId) -> Result { - 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); } + pub(crate) fn execute_compare( + &mut self, + dst: ValueId, + op: &CompareOp, + lhs: ValueId, + rhs: ValueId, + ) -> Result { + 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 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 { VMValue::BoxRef(b) => { - if let Some(ib) = b.as_any().downcast_ref::() { VMValue::Integer(ib.value) } - else { match b.to_string_box().value.trim().parse::() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) } } + if let Some(ib) = b.as_any().downcast_ref::() { + VMValue::Integer(ib.value) + } else { + match b.to_string_box().value.trim().parse::() { + Ok(n) => VMValue::Integer(n), + Err(_) => VMValue::BoxRef(b), + } + } } other => other, }; right = match right { VMValue::BoxRef(b) => { - if let Some(ib) = b.as_any().downcast_ref::() { VMValue::Integer(ib.value) } - else { match b.to_string_box().value.trim().parse::() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) } } + if let Some(ib) = b.as_any().downcast_ref::() { + VMValue::Integer(ib.value) + } else { + match b.to_string_box().value.trim().parse::() { + Ok(n) => VMValue::Integer(n), + Err(_) => VMValue::BoxRef(b), + } + } } other => other, }; @@ -139,33 +237,70 @@ impl VM { println!("{}", val.to_string()); Ok(ControlFlow::Continue) } - pub(crate) fn execute_jump(&self, target: BasicBlockId) -> Result { Ok(ControlFlow::Jump(target)) } - pub(crate) fn execute_branch(&self, condition: ValueId, then_bb: BasicBlockId, else_bb: BasicBlockId) -> Result { + pub(crate) fn execute_jump(&self, target: BasicBlockId) -> Result { + Ok(ControlFlow::Jump(target)) + } + pub(crate) fn execute_branch( + &self, + condition: ValueId, + then_bb: BasicBlockId, + else_bb: BasicBlockId, + ) -> Result { let cond_val = self.get_value(condition)?; let should_branch = match &cond_val { VMValue::Bool(b) => *b, VMValue::Void => false, VMValue::Integer(i) => *i != 0, VMValue::BoxRef(b) => { - if let Some(bool_box) = b.as_any().downcast_ref::() { bool_box.value } - else if b.as_any().downcast_ref::().is_some() { false } - else { return Err(VMError::TypeError(format!("Branch condition must be bool, void, or integer, got BoxRef({})", b.type_name()))); } + if let Some(bool_box) = b.as_any().downcast_ref::() { + bool_box.value + } else if b.as_any().downcast_ref::().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) -> Result { if let Some(val_id) = value { 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)) } 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)) } } - pub(crate) fn execute_typeop(&mut self, dst: ValueId, op: &TypeOpKind, value: ValueId, ty: &MirType) -> Result { + pub(crate) fn execute_typeop( + &mut self, + dst: ValueId, + op: &TypeOpKind, + value: ValueId, + ty: &MirType, + ) -> Result { let val = self.get_value(value)?; match op { TypeOpKind::Check => { @@ -175,7 +310,9 @@ impl VM { (VMValue::Bool(_), MirType::Bool) => true, (VMValue::String(_), MirType::String) => 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, }; self.set_value(dst, VMValue::Bool(is_type)); @@ -185,34 +322,65 @@ impl VM { let result = match (&val, ty) { (VMValue::Integer(i), MirType::Float) => VMValue::Float(*i as f64), (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::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))); } + (VMValue::Integer(_), MirType::Integer) + | (VMValue::Float(_), MirType::Float) + | (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); Ok(ControlFlow::Continue) } } } - pub(crate) fn execute_phi(&mut self, dst: ValueId, inputs: &[(BasicBlockId, ValueId)]) -> Result { + pub(crate) fn execute_phi( + &mut self, + dst: ValueId, + inputs: &[(BasicBlockId, ValueId)], + ) -> Result { let selected = self.loop_execute_phi(dst, inputs)?; self.set_value(dst, selected); Ok(ControlFlow::Continue) } - pub(crate) fn execute_load(&mut self, dst: ValueId, ptr: ValueId) -> Result { + pub(crate) fn execute_load( + &mut self, + dst: ValueId, + ptr: ValueId, + ) -> Result { let loaded_value = self.get_value(ptr)?; self.set_value(dst, loaded_value); Ok(ControlFlow::Continue) } - pub(crate) fn execute_store(&mut self, value: ValueId, ptr: ValueId) -> Result { + pub(crate) fn execute_store( + &mut self, + value: ValueId, + ptr: ValueId, + ) -> Result { let val = self.get_value(value)?; self.set_value(ptr, val); Ok(ControlFlow::Continue) } - pub(crate) fn execute_copy(&mut self, dst: ValueId, src: ValueId) -> Result { + pub(crate) fn execute_copy( + &mut self, + dst: ValueId, + src: ValueId, + ) -> Result { let value = self.get_value(src)?; 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(), }; self.set_value(dst, cloned); @@ -220,7 +388,12 @@ impl VM { } // ---- Arrays ---- - pub(crate) fn execute_array_get(&mut self, dst: ValueId, array: ValueId, index: ValueId) -> Result { + pub(crate) fn execute_array_get( + &mut self, + dst: ValueId, + array: ValueId, + index: ValueId, + ) -> Result { let array_val = self.get_value(array)?; let index_val = self.get_value(index)?; if let VMValue::BoxRef(array_box) = &array_val { @@ -229,10 +402,23 @@ impl VM { let result = array.get(index_box); self.set_value(dst, VMValue::BoxRef(Arc::from(result))); Ok(ControlFlow::Continue) - } else { Err(VMError::TypeError("ArrayGet requires an ArrayBox".to_string())) } - } else { Err(VMError::TypeError("ArrayGet requires array and integer index".to_string())) } + } else { + Err(VMError::TypeError( + "ArrayGet requires an ArrayBox".to_string(), + )) + } + } 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 { + pub(crate) fn execute_array_set( + &mut self, + array: ValueId, + index: ValueId, + value: ValueId, + ) -> Result { let array_val = self.get_value(array)?; let index_val = self.get_value(index)?; let value_val = self.get_value(value)?; @@ -243,27 +429,50 @@ impl VM { let box_value = value_val.to_nyash_box(); array.set(index_box, box_value); Ok(ControlFlow::Continue) - } else { Err(VMError::TypeError("ArraySet requires an ArrayBox".to_string())) } - } else { Err(VMError::TypeError("ArraySet requires array and integer index".to_string())) } + } else { + Err(VMError::TypeError( + "ArraySet requires an ArrayBox".to_string(), + )) + } + } else { + Err(VMError::TypeError( + "ArraySet requires array and integer index".to_string(), + )) + } } // ---- Refs/Weak/Barriers ---- - pub(crate) fn execute_ref_new(&mut self, dst: ValueId, box_val: ValueId) -> Result { + pub(crate) fn execute_ref_new( + &mut self, + dst: ValueId, + box_val: ValueId, + ) -> Result { let box_value = self.get_value(box_val)?; self.set_value(dst, box_value); Ok(ControlFlow::Continue) } - pub(crate) fn execute_ref_get(&mut self, dst: ValueId, reference: ValueId, field: &str) -> Result { + pub(crate) fn execute_ref_get( + &mut self, + dst: ValueId, + reference: ValueId, + field: &str, + ) -> Result { 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); if !is_internal { if let Some(class_name) = self.object_class.get(&reference) { if let Ok(decls) = self.runtime.box_declarations.read() { 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) { - 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) { 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() } else { - if debug_ref { eprintln!("[VM] RefGet miss: {} -> default 0", field); } + if debug_ref { + eprintln!("[VM] RefGet miss: {} -> default 0", field); + } VMValue::Integer(0) } } else { - if debug_ref { eprintln!("[VM] RefGet no fields: -> default 0"); } + if debug_ref { + eprintln!("[VM] RefGet no fields: -> default 0"); + } VMValue::Integer(0) }; 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 = host.read().unwrap(); if let Ok(pbox) = host.create_box("ConsoleBox", &[]) { field_value = VMValue::from_nyash_box(pbox); - if !self.object_fields.contains_key(&reference) { 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()); } + if !self.object_fields.contains_key(&reference) { + 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); Ok(ControlFlow::Continue) } - pub(crate) fn execute_ref_set(&mut self, reference: ValueId, field: &str, value: ValueId) -> Result { + pub(crate) fn execute_ref_set( + &mut self, + reference: ValueId, + field: &str, + value: ValueId, + ) -> Result { let debug_ref = std::env::var("NYASH_VM_DEBUG_REF").ok().as_deref() == Some("1"); 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); if !is_internal { if let Some(class_name) = self.object_class.get(&reference) { if let Ok(decls) = self.runtime.box_declarations.read() { 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) { - 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"); - 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) } - pub(crate) fn execute_weak_new(&mut self, dst: ValueId, box_val: ValueId) -> Result { + pub(crate) fn execute_weak_new( + &mut self, + dst: ValueId, + box_val: ValueId, + ) -> Result { let box_value = self.get_value(box_val)?; self.set_value(dst, box_value); Ok(ControlFlow::Continue) } - pub(crate) fn execute_weak_load(&mut self, dst: ValueId, weak_ref: ValueId) -> Result { + pub(crate) fn execute_weak_load( + &mut self, + dst: ValueId, + weak_ref: ValueId, + ) -> Result { let weak_value = self.get_value(weak_ref)?; self.set_value(dst, weak_value); Ok(ControlFlow::Continue) } - pub(crate) fn execute_barrier_read(&mut self, dst: ValueId, value: ValueId) -> Result { + pub(crate) fn execute_barrier_read( + &mut self, + dst: ValueId, + value: ValueId, + ) -> Result { let val = self.get_value(value)?; self.set_value(dst, val); Ok(ControlFlow::Continue) } - pub(crate) fn execute_barrier_write(&mut self, _value: ValueId) -> Result { Ok(ControlFlow::Continue) } + pub(crate) fn execute_barrier_write( + &mut self, + _value: ValueId, + ) -> Result { + Ok(ControlFlow::Continue) + } pub(crate) fn execute_throw(&mut self, exception: ValueId) -> Result { 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 { + pub(crate) fn execute_catch( + &mut self, + exception_value: ValueId, + ) -> Result { self.set_value(exception_value, VMValue::Void); Ok(ControlFlow::Continue) } // ---- Futures ---- - pub(crate) fn execute_await(&mut self, dst: ValueId, future: ValueId) -> Result { + pub(crate) fn execute_await( + &mut self, + dst: ValueId, + future: ValueId, + ) -> Result { let future_val = self.get_value(future)?; if let VMValue::Future(ref future_box) = future_val { let max_ms: u64 = crate::config::env::await_max_ms(); @@ -350,10 +621,14 @@ impl VM { let mut spins = 0usize; while !future_box.ready() { 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(); 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) { let err = Box::new(crate::box_trait::StringBox::new("Timeout")); 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)); self.set_value(dst, vm_value); Ok(ControlFlow::Continue) - } else { Err(VMError::TypeError(format!("Expected Future, got {:?}", future_val))) } + } else { + Err(VMError::TypeError(format!( + "Expected Future, got {:?}", + future_val + ))) + } } } diff --git a/src/backend/vm_instructions/extern_call.rs b/src/backend/vm_instructions/extern_call.rs index 9368e95b..4f75c58e 100644 --- a/src/backend/vm_instructions/extern_call.rs +++ b/src/backend/vm_instructions/extern_call.rs @@ -1,33 +1,60 @@ +use crate::backend::vm::ControlFlow; +use crate::backend::{VMError, VMValue, VM}; use crate::box_trait::NyashBox; use crate::mir::ValueId; -use crate::backend::vm::ControlFlow; -use crate::backend::{VM, VMError, VMValue}; impl VM { /// Execute ExternCall instruction - pub(crate) fn execute_extern_call(&mut self, dst: Option, iface_name: &str, method_name: &str, args: &[ValueId]) -> Result { + pub(crate) fn execute_extern_call( + &mut self, + dst: Option, + iface_name: &str, + method_name: &str, + args: &[ValueId], + ) -> Result { // Core-13 pure shims: env.local.{get,set}, env.box.new match (iface_name, method_name) { ("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 v = self.get_value(ptr).unwrap_or(crate::backend::vm::VMValue::Void); - if let Some(d) = dst { self.set_value(d, v); } + let v = self + .get_value(ptr) + .unwrap_or(crate::backend::vm::VMValue::Void); + if let Some(d) = dst { + self.set_value(d, v); + } return Ok(ControlFlow::Continue); } ("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 val = self.get_value(args[1])?; 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); } ("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 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 let mut ny_args: Vec> = Vec::new(); for id in args.iter().skip(1) { @@ -36,8 +63,17 @@ impl VM { } let reg = crate::runtime::box_registry::get_global_registry(); 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)); } } - Err(e) => { return Err(VMError::InvalidInstruction(format!("env.box.new failed for {}: {}", ty_name, e))); } + Ok(b) => { + 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); } @@ -45,41 +81,90 @@ impl VM { } // Optional routing to name→slot handlers for stability and diagnostics 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 - let vm_args: Vec = args.iter().filter_map(|a| self.get_value(*a).ok()).collect(); + let vm_args: Vec = args + .iter() + .filter_map(|a| self.get_value(*a).ok()) + .collect(); match (iface_name, method_name, slot) { ("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); } ("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 let Some(d) = dst { self.set_value(d, VMValue::Void); } + 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 let Some(d) = dst { + self.set_value(d, VMValue::Void); + } return Ok(ControlFlow::Continue); } ("env.box", "new", 50) => { - if vm_args.is_empty() { 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())) }; + if vm_args.is_empty() { + 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> = 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(); match reg.create_box(&ty_name, &ny_args) { - Ok(b) => { 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))); } + Ok(b) => { + 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); } ("env.console", m @ ("log" | "warn" | "error" | "println"), 10) => { 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); } ("env.debug", "trace", 11) => { - if let Some(a0) = vm_args.get(0) { eprintln!("[trace] {}", a0.to_string()); } - if let Some(d) = dst { self.set_value(d, VMValue::Void); } + if let Some(a0) = vm_args.get(0) { + eprintln!("[trace] {}", a0.to_string()); + } + if let Some(d) = dst { + self.set_value(d, VMValue::Void); + } return Ok(ControlFlow::Continue); } ("env.runtime", "checkpoint", 12) => { @@ -88,21 +173,35 @@ impl VM { eprintln!("[rt] checkpoint @{} bb={} pc={}", func, bb, pc); } self.runtime.gc.safepoint(); - if let Some(s) = &self.runtime.scheduler { s.poll(); } - if let Some(d) = dst { self.set_value(d, VMValue::Void); } + if let Some(s) = &self.runtime.scheduler { + s.poll(); + } + if let Some(d) = dst { + self.set_value(d, VMValue::Void); + } return Ok(ControlFlow::Continue); } ("env.future", "new", 20) | ("env.future", "birth", 20) => { // Create a new Future and optionally set initial value from arg0 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(d) = dst { self.set_value(d, VMValue::Future(fut)); } + if let Some(a0) = vm_args.get(0) { + fut.set_result(a0.to_nyash_box()); + } + if let Some(d) = dst { + self.set_value(d, VMValue::Future(fut)); + } return Ok(ControlFlow::Continue); } ("env.future", "set", 21) => { // 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 let Some(d) = dst { self.set_value(d, VMValue::Void); } + if vm_args.len() >= 2 { + 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); } ("env.future", "await", 22) => { @@ -112,21 +211,57 @@ impl VM { let max_ms = crate::config::env::await_max_ms(); while !fb.ready() { 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); - if let Some(d) = dst { self.set_value(d, VMValue::from_nyash_box(Box::new(ok))); } - } else if let Some(d) = dst { self.set_value(d, VMValue::Void); } + if let Some(d) = dst { + 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); } - ("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) => { - let ms = vm_args.get(0).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); } + let ms = vm_args + .get(0) + .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); } _ => { /* fallthrough to host */ } @@ -137,24 +272,38 @@ impl VM { match (iface_name, method_name) { ("env.modules", "set") => { // Expect two args - let vm_args: Vec = args.iter().filter_map(|a| self.get_value(*a).ok()).collect(); + let vm_args: Vec = args + .iter() + .filter_map(|a| self.get_value(*a).ok()) + .collect(); if vm_args.len() >= 2 { let key = vm_args[0].to_string(); let val_box = vm_args[1].to_nyash_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); } ("env.modules", "get") => { - let vm_args: Vec = args.iter().filter_map(|a| self.get_value(*a).ok()).collect(); + let vm_args: Vec = args + .iter() + .filter_map(|a| self.get_value(*a).ok()) + .collect(); if let Some(k) = vm_args.get(0) { let key = k.to_string(); if let Some(v) = crate::runtime::modules_registry::get(&key) { - if let Some(d) = dst { self.set_value(d, VMValue::from_nyash_box(v)); } - 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); } + if let Some(d) = dst { + self.set_value(d, VMValue::from_nyash_box(v)); + } 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); + } return Ok(ControlFlow::Continue); } _ => {} @@ -164,7 +313,10 @@ impl VM { // Name-route minimal registry even when slot routing is disabled if iface_name == "env.modules" { // Decode args as VMValue for convenience - let vm_args: Vec = args.iter().filter_map(|a| self.get_value(*a).ok()).collect(); + let vm_args: Vec = args + .iter() + .filter_map(|a| self.get_value(*a).ok()) + .collect(); match method_name { "set" => { if vm_args.len() >= 2 { @@ -172,16 +324,24 @@ impl VM { let val_box = vm_args[1].to_nyash_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); } "get" => { if let Some(k) = vm_args.get(0) { let key = k.to_string(); if let Some(v) = crate::runtime::modules_registry::get(&key) { - if let Some(d) = dst { 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); } + if let Some(d) = dst { + 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); + } return Ok(ControlFlow::Continue); } _ => {} @@ -190,30 +350,78 @@ impl VM { // Evaluate arguments as NyashBox for loader let mut nyash_args: Vec> = 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 let Some(slot) = crate::runtime::extern_registry::resolve_slot(iface_name, method_name) { - 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()); } + if let Some(slot) = + crate::runtime::extern_registry::resolve_slot(iface_name, method_name) + { + 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 = 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) { - Ok(Some(result_box)) => { 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); } } + Ok(Some(result_box)) => { + 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(_) => { - 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(); - 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)); - if let Err(detail) = crate::runtime::extern_registry::check_arity(iface_name, 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)); + if let Err(detail) = crate::runtime::extern_registry::check_arity( + iface_name, + 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 { let known = crate::runtime::extern_registry::known_for_iface(iface_name); - if !known.is_empty() { 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(", "))); } + if !known.is_empty() { + 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)); } diff --git a/src/backend/vm_instructions/function_new.rs b/src/backend/vm_instructions/function_new.rs index 446b63bb..6bbaf0aa 100644 --- a/src/backend/vm_instructions/function_new.rs +++ b/src/backend/vm_instructions/function_new.rs @@ -1,7 +1,7 @@ +use crate::backend::vm::ControlFlow; +use crate::backend::{VMError, VMValue, VM}; use crate::mir::ValueId; use std::sync::Arc; -use crate::backend::vm::ControlFlow; -use crate::backend::{VM, VMError, VMValue}; impl VM { /// Execute FunctionNew instruction (construct a FunctionBox value) @@ -23,11 +23,14 @@ impl VM { // Capture 'me' weakly if provided and is a BoxRef if let Some(m) = me { 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))); Ok(ControlFlow::Continue) } diff --git a/src/backend/vm_instructions/mod.rs b/src/backend/vm_instructions/mod.rs index 04b1c0a6..9375d65e 100644 --- a/src/backend/vm_instructions/mod.rs +++ b/src/backend/vm_instructions/mod.rs @@ -5,11 +5,10 @@ * 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 plugin_invoke; // execute_plugin_invoke + helpers - +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 diff --git a/src/backend/vm_instructions/newbox.rs b/src/backend/vm_instructions/newbox.rs index 0ab3a361..88b67c07 100644 --- a/src/backend/vm_instructions/newbox.rs +++ b/src/backend/vm_instructions/newbox.rs @@ -1,14 +1,20 @@ +use crate::backend::vm::ControlFlow; +use crate::backend::{VMError, VMValue, VM}; use crate::box_trait::NyashBox; use crate::mir::ValueId; use std::sync::Arc; -use crate::backend::vm::ControlFlow; -use crate::backend::{VM, VMError, VMValue}; impl VM { /// Execute NewBox instruction - pub(crate) fn execute_newbox(&mut self, dst: ValueId, box_type: &str, args: &[ValueId]) -> Result { + pub(crate) fn execute_newbox( + &mut self, + dst: ValueId, + box_type: &str, + args: &[ValueId], + ) -> Result { // Convert args to NyashBox values - let arg_values: Vec> = args.iter() + let arg_values: Vec> = args + .iter() .map(|arg| { let val = self.get_value(*arg)?; Ok(val.to_nyash_box()) @@ -17,15 +23,20 @@ impl VM { // Create new box using runtime's registry let new_box = { - let registry = self.runtime.box_registry.lock() - .map_err(|_| 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)))? + let registry = self.runtime.box_registry.lock().map_err(|_| { + 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)) + })? }; - + // 80/20: Basic boxes are stored as primitives in VMValue for simpler ops if box_type == "IntegerBox" { - if let Some(ib) = new_box.as_any().downcast_ref::() { + if let Some(ib) = new_box + .as_any() + .downcast_ref::() + { self.set_value(dst, VMValue::Integer(ib.value)); return Ok(ControlFlow::Continue); } @@ -35,7 +46,10 @@ impl VM { return Ok(ControlFlow::Continue); } } else if box_type == "StringBox" { - if let Some(sb) = new_box.as_any().downcast_ref::() { + if let Some(sb) = new_box + .as_any() + .downcast_ref::() + { self.set_value(dst, VMValue::String(sb.value.clone())); return Ok(ControlFlow::Continue); } diff --git a/src/backend/vm_instructions/plugin_invoke.rs b/src/backend/vm_instructions/plugin_invoke.rs index 259aaa1e..0c29491c 100644 --- a/src/backend/vm_instructions/plugin_invoke.rs +++ b/src/backend/vm_instructions/plugin_invoke.rs @@ -1,79 +1,186 @@ -use crate::mir::ValueId; use crate::backend::vm::ControlFlow; -use crate::backend::{VM, VMError, VMValue}; +use crate::backend::{VMError, VMValue, VM}; +use crate::mir::ValueId; impl VM { /// Execute a forced plugin invocation (no builtin fallback) - pub(crate) fn execute_plugin_invoke(&mut self, dst: Option, box_val: ValueId, method: &str, args: &[ValueId]) -> Result { + pub(crate) fn execute_plugin_invoke( + &mut self, + dst: Option, + box_val: ValueId, + method: &str, + args: &[ValueId], + ) -> Result { // 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 { - if let Some(sb) = bx.as_any().downcast_ref::() { return Some(sb.value.clone()); } - if let Some(res) = bx.as_any().downcast_ref::() { - if let crate::boxes::result::NyashResultBox::Ok(inner) = res { return extract_string_from_box(inner.as_ref()); } + if let Some(sb) = bx.as_any().downcast_ref::() { + return Some(sb.value.clone()); } - if let Some(p) = bx.as_any().downcast_ref::() { + if let Some(res) = bx + .as_any() + .downcast_ref::() + { + 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::() + { if p.box_type == "StringBox" { let host = crate::runtime::get_global_plugin_host(); let tmp: Option = if let Ok(ro) = host.read() { - if let Ok(val_opt) = ro.invoke_instance_method("StringBox", "toUtf8", p.inner.instance_id, &[]) { - if let Some(vb) = val_opt { if let Some(sb2) = vb.as_any().downcast_ref::() { Some(sb2.value.clone()) } else { None } } else { None } - } else { None } - } else { None }; - if tmp.is_some() { return tmp; } + if let Ok(val_opt) = ro.invoke_instance_method( + "StringBox", + "toUtf8", + p.inner.instance_id, + &[], + ) { + if let Some(vb) = val_opt { + if let Some(sb2) = + vb.as_any().downcast_ref::() + { + Some(sb2.value.clone()) + } else { + None + } + } else { + None + } + } else { + None + } + } else { + None + }; + if tmp.is_some() { + return tmp; + } } } None } let recv = self.get_value(box_val)?; - if method == "birth" && !matches!(recv, VMValue::BoxRef(ref b) if b.as_any().downcast_ref::().is_some()) { + if method == "birth" + && !matches!(recv, VMValue::BoxRef(ref b) if b.as_any().downcast_ref::().is_some()) + { eprintln!("[VM PluginInvoke] static birth fallback recv={:?}", recv); let mut created: Option = None; match &recv { VMValue::String(s) => { let host = crate::runtime::get_global_plugin_host(); let host = host.read().unwrap(); - let sb: Box = 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)); } + let sb: Box = + 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) => { let host = crate::runtime::get_global_plugin_host(); 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 Some(p) = pbox.as_any().downcast_ref::() { + if let Some(p) = pbox + .as_any() + .downcast_ref::() + { let host = crate::runtime::get_global_plugin_host(); 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 mut tlv = crate::runtime::plugin_ffi_common::encode_tlv_header(args.len() as u16); + let mh = host.resolve_method(&p.box_type, method).map_err(|_| { + 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() { let v = self.get_value(*a)?; 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::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::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::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) => { - if let Some(h) = b.as_any().downcast_ref::() { + if let Some(h) = b + .as_any() + .downcast_ref::() + { if h.box_type == "StringBox" { let host = crate::runtime::get_global_plugin_host(); let host = host.read().unwrap(); - if let Ok(val_opt) = host.invoke_instance_method("StringBox", "toUtf8", h.inner.instance_id, &[]) { - if let Some(sb) = val_opt.and_then(|bx| bx.as_any().downcast_ref::().map(|s| s.value.clone())) { crate::runtime::plugin_ffi_common::encode::string(&mut tlv, &sb); continue; } + if let Ok(val_opt) = host.invoke_instance_method( + "StringBox", + "toUtf8", + h.inner.instance_id, + &[], + ) { + if let Some(sb) = val_opt.and_then(|bx| { + bx.as_any() + .downcast_ref::() + .map(|s| s.value.clone()) + }) { + crate::runtime::plugin_ffi_common::encode::string( + &mut tlv, &sb, + ); + continue; + } } } else if h.box_type == "IntegerBox" { let host = crate::runtime::get_global_plugin_host(); let host = host.read().unwrap(); - if let Ok(val_opt) = host.invoke_instance_method("IntegerBox", "get", h.inner.instance_id, &[]) { - if let Some(ib) = val_opt.and_then(|bx| bx.as_any().downcast_ref::().map(|i| i.value)) { crate::runtime::plugin_ffi_common::encode::i64(&mut tlv, ib); continue; } + if let Ok(val_opt) = host.invoke_instance_method( + "IntegerBox", + "get", + h.inner.instance_id, + &[], + ) { + if let Some(ib) = val_opt.and_then(|bx| { + bx.as_any() + .downcast_ref::() + .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 { let h = crate::runtime::host_handles::to_handle_arc(b.clone()); 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_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) }; - let vm_out_raw: VMValue = if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..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, + ) + }; + 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") { - eprintln!("[VM←Plugin] tag={} size={} bytes={}", tag, _sz, payload.len()); + eprintln!( + "[VM←Plugin] tag={} size={} bytes={}", + tag, + _sz, + payload.len() + ); } 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 => { - let v = crate::runtime::plugin_ffi_common::decode::i32(payload).unwrap_or_default(); - if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") { eprintln!("[VM←Plugin] decode i32={}", v); } + let v = crate::runtime::plugin_ffi_common::decode::i32(payload) + .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) - }, - 5 => crate::runtime::plugin_ffi_common::decode::f64(payload).map(VMValue::Float).unwrap_or(VMValue::Void), - 6 | 7 => VMValue::String(crate::runtime::plugin_ffi_common::decode::string(payload)), + } + 5 => crate::runtime::plugin_ffi_common::decode::f64(payload) + .map(VMValue::Float) + .unwrap_or(VMValue::Void), + 6 | 7 => VMValue::String( + crate::runtime::plugin_ffi_common::decode::string(payload), + ), 8 => { - 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(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 + } } _ => VMValue::Void, } - } else { VMValue::Void }; + } else { + VMValue::Void + }; // Wrap into Result.Ok when method is declared returns_result let vm_out = { 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::BoxRef(b) => b.share_box(), 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); - VMValue::BoxRef(std::sync::Arc::from(Box::new(res) as Box)) - } else { vm_out_raw } + VMValue::BoxRef(std::sync::Arc::from( + Box::new(res) as Box + )) + } 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); } } @@ -133,28 +281,66 @@ impl VM { if let VMValue::BoxRef(ref bx) = recv { if let Some(s) = extract_string_from_box(bx.as_ref()) { match method { - "length" => { 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); } + "length" => { + 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" => { - let idx_v = if let Some(a0) = args.get(0) { self.get_value(*a0)? } else { VMValue::Integer(0) }; - let idx = match idx_v { VMValue::Integer(i) => i.max(0) as usize, _ => 0 }; + let idx_v = if let Some(a0) = args.get(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); - 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); } "concat" => { - let rhs_v = if let Some(a0) = args.get(0) { 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 rhs_v = if let Some(a0) = args.get(0) { + 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(); new_s.push_str(&rhs_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))); } + if let Some(dst_id) = dst { + self.set_value( + dst_id, + VMValue::BoxRef(std::sync::Arc::from( + out as Box, + )), + ); + } 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 + ))) } } diff --git a/src/backend/vm_methods.rs b/src/backend/vm_methods.rs index 41fc8a82..d9a4c54c 100644 --- a/src/backend/vm_methods.rs +++ b/src/backend/vm_methods.rs @@ -6,8 +6,8 @@ * the existing VM API surface. */ +use super::vm::{VMError, VM}; use crate::box_trait::NyashBox; -use super::vm::{VM, VMError}; impl VM { /// 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) } } - diff --git a/src/backend/vm_phi.rs b/src/backend/vm_phi.rs index 005f6131..0f8ea19d 100644 --- a/src/backend/vm_phi.rs +++ b/src/backend/vm_phi.rs @@ -7,7 +7,7 @@ * Typical Callers: VM 実行ループ(ブロック遷移/phi評価) */ -use super::vm::{VMValue, VMError}; +use super::vm::{VMError, VMValue}; use crate::mir::{BasicBlockId, ValueId}; use std::collections::HashMap; @@ -15,7 +15,7 @@ use std::collections::HashMap; pub struct PhiHandler { /// 現在のブロックに到達する前のブロック previous_block: Option, - + /// Phi nodeの値キャッシュ(最適化用) phi_cache: HashMap, } @@ -28,7 +28,7 @@ impl PhiHandler { phi_cache: HashMap::new(), } } - + /// ブロック遷移を記録 pub fn record_block_transition(&mut self, from: BasicBlockId, to: BasicBlockId) { self.previous_block = Some(from); @@ -37,13 +37,13 @@ impl PhiHandler { self.phi_cache.clear(); } } - + /// 初期ブロックへのエントリを記録 pub fn record_entry(&mut self) { self.previous_block = None; self.phi_cache.clear(); } - + /// Phi命令を実行 pub fn execute_phi( &mut self, @@ -55,16 +55,16 @@ impl PhiHandler { // if let Some(cached) = self.phi_cache.get(&dst) { // return Ok(cached.clone()); // } - + // Phi nodeの入力を選択 let selected_value = self.select_phi_input(inputs, get_value_fn)?; - + // キャッシュに保存(デバッグ用に残すが使わない) // self.phi_cache.insert(dst, selected_value.clone()); - + Ok(selected_value) } - + /// Phi nodeの適切な入力を選択 fn select_phi_input( &self, @@ -72,9 +72,11 @@ impl PhiHandler { get_value_fn: impl Fn(ValueId) -> Result, ) -> Result { 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に基づいて入力を選択 if let Some(prev_block) = self.previous_block { // 対応するブロックからの入力を探す @@ -84,16 +86,16 @@ impl PhiHandler { return Ok(value); } } - + // フォールバック:見つからない場合は最初の入力を使用 // これは通常起こらないはずだが、安全のため } - + // previous_blockがない場合(エントリポイント)は最初の入力を使用 let (_, value_id) = &inputs[0]; get_value_fn(*value_id) } - + /// ループヘッダーかどうかを判定(簡易版) fn is_loop_header(&self, _block_id: BasicBlockId) -> bool { // TODO: MIR情報からループヘッダーを判定する機能を追加 @@ -106,7 +108,7 @@ impl PhiHandler { pub struct LoopExecutor { /// Phiハンドラー phi_handler: PhiHandler, - + /// ループイテレーション数(デバッグ用) iteration_count: HashMap, } @@ -119,23 +121,24 @@ impl LoopExecutor { iteration_count: HashMap::new(), } } - + /// ブロック遷移を記録 pub fn record_transition(&mut self, from: BasicBlockId, to: BasicBlockId) { self.phi_handler.record_block_transition(from, to); - + // ループイテレーション数を更新(デバッグ用) - if from > to { // 単純なバックエッジ検出 + if from > to { + // 単純なバックエッジ検出 *self.iteration_count.entry(to).or_insert(0) += 1; } } - + /// エントリポイントでの初期化 pub fn initialize(&mut self) { self.phi_handler.record_entry(); self.iteration_count.clear(); } - + /// Phi命令を実行 pub fn execute_phi( &mut self, @@ -145,25 +148,25 @@ impl LoopExecutor { ) -> Result { self.phi_handler.execute_phi(dst, inputs, get_value_fn) } - + /// デバッグ情報を取得 pub fn debug_info(&self) -> String { let mut info = String::new(); info.push_str("Loop Executor Debug Info:\n"); - + if let Some(prev) = self.phi_handler.previous_block { info.push_str(&format!(" Previous block: {:?}\n", prev)); } else { info.push_str(" Previous block: None (entry)\n"); } - + if !self.iteration_count.is_empty() { info.push_str(" Loop iterations:\n"); for (block, count) in &self.iteration_count { info.push_str(&format!(" Block {:?}: {} iterations\n", block, count)); } } - + info } } @@ -171,46 +174,38 @@ impl LoopExecutor { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_phi_selection() { let mut handler = PhiHandler::new(); - + // テスト用の値 let inputs = vec![ - (BasicBlockId::new(0), ValueId::new(1)), // エントリブロックからの初期値 - (BasicBlockId::new(2), ValueId::new(2)), // ループボディからの更新値 + (BasicBlockId::new(0), ValueId::new(1)), // エントリブロックからの初期値 + (BasicBlockId::new(2), ValueId::new(2)), // ループボディからの更新値 ]; - + // エントリポイントからの実行 handler.record_entry(); - let result = handler.execute_phi( - ValueId::new(3), - &inputs, - |id| { - if id == ValueId::new(1) { - Ok(VMValue::Integer(0)) - } else { - Ok(VMValue::Integer(10)) - } + let result = handler.execute_phi(ValueId::new(3), &inputs, |id| { + if id == ValueId::new(1) { + Ok(VMValue::Integer(0)) + } else { + Ok(VMValue::Integer(10)) } - ); + }); assert_eq!(result.unwrap(), VMValue::Integer(0)); - + // ループボディからの実行 handler.record_block_transition(BasicBlockId::new(2), BasicBlockId::new(1)); - handler.phi_cache.clear(); // テスト用にキャッシュクリア - let result = handler.execute_phi( - ValueId::new(3), - &inputs, - |id| { - if id == ValueId::new(1) { - Ok(VMValue::Integer(0)) - } else { - Ok(VMValue::Integer(10)) - } + handler.phi_cache.clear(); // テスト用にキャッシュクリア + let result = handler.execute_phi(ValueId::new(3), &inputs, |id| { + if id == ValueId::new(1) { + Ok(VMValue::Integer(0)) + } else { + Ok(VMValue::Integer(10)) } - ); + }); assert_eq!(result.unwrap(), VMValue::Integer(10)); } } diff --git a/src/backend/vm_state.rs b/src/backend/vm_state.rs index bf4a12f4..7b2b2979 100644 --- a/src/backend/vm_state.rs +++ b/src/backend/vm_state.rs @@ -5,9 +5,9 @@ * 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::vm::{VMError, VMValue, VM}; +use super::vm_phi::LoopExecutor; use crate::mir::{BasicBlockId, ValueId}; use crate::runtime::NyashRuntime; use crate::scope_tracker::ScopeTracker; @@ -30,7 +30,9 @@ impl VM { inputs: &[(BasicBlockId, ValueId)], ) -> Result { 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") || 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))) } } else { - Err(VMError::InvalidValue(format!("Value {} out of bounds", val_id))) + Err(VMError::InvalidValue(format!( + "Value {} out of bounds", + val_id + ))) } }); if debug_phi { @@ -88,7 +93,9 @@ impl VM { boxcall_vtable_funcname: std::collections::HashMap::new(), type_versions: std::collections::HashMap::new(), #[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")] jit_manager: None, } @@ -120,7 +127,9 @@ impl VM { boxcall_vtable_funcname: std::collections::HashMap::new(), type_versions: std::collections::HashMap::new(), #[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")] jit_manager: None, } @@ -136,7 +145,10 @@ impl VM { Err(VMError::InvalidValue(format!("Value {} not set", value_id))) } } else { - Err(VMError::InvalidValue(format!("Value {} out of bounds", value_id))) + Err(VMError::InvalidValue(format!( + "Value {} out of bounds", + value_id + ))) } } diff --git a/src/backend/vm_stats.rs b/src/backend/vm_stats.rs index da537bd5..97860966 100644 --- a/src/backend/vm_stats.rs +++ b/src/backend/vm_stats.rs @@ -12,25 +12,45 @@ use super::vm::VM; impl VM { /// Print simple VM execution statistics when enabled via env var pub(super) fn maybe_print_stats(&mut self) { - let enabled = std::env::var("NYASH_VM_STATS").ok().map(|v| v != "0").unwrap_or(false); - if !enabled { return; } + let enabled = std::env::var("NYASH_VM_STATS") + .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 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))); - let total: usize = items.iter().map(|(_,v)| *v).sum(); + let elapsed_ms = self + .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))); + 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) - || std::env::var("NYASH_VM_STATS_FORMAT").map(|v| v == "json").unwrap_or(false); + let json_enabled = std::env::var("NYASH_VM_STATS_JSON") + .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 { - let counts_obj: std::collections::BTreeMap<&str, usize> = self.instr_counter.iter().map(|(k,v)| (*k, *v)).collect(); - let top20: Vec<_> = items.iter().take(20).map(|(op,cnt)| { - serde_json::json!({ "op": op, "count": cnt }) - }).collect(); + let counts_obj: std::collections::BTreeMap<&str, usize> = + self.instr_counter.iter().map(|(k, v)| (*k, *v)).collect(); + let top20: Vec<_> = items + .iter() + .take(20) + .map(|(op, cnt)| serde_json::json!({ "op": op, "count": cnt })) + .collect(); let now_ms = { 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!({ "total": total, @@ -44,7 +64,10 @@ impl VM { Err(_) => println!("{{\"total\":{},\"elapsed_ms\":{:.3}}}", total, elapsed_ms), } } 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) { println!(" {:>10}: {:>8}", k, v); } @@ -55,9 +78,14 @@ impl VM { pub(super) fn maybe_print_jit_unified_stats(&self) { // 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 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"); - if !jit_enabled && !vm_enabled { return; } + if !jit_enabled && !vm_enabled { + return; + } if let Some(jm) = &self.jit_manager { // Gather basic counters let sites = jm.sites(); @@ -66,11 +94,19 @@ impl VM { let ok = jm.exec_ok_count(); let tr = jm.exec_trap_count(); 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 cfg = crate::jit::config::current(); 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 ret_b1_hints = crate::jit::rt::ret_bool_hint_get(); if jit_json { @@ -99,11 +135,17 @@ impl VM { }) }).collect::>() }); - 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 { eprintln!("[JIT] summary: sites={} compiled={} hits={} exec_ok={} trap={} fallback_rate={:.2} 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 + ); } } } diff --git a/src/backend/vm_types.rs b/src/backend/vm_types.rs index 63574aa8..02a7aab7 100644 --- a/src/backend/vm_types.rs +++ b/src/backend/vm_types.rs @@ -5,7 +5,7 @@ * 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 std::sync::Arc; @@ -94,7 +94,10 @@ impl VMValue { pub fn as_integer(&self) -> Result { match self { 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), // Pragmatic coercions for dynamic boxes (preserve legacy semantics) VMValue::BoxRef(b) => { - if let Some(bb) = b.as_any().downcast_ref::() { return Ok(bb.value); } - if let Some(ib) = b.as_any().downcast_ref::() { return Ok(ib.value != 0); } - if let Some(ib) = b.as_any().downcast_ref::() { return Ok(ib.value != 0); } - if b.as_any().downcast_ref::().is_some() { return Ok(false); } - Err(VMError::TypeError(format!("Expected bool, got BoxRef({})", b.type_name()))) + if let Some(bb) = b.as_any().downcast_ref::() { + return Ok(bb.value); + } + if let Some(ib) = b.as_any().downcast_ref::() { + return Ok(ib.value != 0); + } + if let Some(ib) = b + .as_any() + .downcast_ref::() + { + return Ok(ib.value != 0); + } + if b.as_any().downcast_ref::().is_some() { + return Ok(false); + } + Err(VMError::TypeError(format!( + "Expected bool, got BoxRef({})", + b.type_name() + ))) } VMValue::Void => Ok(false), VMValue::Float(f) => Ok(*f != 0.0), @@ -120,7 +137,11 @@ impl VMValue { /// Convert from NyashBox to VMValue pub fn from_nyash_box(nyash_box: Box) -> VMValue { - if nyash_box.as_any().downcast_ref::().is_some() { + if nyash_box + .as_any() + .downcast_ref::() + .is_some() + { // Treat NullBox as Void in VMValue to align with `null` literal semantics VMValue::Void } else if let Some(int_box) = nyash_box.as_any().downcast_ref::() { @@ -129,7 +150,10 @@ impl VMValue { VMValue::Bool(bool_box.value) } else if let Some(string_box) = nyash_box.as_any().downcast_ref::() { VMValue::String(string_box.value.clone()) - } else if let Some(future_box) = nyash_box.as_any().downcast_ref::() { + } else if let Some(future_box) = nyash_box + .as_any() + .downcast_ref::() + { VMValue::Future(future_box.clone()) } else { VMValue::BoxRef(Arc::from(nyash_box)) diff --git a/src/backend/vm_values.rs b/src/backend/vm_values.rs index 897efc5c..72cb26d8 100644 --- a/src/backend/vm_values.rs +++ b/src/backend/vm_values.rs @@ -7,53 +7,101 @@ * Typical Callers: vm_instructions::{execute_binop, execute_unaryop, execute_compare} */ -use crate::mir::{BinaryOp, CompareOp, UnaryOp}; use super::vm::VM; use super::vm_types::{VMError, VMValue}; +use crate::mir::{BinaryOp, CompareOp, UnaryOp}; impl VM { /// 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 { // Avoid recursion via PluginHost<->Loader for PluginBoxV2 during VM add/string ops - if b.as_any().downcast_ref::().is_some() { + if b.as_any() + .downcast_ref::() + .is_some() + { return None; } crate::runtime::semantics::coerce_to_string(b) } /// Execute binary operation - pub(super) fn execute_binary_op(&self, op: &BinaryOp, left: &VMValue, right: &VMValue) -> Result { + pub(super) fn execute_binary_op( + &self, + op: &BinaryOp, + left: &VMValue, + right: &VMValue, + ) -> Result { 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") { - 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" }; + 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", + }; match *op { BinaryOp::Add => { let strat = crate::grammar::engine::get().add_coercion_strategy(); 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 => { let strat = crate::grammar::engine::get().sub_coercion_strategy(); 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 => { let strat = crate::grammar::engine::get().mul_coercion_strategy(); 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 => { let strat = crate::grammar::engine::get().div_coercion_strategy(); 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") { - 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 matches!(*op, BinaryOp::Add) + && std::env::var("NYASH_GRAMMAR_ENFORCE_ADD").ok().as_deref() == Some("1") + { + 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) { match res { "String" => { @@ -86,7 +134,11 @@ impl VM { if matches!(*op, BinaryOp::And | BinaryOp::Or) { let l = left.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) { @@ -96,107 +148,219 @@ impl VM { BinaryOp::Sub => *l - *r, BinaryOp::Mul => *l * *r, BinaryOp::Div => { - if *r == 0 { return Err(VMError::DivisionByZero); } + if *r == 0 { + return Err(VMError::DivisionByZero); + } *l / *r - }, - _ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))), + } + _ => { + return Err(VMError::InvalidInstruction(format!( + "Unsupported integer operation: {:?}", + op + ))) + } }; 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::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)) => match op { + BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), + _ => Err(VMError::TypeError( + "String-bool operations only support addition".to_string(), + )), }, - (VMValue::String(l), VMValue::Bool(r)) => { - match op { - 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())) } + (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 (VMValue::String(l), VMValue::BoxRef(r)) => { - let rs = self.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())) } - }, + let rs = self + .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 (VMValue::BoxRef(l), VMValue::String(r)) => { - let ls = self.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())) } - }, + let ls = self + .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 - (VMValue::BoxRef(li), VMValue::BoxRef(ri)) if { - li.as_any().downcast_ref::().is_some() - || li.as_any().downcast_ref::().is_some() - } && { - ri.as_any().downcast_ref::().is_some() - || ri.as_any().downcast_ref::().is_some() - } => { - let l = li.as_any().downcast_ref::() + (VMValue::BoxRef(li), VMValue::BoxRef(ri)) + if { + li.as_any() + .downcast_ref::() + .is_some() + || li + .as_any() + .downcast_ref::() + .is_some() + } && { + ri.as_any() + .downcast_ref::() + .is_some() + || ri + .as_any() + .downcast_ref::() + .is_some() + } => + { + let l = li + .as_any() + .downcast_ref::() .map(|x| x.value) - .or_else(|| li.as_any().downcast_ref::().map(|x| x.value)) + .or_else(|| { + li.as_any() + .downcast_ref::() + .map(|x| x.value) + }) .unwrap(); - let r = ri.as_any().downcast_ref::() + let r = ri + .as_any() + .downcast_ref::() .map(|x| x.value) - .or_else(|| ri.as_any().downcast_ref::().map(|x| x.value)) + .or_else(|| { + ri.as_any() + .downcast_ref::() + .map(|x| x.value) + }) .unwrap(); 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 BoxRef operation: {:?}", op))), + BinaryOp::Div => { + if r == 0 { + return Err(VMError::DivisionByZero); + } + l / r + } + _ => { + return Err(VMError::InvalidInstruction(format!( + "Unsupported integer BoxRef operation: {:?}", + op + ))) + } }; Ok(VMValue::Integer(res)) } // BoxRef + BoxRef string-like concatenation (VMValue::BoxRef(li), VMValue::BoxRef(ri)) => { if matches!(*op, BinaryOp::Add) { - let ls = self.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); + let ls = self + .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))); } - Err(VMError::TypeError("Unsupported BoxRef+BoxRef operation".to_string())) + Err(VMError::TypeError( + "Unsupported BoxRef+BoxRef operation".to_string(), + )) } // Mixed Integer forms - (VMValue::BoxRef(li), VMValue::Integer(r)) if li.as_any().downcast_ref::().is_some() - || li.as_any().downcast_ref::().is_some() => { - let l = li.as_any().downcast_ref::() + (VMValue::BoxRef(li), VMValue::Integer(r)) + if li + .as_any() + .downcast_ref::() + .is_some() + || li + .as_any() + .downcast_ref::() + .is_some() => + { + let l = li + .as_any() + .downcast_ref::() .map(|x| x.value) - .or_else(|| li.as_any().downcast_ref::().map(|x| x.value)) + .or_else(|| { + li.as_any() + .downcast_ref::() + .map(|x| x.value) + }) .unwrap(); 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))), + BinaryOp::Div => { + if *r == 0 { + return Err(VMError::DivisionByZero); + } + l / *r + } + _ => { + return Err(VMError::InvalidInstruction(format!( + "Unsupported integer operation: {:?}", + op + ))) + } }; Ok(VMValue::Integer(res)) } - (VMValue::Integer(l), VMValue::BoxRef(ri)) if ri.as_any().downcast_ref::().is_some() - || ri.as_any().downcast_ref::().is_some() => { - let r = ri.as_any().downcast_ref::() + (VMValue::Integer(l), VMValue::BoxRef(ri)) + if ri + .as_any() + .downcast_ref::() + .is_some() + || ri + .as_any() + .downcast_ref::() + .is_some() => + { + let r = ri + .as_any() + .downcast_ref::() .map(|x| x.value) - .or_else(|| ri.as_any().downcast_ref::().map(|x| x.value)) + .or_else(|| { + ri.as_any() + .downcast_ref::() + .map(|x| x.value) + }) .unwrap(); 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))), + BinaryOp::Div => { + if r == 0 { + return Err(VMError::DivisionByZero); + } + *l / r + } + _ => { + return Err(VMError::InvalidInstruction(format!( + "Unsupported integer operation: {:?}", + op + ))) + } }; Ok(VMValue::Integer(res)) } @@ -210,80 +374,211 @@ impl VM { 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))), + 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-BoxRef -> {}", res); } + if debug_bin { + eprintln!("[VM] binop fallback BoxRef-BoxRef -> {}", res); + } Ok(VMValue::Integer(res)) } 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)) => { if let Ok(l) = lb.to_string_box().value.trim().parse::() { - 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))), }; - if debug_bin { eprintln!("[VM] binop fallback BoxRef-Int -> {}", res); } + 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 + ))) + } + }; + if debug_bin { + eprintln!("[VM] binop fallback BoxRef-Int -> {}", res); + } Ok(VMValue::Integer(res)) } 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)) => { if let Ok(r) = rb.to_string_box().value.trim().parse::() { - 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))), }; - if debug_bin { eprintln!("[VM] binop fallback Int-BoxRef -> {}", res); } + 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 + ))) + } + }; + if debug_bin { + eprintln!("[VM] binop fallback Int-BoxRef -> {}", res); + } Ok(VMValue::Integer(res)) } 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 - pub(super) fn execute_unary_op(&self, op: &UnaryOp, operand: &VMValue) -> Result { + pub(super) fn execute_unary_op( + &self, + op: &UnaryOp, + operand: &VMValue, + ) -> Result { match (op, operand) { (UnaryOp::Neg, VMValue::Integer(i)) => Ok(VMValue::Integer(-i)), (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 - pub(super) fn execute_compare_op(&self, op: &CompareOp, left: &VMValue, right: &VMValue) -> Result { - 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); } + pub(super) fn execute_compare_op( + &self, + op: &CompareOp, + left: &VMValue, + right: &VMValue, + ) -> Result { + 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) { // Mixed numeric (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)) => { - 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 - (VMValue::Bool(l), VMValue::Bool(r)) => { - Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, _ => return Err(VMError::TypeError(format!("Unsupported boolean comparison: {:?}", op))) }) - } + (VMValue::Bool(l), VMValue::Bool(r)) => Ok(match op { + CompareOp::Eq => l == r, + CompareOp::Ne => l != r, + _ => { + return Err(VMError::TypeError(format!( + "Unsupported boolean comparison: {:?}", + op + ))) + } + }), // Void - (VMValue::Void, VMValue::Void) => { - Ok(match op { CompareOp::Eq => true, CompareOp::Ne => false, _ => 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())) }) - } + (VMValue::Void, VMValue::Void) => Ok(match op { + CompareOp::Eq => true, + CompareOp::Ne => false, + _ => 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 - (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::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 }), + (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::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) (VMValue::BoxRef(li), VMValue::BoxRef(ri)) => { 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") { eprintln!( @@ -296,91 +591,265 @@ impl VM { if let Some(sb) = b.as_any().downcast_ref::() { return Some(sb.value.clone()); } - if let Some(pb) = b.as_any().downcast_ref::() { + if let Some(pb) = b + .as_any() + .downcast_ref::() + { if pb.box_type == "StringBox" { let host = crate::runtime::get_global_plugin_host(); let s_opt: Option = { 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(sbb) = vb.as_any().downcast_ref::() { + if let Some(sbb) = + vb.as_any() + .downcast_ref::() + { Some(sbb.value.clone()) - } else { None } - } else { None } - } else { 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 } - if let (Some(ls), Some(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 }); + if let (Some(ls), Some(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 - let l_opt = li.as_any().downcast_ref::().map(|x| x.value) - .or_else(|| li.as_any().downcast_ref::().map(|x| x.value)) + let l_opt = li + .as_any() + .downcast_ref::() + .map(|x| x.value) + .or_else(|| { + li.as_any() + .downcast_ref::() + .map(|x| x.value) + }) .or_else(|| li.to_string_box().value.parse::().ok()); - let r_opt = ri.as_any().downcast_ref::().map(|x| x.value) - .or_else(|| ri.as_any().downcast_ref::().map(|x| x.value)) + let r_opt = ri + .as_any() + .downcast_ref::() + .map(|x| x.value) + .or_else(|| { + ri.as_any() + .downcast_ref::() + .map(|x| x.value) + }) .or_else(|| ri.to_string_box().value.parse::().ok()); 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) (VMValue::String(ls), VMValue::BoxRef(ri)) => { - let rs_opt = if let Some(sb) = ri.as_any().downcast_ref::() { Some(sb.value.clone()) } else { - if let Some(pb) = ri.as_any().downcast_ref::() { - if pb.box_type == "StringBox" { - let host = crate::runtime::get_global_plugin_host(); - 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 Some(vb) = val_opt { - if let Some(sbb) = vb.as_any().downcast_ref::() { Some(sbb.value.clone()) } else { None } - } else { None } - } else { None } - } else { None }; - tmp - } 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))) + let rs_opt = + if let Some(sb) = ri.as_any().downcast_ref::() { + Some(sb.value.clone()) + } else { + if let Some(pb) = ri + .as_any() + .downcast_ref::() + { + if pb.box_type == "StringBox" { + let host = crate::runtime::get_global_plugin_host(); + 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 Some(vb) = val_opt { + if let Some(sbb) = + vb.as_any() + .downcast_ref::() + { + Some(sbb.value.clone()) + } else { + None + } + } else { + None + } + } else { + None + } + } else { + None + }; + tmp + } 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)) => { - let ls_opt = if let Some(sb) = li.as_any().downcast_ref::() { Some(sb.value.clone()) } else { - if let Some(pb) = li.as_any().downcast_ref::() { - if pb.box_type == "StringBox" { - let host = crate::runtime::get_global_plugin_host(); - 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 Some(vb) = val_opt { if let Some(sbb) = vb.as_any().downcast_ref::() { Some(sbb.value.clone()) } else { None } } else { None } - } else { None } - } else { None }; - tmp - } 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))) + let ls_opt = + if let Some(sb) = li.as_any().downcast_ref::() { + Some(sb.value.clone()) + } else { + if let Some(pb) = li + .as_any() + .downcast_ref::() + { + if pb.box_type == "StringBox" { + let host = crate::runtime::get_global_plugin_host(); + 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 Some(vb) = val_opt { + if let Some(sbb) = + vb.as_any() + .downcast_ref::() + { + Some(sbb.value.clone()) + } else { + None + } + } else { + None + } + } else { + None + } + } else { + None + }; + tmp + } 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) (VMValue::BoxRef(li), VMValue::Integer(r)) => { - let l_opt = li.as_any().downcast_ref::().map(|x| x.value) - .or_else(|| li.as_any().downcast_ref::().map(|x| x.value)) + let l_opt = li + .as_any() + .downcast_ref::() + .map(|x| x.value) + .or_else(|| { + li.as_any() + .downcast_ref::() + .map(|x| x.value) + }) .or_else(|| li.to_string_box().value.parse::().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 }); } - Err(VMError::TypeError(format!("[BoxRef-Integer] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))) + 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, + }); + } + Err(VMError::TypeError(format!( + "[BoxRef-Integer] Unsupported comparison: {:?} on {:?} and {:?}", + op, left, right + ))) } (VMValue::Integer(l), VMValue::BoxRef(ri)) => { - let r_opt = ri.as_any().downcast_ref::().map(|x| x.value) - .or_else(|| ri.as_any().downcast_ref::().map(|x| x.value)) + let r_opt = ri + .as_any() + .downcast_ref::() + .map(|x| x.value) + .or_else(|| { + ri.as_any() + .downcast_ref::() + .map(|x| x.value) + }) .or_else(|| ri.to_string_box().value.parse::().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 }); } - Err(VMError::TypeError(format!("[Integer-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))) + 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, + }); + } + Err(VMError::TypeError(format!( + "[Integer-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}", + op, left, right + ))) } _ => { // 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)) { - 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") { - let lty = match left { VMValue::BoxRef(b) => format!("BoxRef({})", b.type_name()), 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); + let lty = match left { + VMValue::BoxRef(b) => format!("BoxRef({})", b.type_name()), + 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 + ))) + } } } } diff --git a/src/backend/wasm/codegen.rs b/src/backend/wasm/codegen.rs index dbf0c42f..4648ae94 100644 --- a/src/backend/wasm/codegen.rs +++ b/src/backend/wasm/codegen.rs @@ -1,12 +1,14 @@ /*! * WASM Code Generation - Core MIR to WASM instruction conversion - * + * * Phase 8.2 PoC1: Basic operations (arithmetic, control flow, print) * Phase 8.3 PoC2: Reference operations (RefNew/RefGet/RefSet) */ -use crate::mir::{MirModule, MirFunction, MirInstruction, ConstValue, BinaryOp, CompareOp, ValueId, BasicBlockId}; -use super::{WasmError, MemoryManager, RuntimeImports}; +use super::{MemoryManager, RuntimeImports, WasmError}; +use crate::mir::{ + BasicBlockId, BinaryOp, CompareOp, ConstValue, MirFunction, MirInstruction, MirModule, ValueId, +}; use std::collections::HashMap; /// WASM module representation for WAT generation @@ -30,42 +32,42 @@ impl WasmModule { exports: Vec::new(), } } - + /// Generate WAT text format pub fn to_wat(&self) -> String { let mut wat = String::new(); wat.push_str("(module\n"); - + // Add imports first (must come before other definitions in WASM) for import in &self.imports { wat.push_str(&format!(" {}\n", import)); } - - // Add memory declaration + + // Add memory declaration if !self.memory.is_empty() { wat.push_str(&format!(" {}\n", self.memory)); } - + // Add data segments (must come after memory) for data_segment in &self.data_segments { wat.push_str(&format!(" {}\n", data_segment)); } - + // Add globals for global in &self.globals { wat.push_str(&format!(" {}\n", global)); } - + // Add functions for function in &self.functions { wat.push_str(&format!(" {}\n", function)); } - + // Add exports for export in &self.exports { wat.push_str(&format!(" {}\n", export)); } - + wat.push_str(")\n"); wat } @@ -90,72 +92,87 @@ impl WasmCodegen { next_data_offset: 0x1000, // Start data after initial heap space } } - + /// Generate WASM module from MIR module pub fn generate_module( - &mut self, - mir_module: MirModule, - memory_manager: &MemoryManager, - runtime: &RuntimeImports + &mut self, + mir_module: MirModule, + memory_manager: &MemoryManager, + runtime: &RuntimeImports, ) -> Result { let mut wasm_module = WasmModule::new(); - + // Add memory declaration (64KB initial) wasm_module.memory = "(memory (export \"memory\") 1)".to_string(); - + // Add runtime imports (env.print for debugging) wasm_module.imports.extend(runtime.get_imports()); - + // Add globals (heap pointer) wasm_module.globals.extend(memory_manager.get_globals()); - + // Add memory management functions - wasm_module.functions.push(memory_manager.get_malloc_function()); - wasm_module.functions.push(memory_manager.get_generic_box_alloc_function()); - + wasm_module + .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 for box_type in ["StringBox", "IntegerBox", "BoolBox", "DataBox"] { if let Ok(alloc_func) = memory_manager.get_box_alloc_function(box_type) { wasm_module.functions.push(alloc_func); } } - + // Generate functions for (name, function) in &mir_module.functions { let wasm_function = self.generate_function(name, function.clone())?; wasm_module.functions.push(wasm_function); } - + // 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 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) } - + /// Generate WASM function from MIR function - fn generate_function(&mut self, name: &str, mir_function: MirFunction) -> Result { + fn generate_function( + &mut self, + name: &str, + mir_function: MirFunction, + ) -> Result { // Reset local variable tracking for this function self.current_locals.clear(); self.next_local_index = 0; - + let mut function_body = String::new(); function_body.push_str(&format!("(func ${}", name)); - + // Add return type if not void match mir_function.signature.return_type { crate::mir::MirType::Integer => function_body.push_str(" (result i32)"), crate::mir::MirType::Bool => function_body.push_str(" (result i32)"), - crate::mir::MirType::Void => {}, // No return type - _ => return Err(WasmError::UnsupportedInstruction( - format!("Unsupported return type: {:?}", mir_function.signature.return_type) - )), + crate::mir::MirType::Void => {} // No return type + _ => { + return Err(WasmError::UnsupportedInstruction(format!( + "Unsupported return type: {:?}", + mir_function.signature.return_type + ))) + } } - + // Collect all local variables needed let local_count = self.count_locals(&mir_function)?; if local_count > 0 { @@ -164,23 +181,24 @@ impl WasmCodegen { function_body.push_str(&format!(" (local ${} i32)", i)); } } - + function_body.push('\n'); - + // 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 { function_body.push_str(&format!(" {}\n", instruction)); } - + function_body.push_str(" )"); Ok(function_body) } - + /// Count local variables needed for the function fn count_locals(&mut self, mir_function: &MirFunction) -> Result { let mut max_value_id = 0; - + for (_, block) in &mir_function.blocks { for instruction in &block.instructions { if let Some(value_id) = instruction.dst_value() { @@ -191,63 +209,65 @@ impl WasmCodegen { } } } - + // Assign local indices to value IDs for i in 0..=max_value_id { let value_id = ValueId::new(i); self.current_locals.insert(value_id, self.next_local_index); self.next_local_index += 1; } - + Ok(self.next_local_index) } - + /// Generate WASM instructions for a basic block - fn generate_basic_block(&mut self, mir_function: &MirFunction, block_id: BasicBlockId) -> Result, WasmError> { - let block = mir_function.blocks.get(&block_id) - .ok_or_else(|| WasmError::CodegenError(format!("Basic block {:?} not found", block_id)))?; - + fn generate_basic_block( + &mut self, + mir_function: &MirFunction, + block_id: BasicBlockId, + ) -> Result, 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(); - + // Process regular instructions for mir_instruction in &block.instructions { let wasm_instructions = self.generate_instruction(mir_instruction)?; instructions.extend(wasm_instructions); } - + // Process terminator instruction if let Some(ref terminator) = block.terminator { let wasm_instructions = self.generate_instruction(terminator)?; instructions.extend(wasm_instructions); } - + Ok(instructions) } - + /// Generate WASM instructions for a single MIR instruction - fn generate_instruction(&mut self, instruction: &MirInstruction) -> Result, WasmError> { + fn generate_instruction( + &mut self, + instruction: &MirInstruction, + ) -> Result, WasmError> { match instruction { // Phase 8.2 PoC1: Basic operations - MirInstruction::Const { dst, value } => { - self.generate_const(*dst, value) - }, - + MirInstruction::Const { dst, value } => self.generate_const(*dst, value), + MirInstruction::BinOp { dst, op, lhs, rhs } => { self.generate_binop(*dst, *op, *lhs, *rhs) - }, - + } + MirInstruction::Compare { dst, op, lhs, rhs } => { self.generate_compare(*dst, *op, *lhs, *rhs) - }, - - MirInstruction::Return { value } => { - self.generate_return(value.as_ref()) - }, - - MirInstruction::Print { value, .. } => { - self.generate_print(*value) - }, - + } + + MirInstruction::Return { value } => self.generate_return(value.as_ref()), + + MirInstruction::Print { value, .. } => self.generate_print(*value), + // Phase 8.3 PoC2: Reference operations MirInstruction::RefNew { dst, box_val } => { // Create a new reference to a Box by copying the Box value @@ -256,9 +276,13 @@ impl WasmCodegen { format!("local.get ${}", self.get_local_index(*box_val)?), 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 // reference contains Box pointer, field is the field name // For now, assume all fields are at offset 12 (first field after header) @@ -270,10 +294,14 @@ impl WasmCodegen { "i32.load".to_string(), format!("local.set ${}", self.get_local_index(*dst)?), ]) - }, - - MirInstruction::RefSet { reference, field: _, value } => { - // Store field value to Box through reference + } + + MirInstruction::RefSet { + reference, + field: _, + value, + } => { + // Store field value to Box through reference // 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) // TODO: Add proper field offset calculation @@ -284,9 +312,13 @@ impl WasmCodegen { format!("local.get ${}", self.get_local_index(*value)?), "i32.store".to_string(), ]) - }, - - MirInstruction::NewBox { dst, box_type, args } => { + } + + MirInstruction::NewBox { + dst, + box_type, + args, + } => { // Create a new Box using the generic allocator match box_type.as_str() { "DataBox" => { @@ -295,7 +327,7 @@ impl WasmCodegen { "call $alloc_databox".to_string(), format!("local.set ${}", self.get_local_index(*dst)?), ]; - + // Initialize fields with arguments if provided for (i, arg) in args.iter().enumerate() { instructions.extend(vec![ @@ -306,9 +338,9 @@ impl WasmCodegen { "i32.store".to_string(), ]); } - + Ok(instructions) - }, + } _ => { // Use generic allocator for unknown types // 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 - MirInstruction::WeakNew { dst, box_val } | - MirInstruction::FutureNew { dst, value: box_val } => { + MirInstruction::WeakNew { dst, box_val } + | MirInstruction::FutureNew { + dst, + value: box_val, + } => { // Treat as regular reference for now Ok(vec![ format!("local.get ${}", self.get_local_index(*box_val)?), format!("local.set ${}", self.get_local_index(*dst)?), ]) - }, - - MirInstruction::WeakLoad { dst, weak_ref } | - MirInstruction::Await { dst, future: weak_ref } => { + } + + MirInstruction::WeakLoad { dst, weak_ref } + | MirInstruction::Await { + dst, + future: weak_ref, + } => { // Always succeed for now Ok(vec![ format!("local.get ${}", self.get_local_index(*weak_ref)?), format!("local.set ${}", self.get_local_index(*dst)?), ]) - }, - - MirInstruction::BarrierRead { .. } | - MirInstruction::BarrierWrite { .. } | - MirInstruction::FutureSet { .. } | - MirInstruction::Safepoint => { + } + + MirInstruction::BarrierRead { .. } + | MirInstruction::BarrierWrite { .. } + | MirInstruction::FutureSet { .. } + | MirInstruction::Safepoint => { // No-op for now Ok(vec!["nop".to_string()]) - }, - + } + // Control Flow Instructions (Critical for loops and conditions) MirInstruction::Jump { target } => { // Unconditional jump to target basic block // Use WASM br instruction to break to the target block - Ok(vec![ - format!("br $block_{}", target.as_u32()), - ]) - }, - - MirInstruction::Branch { condition, then_bb, else_bb } => { + Ok(vec![format!("br $block_{}", target.as_u32())]) + } + + MirInstruction::Branch { + condition, + then_bb, + else_bb, + } => { // Conditional branch based on condition value // Load condition value and branch accordingly Ok(vec![ @@ -369,54 +409,73 @@ impl WasmCodegen { // Otherwise, fall through to else_bb format!("br $block_{}", else_bb.as_u32()), ]) - }, - + } + // 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 let call_target = match (iface_name.as_str(), method_name.as_str()) { ("env.console", "log") => "console_log", - ("env.canvas", "fillRect") => "canvas_fillRect", + ("env.canvas", "fillRect") => "canvas_fillRect", ("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(); - + // Load all arguments onto stack in order for arg in args { instructions.push(format!("local.get ${}", self.get_local_index(*arg)?)); } - + // Call the external function instructions.push(format!("call ${}", call_target)); - + // Store result if destination is provided if let Some(dst) = dst { // For void functions, we still need to provide a dummy value instructions.push("i32.const 0".to_string()); // Void result instructions.push(format!("local.set ${}", self.get_local_index(*dst)?)); } - + Ok(instructions) - }, - + } + // BoxCall codegen - critical Box method calls - MirInstruction::BoxCall { dst, box_val, method, args, effects: _ , .. } => { - self.generate_box_call(*dst, *box_val, method, args) - }, - + MirInstruction::BoxCall { + dst, + box_val, + method, + args, + effects: _, + .. + } => self.generate_box_call(*dst, *box_val, method, args), + // Unsupported instructions - _ => Err(WasmError::UnsupportedInstruction( - format!("Instruction not yet supported: {:?}", instruction) - )), + _ => Err(WasmError::UnsupportedInstruction(format!( + "Instruction not yet supported: {:?}", + instruction + ))), } } - + /// Generate constant loading - fn generate_const(&mut self, dst: ValueId, value: &ConstValue) -> Result, WasmError> { + fn generate_const( + &mut self, + dst: ValueId, + value: &ConstValue, + ) -> Result, WasmError> { let const_instruction = match value { ConstValue::Integer(n) => format!("i32.const {}", n), ConstValue::Bool(b) => format!("i32.const {}", if *b { 1 } else { 0 }), @@ -425,36 +484,48 @@ impl WasmCodegen { // Register the string literal and get its offset let data_offset = self.register_string_literal(s); let string_len = s.len() as u32; - + // Generate code to allocate a StringBox and return its pointer // This is more complex and will need StringBox allocation 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![ const_instruction, format!("local.set ${}", self.get_local_index(dst)?), ]) } - + /// Generate binary operation - fn generate_binop(&self, dst: ValueId, op: BinaryOp, lhs: ValueId, rhs: ValueId) -> Result, WasmError> { + fn generate_binop( + &self, + dst: ValueId, + op: BinaryOp, + lhs: ValueId, + rhs: ValueId, + ) -> Result, WasmError> { let wasm_op = match op { BinaryOp::Add => "i32.add", - BinaryOp::Sub => "i32.sub", + BinaryOp::Sub => "i32.sub", BinaryOp::Mul => "i32.mul", BinaryOp::Div => "i32.div_s", BinaryOp::And => "i32.and", BinaryOp::Or => "i32.or", - _ => return Err(WasmError::UnsupportedInstruction( - format!("Unsupported binary operation: {:?}", op) - )), + _ => { + return Err(WasmError::UnsupportedInstruction(format!( + "Unsupported binary operation: {:?}", + op + ))) + } }; - + Ok(vec![ format!("local.get ${}", self.get_local_index(lhs)?), format!("local.get ${}", self.get_local_index(rhs)?), @@ -462,9 +533,15 @@ impl WasmCodegen { format!("local.set ${}", self.get_local_index(dst)?), ]) } - + /// Generate comparison operation - fn generate_compare(&self, dst: ValueId, op: CompareOp, lhs: ValueId, rhs: ValueId) -> Result, WasmError> { + fn generate_compare( + &self, + dst: ValueId, + op: CompareOp, + lhs: ValueId, + rhs: ValueId, + ) -> Result, WasmError> { let wasm_op = match op { CompareOp::Eq => "i32.eq", CompareOp::Ne => "i32.ne", @@ -473,7 +550,7 @@ impl WasmCodegen { CompareOp::Gt => "i32.gt_s", CompareOp::Ge => "i32.ge_s", }; - + Ok(vec![ format!("local.get ${}", self.get_local_index(lhs)?), format!("local.get ${}", self.get_local_index(rhs)?), @@ -481,7 +558,7 @@ impl WasmCodegen { format!("local.set ${}", self.get_local_index(dst)?), ]) } - + /// Generate return instruction fn generate_return(&self, value: Option<&ValueId>) -> Result, WasmError> { if let Some(value_id) = value { @@ -493,9 +570,14 @@ impl WasmCodegen { Ok(vec!["return".to_string()]) } } - + /// Generate StringBox allocation for a string constant - fn generate_string_box_const(&self, dst: ValueId, data_offset: u32, string_len: u32) -> Result, WasmError> { + fn generate_string_box_const( + &self, + dst: ValueId, + data_offset: u32, + string_len: u32, + ) -> Result, WasmError> { // Allocate a StringBox using the StringBox allocator // StringBox layout: [type_id:0x1001][ref_count:1][field_count:2][data_ptr:offset][length:len] Ok(vec![ @@ -503,7 +585,6 @@ impl WasmCodegen { "call $alloc_stringbox".to_string(), // Store the result (StringBox pointer) in local variable format!("local.set ${}", self.get_local_index(dst)?), - // Initialize StringBox fields // Get StringBox pointer back format!("local.get ${}", self.get_local_index(dst)?), @@ -512,8 +593,7 @@ impl WasmCodegen { "i32.add".to_string(), format!("i32.const {}", data_offset), "i32.store".to_string(), - - // Get StringBox pointer again + // Get StringBox pointer again format!("local.get ${}", self.get_local_index(dst)?), // Set length field (offset 16 from StringBox pointer) "i32.const 16".to_string(), @@ -522,7 +602,7 @@ impl WasmCodegen { "i32.store".to_string(), ]) } - + /// Generate print instruction (calls env.print import) fn generate_print(&self, value: ValueId) -> Result, WasmError> { Ok(vec![ @@ -530,89 +610,112 @@ impl WasmCodegen { "call $print".to_string(), ]) } - + /// Register a string literal and return its data offset fn register_string_literal(&mut self, string: &str) -> u32 { if let Some(&offset) = self.string_literals.get(string) { return offset; } - + let offset = self.next_data_offset; let string_bytes = string.as_bytes(); self.string_literals.insert(string.to_string(), offset); self.next_data_offset += string_bytes.len() as u32; - + offset } - + /// Generate data segments for all registered string literals fn generate_data_segments(&self) -> Vec { let mut segments = Vec::new(); - + for (string, &offset) in &self.string_literals { let string_bytes = string.as_bytes(); - + // Convert to hex-escaped string for WAT - let byte_string = string_bytes.iter() + let byte_string = string_bytes + .iter() .map(|b| format!("\\{:02x}", b)) .collect::(); - - let data_segment = format!( - "(data (i32.const {}) \"{}\")", - offset, - byte_string - ); - + + let data_segment = format!("(data (i32.const {}) \"{}\")", offset, byte_string); + segments.push(data_segment); } - + segments } - + /// Get WASM local variable index for ValueId fn get_local_index(&self, value_id: ValueId) -> Result { - self.current_locals.get(&value_id) - .copied() - .ok_or_else(|| WasmError::CodegenError(format!("Local variable not found for ValueId: {:?}", value_id))) + self.current_locals.get(&value_id).copied().ok_or_else(|| { + WasmError::CodegenError(format!( + "Local variable not found for ValueId: {:?}", + value_id + )) + }) } - + /// Generate BoxCall method invocation /// Implements critical Box methods: toString, print, equals, clone - fn generate_box_call(&mut self, dst: Option, box_val: ValueId, method: &str, args: &[ValueId]) -> Result, WasmError> { + fn generate_box_call( + &mut self, + dst: Option, + box_val: ValueId, + method: &str, + args: &[ValueId], + ) -> Result, WasmError> { match method { "toString" => self.generate_to_string_call(dst, box_val), "print" => self.generate_print_call(dst, box_val), "equals" => self.generate_equals_call(dst, box_val, args), "clone" => self.generate_clone_call(dst, box_val), "log" => self.generate_log_call(dst, box_val, args), - _ => Err(WasmError::UnsupportedInstruction( - format!("Unsupported BoxCall method: {}", method) - )) + _ => Err(WasmError::UnsupportedInstruction(format!( + "Unsupported BoxCall method: {}", + method + ))), } } - + /// Generate toString() method call - Box → String conversion - fn generate_to_string_call(&mut self, dst: Option, box_val: ValueId) -> Result, WasmError> { + fn generate_to_string_call( + &mut self, + dst: Option, + box_val: ValueId, + ) -> Result, WasmError> { 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![ - 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)?), "call $box_to_string".to_string(), format!("local.set ${}", self.get_local_index(dst)?), ]) } - + /// Generate print() method call - Basic output - fn generate_print_call(&mut self, dst: Option, box_val: ValueId) -> Result, WasmError> { + fn generate_print_call( + &mut self, + dst: Option, + box_val: ValueId, + ) -> Result, WasmError> { 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)?), "call $box_print".to_string(), ]; - + // Store void result if destination is provided if let Some(dst) = dst { instructions.extend(vec![ @@ -620,62 +723,89 @@ impl WasmCodegen { format!("local.set ${}", self.get_local_index(dst)?), ]); } - + Ok(instructions) } - + /// Generate equals() method call - Box comparison - fn generate_equals_call(&mut self, dst: Option, box_val: ValueId, args: &[ValueId]) -> Result, WasmError> { + fn generate_equals_call( + &mut self, + dst: Option, + box_val: ValueId, + args: &[ValueId], + ) -> Result, WasmError> { let Some(dst) = dst else { - return Err(WasmError::CodegenError("equals() requires destination".to_string())); - }; - - if args.len() != 1 { return Err(WasmError::CodegenError( - format!("equals() expects 1 argument, got {}", args.len()) + "equals() requires destination".to_string(), )); + }; + + if args.len() != 1 { + return Err(WasmError::CodegenError(format!( + "equals() expects 1 argument, got {}", + args.len() + ))); } - + 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(args[0])?), "call $box_equals".to_string(), format!("local.set ${}", self.get_local_index(dst)?), ]) } - + /// Generate clone() method call - Box duplication - fn generate_clone_call(&mut self, dst: Option, box_val: ValueId) -> Result, WasmError> { + fn generate_clone_call( + &mut self, + dst: Option, + box_val: ValueId, + ) -> Result, WasmError> { 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![ - 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)?), "call $box_clone".to_string(), format!("local.set ${}", self.get_local_index(dst)?), ]) } - + /// Generate log() method call - Console logging (ConsoleBox.log) - fn generate_log_call(&mut self, dst: Option, box_val: ValueId, args: &[ValueId]) -> Result, WasmError> { - let mut instructions = vec![ - format!(";; log() implementation for ValueId({})", box_val.as_u32()), - ]; - + fn generate_log_call( + &mut self, + dst: Option, + box_val: ValueId, + args: &[ValueId], + ) -> Result, WasmError> { + let mut instructions = vec![format!( + ";; log() implementation for ValueId({})", + box_val.as_u32() + )]; + // Load box_val (ConsoleBox instance) instructions.push(format!("local.get ${}", self.get_local_index(box_val)?)); - + // Load all arguments for arg in args { instructions.push(format!("local.get ${}", self.get_local_index(*arg)?)); } - + // Call console log function instructions.push("call $console_log".to_string()); - + // Store void result if destination is provided if let Some(dst) = dst { instructions.extend(vec![ @@ -683,7 +813,7 @@ impl WasmCodegen { format!("local.set ${}", self.get_local_index(dst)?), ]); } - + Ok(instructions) } } @@ -691,25 +821,30 @@ impl WasmCodegen { #[cfg(test)] mod tests { 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] fn test_wasm_module_wat_generation() { let mut module = WasmModule::new(); 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(); assert!(wat.contains("(module")); assert!(wat.contains("memory")); assert!(wat.contains("import")); } - + #[test] fn test_constant_generation() { let mut codegen = WasmCodegen::new(); let dst = ValueId::new(0); - + let result = codegen.generate_const(dst, &ConstValue::Integer(42)); assert!(result.is_err()); // Should fail without local mapping } diff --git a/src/backend/wasm/memory.rs b/src/backend/wasm/memory.rs index 3083ce57..50891216 100644 --- a/src/backend/wasm/memory.rs +++ b/src/backend/wasm/memory.rs @@ -1,6 +1,6 @@ /*! * WASM Memory Management - Box layout and heap allocation - * + * * Phase 8.3 PoC2: Implements bump allocator and Box memory layout * Memory Layout: 0x000-0x3FF (reserved), 0x400-0x7FF (stack), 0x800+ (heap) */ @@ -24,26 +24,26 @@ impl BoxLayout { "IntegerBox" => 0x1002, "BoolBox" => 0x1003, "ArrayBox" => 0x1004, - "DataBox" => 0x1005, // For testing + "DataBox" => 0x1005, // For testing _ => { // Generate ID from hash for custom types type_name.chars().map(|c| c as u32).sum::() % 65536 + 0x2000 } }; - + Self { type_id, size: 12, // Header: type_id + ref_count + field_count field_offsets: HashMap::new(), } } - + pub fn add_field(&mut self, field_name: String) { let offset = self.size; self.field_offsets.insert(field_name, offset); self.size += 4; // Each field is 4 bytes (i32) } - + pub fn get_field_offset(&self, field_name: &str) -> Option { self.field_offsets.get(field_name).copied() } @@ -63,50 +63,54 @@ impl MemoryManager { box_layouts: HashMap::new(), heap_start: 0x800, // 2KB reserved for stack/globals }; - + // Register standard Box types manager.register_standard_box_types(); manager } - + /// Register standard built-in Box types fn register_standard_box_types(&mut self) { // 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] self.register_box_type("IntegerBox".to_string(), vec!["value".to_string()]); - + // BoolBox: [type_id][ref_count][field_count][value] self.register_box_type("BoolBox".to_string(), vec!["value".to_string()]); - + // DataBox: [type_id][ref_count][field_count][value] - for testing self.register_box_type("DataBox".to_string(), vec!["value".to_string()]); } - + /// Register a Box type layout pub fn register_box_type(&mut self, type_name: String, fields: Vec) { let mut layout = BoxLayout::new(&type_name); - + for field in fields { layout.add_field(field); } - + self.box_layouts.insert(type_name, layout); } - + /// Get Box layout by type name pub fn get_box_layout(&self, type_name: &str) -> Option<&BoxLayout> { self.box_layouts.get(type_name) } - + /// Generate WASM globals for heap management pub fn get_globals(&self) -> Vec { - vec![ - format!("(global $heap_ptr (mut i32) (i32.const {}))", self.heap_start), - ] + vec![format!( + "(global $heap_ptr (mut i32) (i32.const {}))", + self.heap_start + )] } - + /// Generate heap allocation function with 4-byte alignment pub fn get_malloc_function(&self) -> String { format!( @@ -137,12 +141,13 @@ impl MemoryManager { )"# ) } - + /// Generate Box allocation function for specific type pub fn get_box_alloc_function(&self, type_name: &str) -> Result { - 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(format!( r#"(func $alloc_{} (result i32) (local $ptr i32) @@ -180,15 +185,21 @@ impl MemoryManager { layout.field_offsets.len() )) } - + /// Generate field getter function - pub fn get_field_get_function(&self, type_name: &str, field_name: &str) -> Result { - let layout = self.get_box_layout(type_name) + pub fn get_field_get_function( + &self, + type_name: &str, + field_name: &str, + ) -> Result { + let layout = self + .get_box_layout(type_name) .ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?; - - let offset = layout.get_field_offset(field_name) - .ok_or_else(|| WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name)))?; - + + let offset = layout.get_field_offset(field_name).ok_or_else(|| { + WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name)) + })?; + Ok(format!( r#"(func $get_{}_{} (param $box_ptr i32) (result i32) ;; Verify type_id (optional safety check) @@ -213,15 +224,21 @@ impl MemoryManager { offset )) } - + /// Generate field setter function - pub fn get_field_set_function(&self, type_name: &str, field_name: &str) -> Result { - let layout = self.get_box_layout(type_name) + pub fn get_field_set_function( + &self, + type_name: &str, + field_name: &str, + ) -> Result { + let layout = self + .get_box_layout(type_name) .ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?; - - let offset = layout.get_field_offset(field_name) - .ok_or_else(|| WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name)))?; - + + let offset = layout.get_field_offset(field_name).ok_or_else(|| { + WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name)) + })?; + Ok(format!( r#"(func $set_{}_{} (param $box_ptr i32) (param $value i32) ;; Verify type_id (optional safety check) @@ -246,7 +263,7 @@ impl MemoryManager { offset )) } - + /// Get memory layout constants for documentation pub fn get_memory_layout_info(&self) -> String { format!( @@ -268,12 +285,12 @@ impl MemoryManager { self.heap_start ) } - + /// Get type ID for a Box type pub fn get_type_id(&self, type_name: &str) -> Option { self.box_layouts.get(type_name).map(|layout| layout.type_id) } - + /// Generate generic Box creation helper pub fn get_generic_box_alloc_function(&self) -> String { format!( @@ -323,7 +340,7 @@ impl MemoryManager { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_box_layout_creation() { let layout = BoxLayout::new("DataBox"); @@ -331,76 +348,79 @@ mod tests { assert_eq!(layout.type_id, 0x1005); // DataBox has specific ID assert!(layout.field_offsets.is_empty()); } - + #[test] fn test_box_layout_field_addition() { let mut layout = BoxLayout::new("DataBox"); layout.add_field("field1".to_string()); layout.add_field("field2".to_string()); - + assert_eq!(layout.size, 20); // 12 + 4 + 4 assert_eq!(layout.get_field_offset("field1"), Some(12)); assert_eq!(layout.get_field_offset("field2"), Some(16)); } - + #[test] fn test_memory_manager_standard_types() { let manager = MemoryManager::new(); - + // Verify standard types are registered assert!(manager.get_box_layout("StringBox").is_some()); assert!(manager.get_box_layout("IntegerBox").is_some()); assert!(manager.get_box_layout("BoolBox").is_some()); assert!(manager.get_box_layout("DataBox").is_some()); - + // Verify type IDs assert_eq!(manager.get_type_id("StringBox"), Some(0x1001)); assert_eq!(manager.get_type_id("IntegerBox"), Some(0x1002)); assert_eq!(manager.get_type_id("DataBox"), Some(0x1005)); } - + #[test] fn test_memory_manager_registration() { 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(); assert_eq!(layout.field_offsets.len(), 2); assert!(layout.get_field_offset("x").is_some()); assert!(layout.get_field_offset("y").is_some()); assert!(layout.type_id >= 0x2000); // Custom types start at 0x2000 } - + #[test] fn test_malloc_function_generation() { let manager = MemoryManager::new(); let malloc_func = manager.get_malloc_function(); - + assert!(malloc_func.contains("$malloc")); assert!(malloc_func.contains("$heap_ptr")); assert!(malloc_func.contains("global.get")); assert!(malloc_func.contains("i32.and")); // Alignment check } - + #[test] fn test_box_alloc_function_generation() { let manager = MemoryManager::new(); let alloc_func = manager.get_box_alloc_function("DataBox").unwrap(); - + assert!(alloc_func.contains("$alloc_databox")); assert!(alloc_func.contains("call $malloc")); assert!(alloc_func.contains("4101")); // 0x1005 type ID for DataBox assert!(alloc_func.contains("i32.const 1")); // ref_count initialization } - + #[test] fn test_generic_box_alloc_function() { let manager = MemoryManager::new(); let generic_func = manager.get_generic_box_alloc_function(); - + assert!(generic_func.contains("$box_alloc")); assert!(generic_func.contains("$type_id")); assert!(generic_func.contains("$field_count")); assert!(generic_func.contains("i32.const 12")); // Header size } -} \ No newline at end of file +} diff --git a/src/backend/wasm/mod.rs b/src/backend/wasm/mod.rs index bdb39474..8b115118 100644 --- a/src/backend/wasm/mod.rs +++ b/src/backend/wasm/mod.rs @@ -1,6 +1,6 @@ /*! * WASM Backend - Phase 8 Implementation - * + * * Converts MIR instructions to WebAssembly for sandboxed execution * Targets browser execution and wasmtime runtime */ @@ -11,7 +11,7 @@ mod runtime; // mod executor; // TODO: Fix WASM executor build errors pub use codegen::{WasmCodegen, WasmModule}; -pub use memory::{MemoryManager, BoxLayout}; +pub use memory::{BoxLayout, MemoryManager}; pub use runtime::RuntimeImports; // pub use executor::WasmExecutor; // TODO: Fix WASM executor build errors @@ -57,98 +57,120 @@ impl WasmBackend { runtime: RuntimeImports::new(), } } - + /// Compile MIR module to WASM bytes pub fn compile_module(&mut self, mir_module: MirModule) -> Result, WasmError> { // Generate WAT (WebAssembly Text) first for debugging let wat_text = self.compile_to_wat(mir_module)?; - + // Phase 9.77 Task 1.3: Fix UTF-8 encoding error in WAT→WASM conversion self.wat_to_wasm(&wat_text) } - + /// Convert WAT text to WASM binary with proper UTF-8 handling fn wat_to_wasm(&self, wat_source: &str) -> Result, WasmError> { // Debug: Print WAT source for analysis eprintln!("🔍 WAT Source Debug (length: {}):", wat_source.len()); eprintln!("WAT Content:\n{}", wat_source); - + // UTF-8 validation to prevent encoding errors if !wat_source.is_ascii() { eprintln!("❌ WAT source contains non-ASCII characters"); return Err(WasmError::WasmValidationError( - "WAT source contains non-ASCII characters".to_string() + "WAT source contains non-ASCII characters".to_string(), )); } - + eprintln!("✅ WAT source is ASCII-compatible"); - + // Convert to bytes as required by wabt::wat2wasm eprintln!("🔄 Converting WAT to WASM bytes..."); - let wasm_bytes = wabt::wat2wasm(wat_source.as_bytes()) - .map_err(|e| { - eprintln!("❌ wabt::wat2wasm failed: {}", e); - WasmError::WasmValidationError(format!("WAT to WASM conversion failed: {}", e)) - })?; - - eprintln!("✅ WASM conversion successful, {} bytes generated", wasm_bytes.len()); + let wasm_bytes = wabt::wat2wasm(wat_source.as_bytes()).map_err(|e| { + eprintln!("❌ wabt::wat2wasm failed: {}", e); + WasmError::WasmValidationError(format!("WAT to WASM conversion failed: {}", e)) + })?; + + eprintln!( + "✅ WASM conversion successful, {} bytes generated", + wasm_bytes.len() + ); Ok(wasm_bytes) } - + /// Compile MIR module to WAT text format (for debugging) pub fn compile_to_wat(&mut self, mir_module: MirModule) -> Result { - 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()) } - + /// Execute WASM bytes using wasmtime (for testing) pub fn execute_wasm(&self, wasm_bytes: &[u8]) -> Result { let engine = wasmtime::Engine::default(); - let module = wasmtime::Module::new(&engine, wasm_bytes) - .map_err(|e| WasmError::WasmValidationError(format!("Module creation failed: {}", e)))?; - + let module = wasmtime::Module::new(&engine, wasm_bytes).map_err(|e| { + WasmError::WasmValidationError(format!("Module creation failed: {}", e)) + })?; + let mut store = wasmtime::Store::new(&engine, ()); - + // Create print function import let print_func = wasmtime::Func::wrap(&mut store, |value: i32| { println!("{}", value); }); - + // 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 memory = caller.get_export("memory") - .and_then(|export| export.into_memory()) - .ok_or_else(|| wasmtime::Error::msg("Memory export not found"))?; - - let data = memory.data(&caller); - let start = ptr as usize; - let end = start + len as usize; - - if end <= data.len() { - let bytes = &data[start..end]; - if let Ok(s) = std::str::from_utf8(bytes) { - println!("String: {}", s); + let print_str_func = wasmtime::Func::wrap( + &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()) + .ok_or_else(|| wasmtime::Error::msg("Memory export not found"))?; + + let data = memory.data(&caller); + let start = ptr as usize; + let end = start + len as usize; + + if end <= data.len() { + let bytes = &data[start..end]; + if let Ok(s) = std::str::from_utf8(bytes) { + println!("String: {}", s); + } else { + println!("Invalid UTF-8 bytes: {:?}", bytes); + } } else { - println!("Invalid UTF-8 bytes: {:?}", bytes); + println!( + "String out of bounds: ptr={}, len={}, memory_size={}", + ptr, + len, + data.len() + ); } - } else { - 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 instance = wasmtime::Instance::new(&mut store, &module, &imports) - .map_err(|e| WasmError::WasmValidationError(format!("Instance creation failed: {}", e)))?; - + let instance = wasmtime::Instance::new(&mut store, &module, &imports).map_err(|e| { + WasmError::WasmValidationError(format!("Instance creation failed: {}", e)) + })?; + // Call main function - let main_func = instance.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 main_func = instance + .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, ()) .map_err(|e| WasmError::WasmValidationError(format!("Execution failed: {}", e)))?; - + Ok(result) } } @@ -163,21 +185,21 @@ impl Default for WasmBackend { mod tests { use super::*; use crate::mir::MirModule; - + #[test] fn test_backend_creation() { let _backend = WasmBackend::new(); // Should not panic assert!(true); } - + #[test] fn test_empty_module_compilation() { let mut backend = WasmBackend::new(); let module = MirModule::new("test".to_string()); - + // Should handle empty module gracefully let result = backend.compile_to_wat(module); assert!(result.is_ok()); } -} \ No newline at end of file +} diff --git a/src/backend/wasm/runtime.rs b/src/backend/wasm/runtime.rs index df73b82c..c382d146 100644 --- a/src/backend/wasm/runtime.rs +++ b/src/backend/wasm/runtime.rs @@ -1,6 +1,6 @@ /*! * WASM Runtime Imports - External function imports for WASM modules - * + * * Phase 8.2 PoC1: Implements env.print import for debugging * Future: Additional runtime functions (file I/O, network, etc.) */ @@ -27,12 +27,12 @@ impl RuntimeImports { let mut runtime = Self { imports: Vec::new(), }; - + // Add standard runtime imports runtime.add_standard_imports(); runtime } - + /// Add standard runtime function imports fn add_standard_imports(&mut self) { // env.print for debugging output @@ -42,7 +42,7 @@ impl RuntimeImports { params: vec!["i32".to_string()], result: None, }); - + // env.print_str for string debugging (ptr, len) self.imports.push(ImportFunction { module: "env".to_string(), @@ -50,9 +50,9 @@ impl RuntimeImports { params: vec!["i32".to_string(), "i32".to_string()], result: None, }); - + // Phase 9.7: Box FFI/ABI imports per BID specifications - + // env.console_log for console.log(message) - (string_ptr, string_len) self.imports.push(ImportFunction { module: "env".to_string(), @@ -60,37 +60,47 @@ impl RuntimeImports { params: vec!["i32".to_string(), "i32".to_string()], result: None, }); - + // env.canvas_fillRect for canvas.fillRect(canvas_id, x, y, w, h, color) // Parameters: (canvas_id_ptr, canvas_id_len, x, y, w, h, color_ptr, color_len) self.imports.push(ImportFunction { module: "env".to_string(), name: "canvas_fillRect".to_string(), params: vec![ - "i32".to_string(), "i32".to_string(), // canvas_id (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) + "i32".to_string(), + "i32".to_string(), // canvas_id (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, }); - + // env.canvas_fillText for canvas.fillText(canvas_id, text, x, y, font, color) // Parameters: (canvas_id_ptr, canvas_id_len, text_ptr, text_len, x, y, font_ptr, font_len, color_ptr, color_len) self.imports.push(ImportFunction { module: "env".to_string(), name: "canvas_fillText".to_string(), params: vec![ - "i32".to_string(), "i32".to_string(), // canvas_id (ptr, len) - "i32".to_string(), "i32".to_string(), // text (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) + "i32".to_string(), + "i32".to_string(), // canvas_id (ptr, len) + "i32".to_string(), + "i32".to_string(), // text (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, }); - + // Phase 9.77: BoxCall runtime functions - + // box_to_string - Convert any Box to string representation self.imports.push(ImportFunction { module: "env".to_string(), @@ -98,7 +108,7 @@ impl RuntimeImports { params: vec!["i32".to_string()], // box_ptr result: Some("i32".to_string()), // string_box_ptr }); - + // box_print - Print any Box to console self.imports.push(ImportFunction { module: "env".to_string(), @@ -106,15 +116,15 @@ impl RuntimeImports { params: vec!["i32".to_string()], // box_ptr result: None, }); - + // box_equals - Compare two Boxes for equality self.imports.push(ImportFunction { module: "env".to_string(), name: "box_equals".to_string(), params: vec!["i32".to_string(), "i32".to_string()], // box1_ptr, box2_ptr - result: Some("i32".to_string()), // bool result + result: Some("i32".to_string()), // bool result }); - + // box_clone - Clone a Box self.imports.push(ImportFunction { module: "env".to_string(), @@ -122,39 +132,44 @@ impl RuntimeImports { params: vec!["i32".to_string()], // box_ptr result: Some("i32".to_string()), // cloned_box_ptr }); - + // Future: env.file_read, env.file_write for file I/O // Future: env.http_request for network access } - + /// Get all import declarations in WAT format pub fn get_imports(&self) -> Vec { - self.imports.iter().map(|import| { - let params = if import.params.is_empty() { - String::new() - } else { - format!("(param {})", import.params.join(" ")) - }; - - let result = if let Some(ref result_type) = import.result { - format!("(result {})", result_type) - } else { - String::new() - }; - - format!( - "(import \"{}\" \"{}\" (func ${} {} {}))", - import.module, - import.name, - import.name, - params, - result - ) - }).collect() + self.imports + .iter() + .map(|import| { + let params = if import.params.is_empty() { + String::new() + } else { + format!("(param {})", import.params.join(" ")) + }; + + let result = if let Some(ref result_type) = import.result { + format!("(result {})", result_type) + } else { + String::new() + }; + + format!( + "(import \"{}\" \"{}\" (func ${} {} {}))", + import.module, import.name, import.name, params, result + ) + }) + .collect() } - + /// Add custom import function - pub fn add_import(&mut self, module: String, name: String, params: Vec, result: Option) { + pub fn add_import( + &mut self, + module: String, + name: String, + params: Vec, + result: Option, + ) { self.imports.push(ImportFunction { module, name, @@ -162,50 +177,54 @@ impl RuntimeImports { result, }); } - + /// Check if an import is available pub fn has_import(&self, name: &str) -> bool { self.imports.iter().any(|import| import.name == name) } - + /// Get import function by name pub fn get_import(&self, name: &str) -> Option<&ImportFunction> { self.imports.iter().find(|import| import.name == name) } - + /// Generate JavaScript import object for browser execution pub fn get_js_import_object(&self) -> String { let mut js = String::new(); js.push_str("const importObject = {\n"); - + // Group by module - let mut modules: std::collections::HashMap> = std::collections::HashMap::new(); + let mut modules: std::collections::HashMap> = + std::collections::HashMap::new(); 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 { js.push_str(&format!(" {}: {{\n", module_name)); - + for function in functions { match function.name.as_str() { "print" => { js.push_str(" print: (value) => console.log(value),\n"); - }, + } "print_str" => { js.push_str(" print_str: (ptr, len) => {\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(" console.log(str);\n"); js.push_str(" },\n"); - }, + } "console_log" => { js.push_str(" console_log: (ptr, len) => {\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(" console.log(str);\n"); js.push_str(" },\n"); - }, + } "canvas_fillRect" => { js.push_str(" canvas_fillRect: (canvasIdPtr, canvasIdLen, x, y, w, h, colorPtr, colorLen) => {\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(" }\n"); js.push_str(" },\n"); - }, + } "canvas_fillText" => { 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"); @@ -234,49 +253,56 @@ impl RuntimeImports { js.push_str(" ctx.fillText(text, x, y);\n"); js.push_str(" }\n"); js.push_str(" },\n"); - }, + } _ => { - js.push_str(&format!(" {}: () => {{ throw new Error('Not implemented: {}'); }},\n", - function.name, function.name)); + js.push_str(&format!( + " {}: () => {{ throw new Error('Not implemented: {}'); }},\n", + function.name, function.name + )); } } } - + js.push_str(" },\n"); } - + js.push_str("};\n"); js } - + /// Generate Rust wasmtime import bindings pub fn get_wasmtime_imports(&self) -> Result { let mut rust_code = String::new(); rust_code.push_str("// Wasmtime import bindings\n"); rust_code.push_str("let mut imports = Vec::new();\n\n"); - + for import in &self.imports { match import.name.as_str() { "print" => { - rust_code.push_str(r#" + rust_code.push_str( + r#" let print_func = wasmtime::Func::wrap(&mut store, |value: i32| { println!("{}", value); }); imports.push(print_func.into()); -"#); - }, +"#, + ); + } _ => { - rust_code.push_str(&format!(r#" + rust_code.push_str(&format!( + r#" // TODO: Implement {} import let {}_func = wasmtime::Func::wrap(&mut store, || {{ panic!("Not implemented: {}") }}); imports.push({}_func.into()); -"#, import.name, import.name, import.name, import.name)); +"#, + import.name, import.name, import.name, import.name + )); } } } - + Ok(rust_code) } } @@ -284,25 +310,25 @@ imports.push({}_func.into()); #[cfg(test)] mod tests { use super::*; - + #[test] fn test_runtime_imports_creation() { let runtime = RuntimeImports::new(); assert!(!runtime.imports.is_empty()); assert!(runtime.has_import("print")); } - + #[test] fn test_import_wat_generation() { let runtime = RuntimeImports::new(); let imports = runtime.get_imports(); - + assert!(!imports.is_empty()); assert!(imports[0].contains("import")); assert!(imports[0].contains("env")); assert!(imports[0].contains("print")); } - + #[test] fn test_custom_import_addition() { let mut runtime = RuntimeImports::new(); @@ -310,34 +336,34 @@ mod tests { "custom".to_string(), "test_func".to_string(), vec!["i32".to_string(), "i32".to_string()], - Some("i32".to_string()) + Some("i32".to_string()), ); - + assert!(runtime.has_import("test_func")); let import = runtime.get_import("test_func").unwrap(); assert_eq!(import.module, "custom"); assert_eq!(import.params.len(), 2); assert!(import.result.is_some()); } - + #[test] fn test_js_import_object_generation() { let runtime = RuntimeImports::new(); let js = runtime.get_js_import_object(); - + assert!(js.contains("importObject")); assert!(js.contains("env")); assert!(js.contains("print")); assert!(js.contains("console.log")); } - + #[test] fn test_wasmtime_imports_generation() { let runtime = RuntimeImports::new(); let rust_code = runtime.get_wasmtime_imports().unwrap(); - + assert!(rust_code.contains("wasmtime::Func::wrap")); assert!(rust_code.contains("print_func")); assert!(rust_code.contains("println!")); } -} \ No newline at end of file +} diff --git a/src/backend/wasm_v2/mod.rs b/src/backend/wasm_v2/mod.rs index e849fa2c..bd785b4e 100644 --- a/src/backend/wasm_v2/mod.rs +++ b/src/backend/wasm_v2/mod.rs @@ -13,14 +13,20 @@ use crate::box_trait::{NyashBox, StringBox}; use crate::boxes::ConsoleBox; /// WASM v2エントリポイント: 統一vtableディスパッチの最小テスト -pub fn compile_and_execute_v2(_module: &crate::mir::MirModule, _temp_name: &str) -> Result, String> { +pub fn compile_and_execute_v2( + _module: &crate::mir::MirModule, + _temp_name: &str, +) -> Result, String> { // 1) ConsoleBoxを生成(WASM環境ではブラウザコンソールに委譲) let console = Box::new(ConsoleBox::new()); // 2) slot解決→dispatchでlogを呼ぶ(最小疎通) 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]; + let args = + vec![Box::new(StringBox::new("🎉 WASM v2 console.log working!")) as Box]; let _ = unified_dispatch::dispatch_by_slot(slot_id, console.as_ref(), &args); } // 3) 結果を返す - Ok(Box::new(StringBox::new("WASM v2 unified dispatch test completed"))) + Ok(Box::new(StringBox::new( + "WASM v2 unified dispatch test completed", + ))) } diff --git a/src/backend/wasm_v2/unified_dispatch.rs b/src/backend/wasm_v2/unified_dispatch.rs index a857beef..bd0ef9a1 100644 --- a/src/backend/wasm_v2/unified_dispatch.rs +++ b/src/backend/wasm_v2/unified_dispatch.rs @@ -5,8 +5,8 @@ #![cfg(feature = "wasm-backend")] -use crate::box_trait::{NyashBox, StringBox, VoidBox, BoolBox}; -use crate::boxes::{ConsoleBox, ArrayBox, MapBox}; +use crate::box_trait::{BoolBox, NyashBox, StringBox, VoidBox}; +use crate::boxes::{ArrayBox, ConsoleBox, MapBox}; /// 受信ボックス/メソッド名/アリティからスロットを解決し、識別子を返す。 pub fn resolve_slot(recv: &dyn NyashBox, method: &str, arity: usize) -> Option { @@ -65,7 +65,7 @@ pub fn dispatch_by_slot( } None } - + // ArrayBox slots (100番台) 100 => { // array.get(index) @@ -84,8 +84,8 @@ pub fn dispatch_by_slot( } None } - - // MapBox slots (200番台) + + // MapBox slots (200番台) 200 => { // map.size() if let Some(map) = recv.as_any().downcast_ref::() { @@ -113,7 +113,7 @@ pub fn dispatch_by_slot( } None } - - _ => None + + _ => None, } } diff --git a/src/backend/wasm_v2/vtable_codegen.rs b/src/backend/wasm_v2/vtable_codegen.rs index e5867a0e..223ed11c 100644 --- a/src/backend/wasm_v2/vtable_codegen.rs +++ b/src/backend/wasm_v2/vtable_codegen.rs @@ -13,6 +13,8 @@ pub struct GeneratedVTableInfo { pub fn generate_tables() -> GeneratedVTableInfo { // 未実装: TypeRegistry::resolve_typebox_by_name()/methods を走査して集計 - GeneratedVTableInfo { types: 0, methods: 0 } + GeneratedVTableInfo { + types: 0, + methods: 0, + } } - diff --git a/src/benchmarks.rs b/src/benchmarks.rs index dc1c1ca9..28341d42 100644 --- a/src/benchmarks.rs +++ b/src/benchmarks.rs @@ -1,20 +1,20 @@ /*! * Nyash Performance Benchmarks - * + * * Compare execution performance across different backends: * - Interpreter (AST direct execution) * - VM (MIR -> VM 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")] 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)] pub struct BenchmarkResult { @@ -33,31 +33,31 @@ impl BenchmarkSuite { pub fn new(iterations: u32) -> Self { Self { iterations } } - + /// Run comprehensive benchmark across all backends pub fn run_all(&self) -> Vec { let mut results = Vec::new(); - + let benchmarks = [ ("bench_light", "benchmarks/bench_light.nyash"), - ("bench_medium", "benchmarks/bench_medium.nyash"), + ("bench_medium", "benchmarks/bench_medium.nyash"), ("bench_heavy", "benchmarks/bench_heavy.nyash"), ]; - + for (name, file_path) in &benchmarks { println!("🚀 Running benchmark: {}", name); - + // Test if file exists and is readable if let Ok(source) = fs::read_to_string(file_path) { // Run on all backends if let Ok(interpreter_result) = self.run_interpreter_benchmark(name, &source) { results.push(interpreter_result); } - + if let Ok(vm_result) = self.run_vm_benchmark(name, &source) { results.push(vm_result); } - + #[cfg(feature = "wasm-backend")] if let Ok(wasm_result) = self.run_wasm_benchmark(name, &source) { results.push(wasm_result); @@ -66,30 +66,34 @@ impl BenchmarkSuite { println!("⚠️ Could not read benchmark file: {}", file_path); } } - + results } - + /// Run benchmark on interpreter backend - fn run_interpreter_benchmark(&self, name: &str, source: &str) -> Result> { + fn run_interpreter_benchmark( + &self, + name: &str, + source: &str, + ) -> Result> { let mut total_duration = 0.0; - + for i in 0..self.iterations { let start = Instant::now(); - + // Parse and execute let ast = NyashParser::parse_from_string(source)?; let mut interpreter = NyashInterpreter::new(); let _result = interpreter.execute(ast)?; - + let duration = start.elapsed(); total_duration += duration.as_secs_f64() * 1000.0; // Convert to ms - + if i == 0 { println!(" 📊 Interpreter: First run completed"); } } - + Ok(BenchmarkResult { name: name.to_string(), backend: "Interpreter".to_string(), @@ -98,29 +102,33 @@ impl BenchmarkSuite { avg_duration_ms: total_duration / (self.iterations as f64), }) } - + /// Run benchmark on VM backend - fn run_vm_benchmark(&self, name: &str, source: &str) -> Result> { + fn run_vm_benchmark( + &self, + name: &str, + source: &str, + ) -> Result> { let mut total_duration = 0.0; - + for i in 0..self.iterations { let start = Instant::now(); - + // Parse -> MIR -> VM let ast = NyashParser::parse_from_string(source)?; let mut compiler = MirCompiler::new(); let compile_result = compiler.compile(ast)?; let mut vm = VM::new(); let _result = vm.execute_module(&compile_result.module)?; - + let duration = start.elapsed(); total_duration += duration.as_secs_f64() * 1000.0; // Convert to ms - + if i == 0 { println!(" 🏎️ VM: First run completed"); } } - + Ok(BenchmarkResult { name: name.to_string(), backend: "VM".to_string(), @@ -129,34 +137,38 @@ impl BenchmarkSuite { avg_duration_ms: total_duration / (self.iterations as f64), }) } - + /// Run benchmark on WASM backend #[cfg(feature = "wasm-backend")] - fn run_wasm_benchmark(&self, name: &str, source: &str) -> Result> { + fn run_wasm_benchmark( + &self, + name: &str, + source: &str, + ) -> Result> { let mut total_duration = 0.0; - + for i in 0..self.iterations { let start = Instant::now(); - + // Parse -> MIR -> WASM let ast = NyashParser::parse_from_string(source)?; let mut compiler = MirCompiler::new(); let compile_result = compiler.compile(ast)?; - + // Generate and execute WASM let mut wasm_backend = WasmBackend::new(); let _wat_output = wasm_backend.compile_module(compile_result.module)?; // Note: For now we only measure compilation time // Full WASM execution would require wasmtime integration - + let duration = start.elapsed(); total_duration += duration.as_secs_f64() * 1000.0; // Convert to ms - + if i == 0 { println!(" 🌐 WASM: First run completed (compilation only)"); } } - + Ok(BenchmarkResult { name: name.to_string(), backend: "WASM".to_string(), @@ -165,76 +177,98 @@ impl BenchmarkSuite { avg_duration_ms: total_duration / (self.iterations as f64), }) } - + /// Print benchmark results in a nice format pub fn print_results(&self, results: &[BenchmarkResult]) { println!("\n📊 Nyash Performance Benchmark Results"); println!("====================================="); println!("Iterations per test: {}", self.iterations); println!(); - + // Group by benchmark name - let mut benchmarks: std::collections::HashMap> = std::collections::HashMap::new(); + let mut benchmarks: std::collections::HashMap> = + std::collections::HashMap::new(); 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 { println!("🎯 {}", bench_name); println!(" Backend | Avg Time (ms) | Total Time (ms) | Speed Ratio"); println!(" --------------|---------------|-----------------|------------"); - + // 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 { let ratio = result.avg_duration_ms / fastest.avg_duration_ms; - println!(" {:12} | {:11.3} | {:13.1} | {:8.2}x", - result.backend, - result.avg_duration_ms, - result.duration_ms, - ratio + println!( + " {:12} | {:11.3} | {:13.1} | {:8.2}x", + result.backend, result.avg_duration_ms, result.duration_ms, ratio ); } println!(); } - + // Summary println!("💡 Performance Summary:"); - let interpreter_avg: f64 = results.iter() + let interpreter_avg: f64 = results + .iter() .filter(|r| r.backend == "Interpreter") .map(|r| r.avg_duration_ms) - .sum::() / results.iter().filter(|r| r.backend == "Interpreter").count() as f64; - - let vm_avg: f64 = results.iter() + .sum::() + / results + .iter() + .filter(|r| r.backend == "Interpreter") + .count() as f64; + + let vm_avg: f64 = results + .iter() .filter(|r| r.backend == "VM") .map(|r| r.avg_duration_ms) - .sum::() / results.iter().filter(|r| r.backend == "VM").count() as f64; - - let wasm_avg: f64 = results.iter() + .sum::() + / results.iter().filter(|r| r.backend == "VM").count() as f64; + + let wasm_avg: f64 = results + .iter() .filter(|r| r.backend == "WASM") .map(|r| r.avg_duration_ms) - .sum::() / results.iter().filter(|r| r.backend == "WASM").count() as f64; - + .sum::() + / results.iter().filter(|r| r.backend == "WASM").count() as f64; + println!(" 📈 Average across all benchmarks:"); println!(" Interpreter: {:.2} ms", interpreter_avg); - println!(" 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); + println!( + " 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 + ); } } #[cfg(test)] mod tests { use super::*; - + #[test] fn test_benchmark_light() { let suite = BenchmarkSuite::new(3); // Only 3 iterations for testing let results = suite.run_all(); - + // Should have results for all backends assert!(results.len() >= 3); // At least one benchmark with 3 backends - + suite.print_results(&results); } -} \ No newline at end of file +} diff --git a/src/bid/bridge.rs b/src/bid/bridge.rs index 94791f5a..8ece454f 100644 --- a/src/bid/bridge.rs +++ b/src/bid/bridge.rs @@ -1,14 +1,14 @@ +use super::{BidError, BidHandle, BidType}; use crate::box_trait::NyashBox; -use super::{BidHandle, BidType, BidError}; -use std::sync::Arc; use std::collections::HashMap; +use std::sync::Arc; /// BID-FFI Bridge for Nyash Box types /// Provides conversion between Nyash runtime values and BID handles pub trait BidBridge { /// Convert a Nyash Box to a BID handle fn to_bid_handle(&self, registry: &mut BoxRegistry) -> Result; - + /// Get the BID type representation fn bid_type(&self) -> BidType; } @@ -17,10 +17,10 @@ pub trait BidBridge { pub struct BoxRegistry { /// Maps handle to Arc handle_to_box: HashMap>, - + /// Next instance ID for each type next_instance_id: HashMap, - + /// Reverse lookup: Arc pointer to handle box_to_handle: HashMap, } @@ -33,7 +33,7 @@ impl BoxRegistry { box_to_handle: HashMap::new(), } } - + /// Register a Box and get its handle pub fn register_box(&mut self, type_id: u32, boxed: Arc) -> BidHandle { // Check if already registered by comparing Arc pointers @@ -42,24 +42,24 @@ impl BoxRegistry { if let Some(&handle) = self.box_to_handle.get(&arc_addr) { return handle; } - + // Generate new instance ID let instance_id = self.next_instance_id.entry(type_id).or_insert(1); let handle = BidHandle::new(type_id, *instance_id); *instance_id += 1; - + // Register bidirectionally self.handle_to_box.insert(handle, boxed.clone()); self.box_to_handle.insert(arc_addr, handle); - + handle } - + /// Retrieve a Box by its handle pub fn get_box(&self, handle: BidHandle) -> Option> { self.handle_to_box.get(&handle).cloned() } - + /// Remove a Box from the registry pub fn unregister(&mut self, handle: BidHandle) -> Option> { if let Some(boxed) = self.handle_to_box.remove(&handle) { @@ -78,24 +78,51 @@ pub fn box_to_bid_handle( registry: &mut BoxRegistry, ) -> Result<(BidType, BidHandle), BidError> { // Downcast to specific box types - if let Some(_string_box) = arc_box.as_any().downcast_ref::() { + if let Some(_string_box) = arc_box + .as_any() + .downcast_ref::() + { let handle = registry.register_box( 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)) - } else if let Some(_integer_box) = arc_box.as_any().downcast_ref::() { + Ok(( + BidType::Handle { + type_id: 1, + instance_id: handle.instance_id, + }, + handle, + )) + } else if let Some(_integer_box) = arc_box + .as_any() + .downcast_ref::() + { let handle = registry.register_box( 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)) - } else if let Some(_future_box) = arc_box.as_any().downcast_ref::() { + Ok(( + BidType::Handle { + type_id: 2, + instance_id: handle.instance_id, + }, + handle, + )) + } else if let Some(_future_box) = arc_box + .as_any() + .downcast_ref::() + { let handle = registry.register_box( 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 { Err(BidError::InvalidType) } @@ -106,13 +133,15 @@ pub fn bid_handle_to_box( handle: BidHandle, registry: &BoxRegistry, ) -> Result, BidError> { - registry.get_box(handle) - .ok_or(BidError::InvalidHandle) + registry.get_box(handle).ok_or(BidError::InvalidHandle) } /// Extract string value from a Box for TLV encoding pub fn extract_string_value(arc_box: &Arc) -> Result { - if let Some(string_box) = arc_box.as_any().downcast_ref::() { + if let Some(string_box) = arc_box + .as_any() + .downcast_ref::() + { Ok(string_box.value.clone()) } else { Err(BidError::InvalidType) @@ -121,7 +150,10 @@ pub fn extract_string_value(arc_box: &Arc) -> Result) -> Result { - if let Some(integer_box) = arc_box.as_any().downcast_ref::() { + if let Some(integer_box) = arc_box + .as_any() + .downcast_ref::() + { Ok(integer_box.value) } else { Err(BidError::InvalidType) @@ -131,37 +163,37 @@ pub fn extract_integer_value(arc_box: &Arc) -> Result = Arc::new(string_box); - + // Register it let handle = registry.register_box(1, arc_box.clone()); assert_eq!(handle.type_id, 1); assert_eq!(handle.instance_id, 1); - + // Retrieve it let retrieved = registry.get_box(handle).unwrap(); assert_eq!(Arc::as_ptr(&retrieved), Arc::as_ptr(&arc_box)); - + // Register same box again should return same handle let handle2 = registry.register_box(1, arc_box.clone()); assert_eq!(handle, handle2); } - + #[test] fn test_string_box_bid_conversion() { let mut registry = BoxRegistry::new(); - + // Create StringBox let string_box = crate::boxes::string_box::StringBox::new("Test String"); let arc_box: Arc = Arc::new(string_box); - + // Convert to BID handle let (bid_type, handle) = box_to_bid_handle(&arc_box, &mut registry).unwrap(); assert_eq!(handle.type_id, 1); // StringBox type ID @@ -169,25 +201,25 @@ mod tests { BidType::Handle { type_id, .. } => assert_eq!(type_id, 1), _ => panic!("Expected Handle type"), } - + // Extract string value let value = extract_string_value(&arc_box).unwrap(); assert_eq!(value, "Test String"); - + // Round-trip test let retrieved = bid_handle_to_box(handle, ®istry).unwrap(); let retrieved_value = extract_string_value(&retrieved).unwrap(); assert_eq!(retrieved_value, "Test String"); } - + #[test] fn test_integer_box_bid_conversion() { let mut registry = BoxRegistry::new(); - + // Create IntegerBox let integer_box = crate::boxes::integer_box::IntegerBox::new(42); let arc_box: Arc = Arc::new(integer_box); - + // Convert to BID handle let (bid_type, handle) = box_to_bid_handle(&arc_box, &mut registry).unwrap(); assert_eq!(handle.type_id, 2); // IntegerBox type ID @@ -195,25 +227,25 @@ mod tests { BidType::Handle { type_id, .. } => assert_eq!(type_id, 2), _ => panic!("Expected Handle type"), } - + // Extract integer value let value = extract_integer_value(&arc_box).unwrap(); assert_eq!(value, 42); - + // Round-trip test let retrieved = bid_handle_to_box(handle, ®istry).unwrap(); let retrieved_value = extract_integer_value(&retrieved).unwrap(); assert_eq!(retrieved_value, 42); } - + #[test] fn test_future_box_bid_conversion() { let mut registry = BoxRegistry::new(); - + // Create FutureBox let future_box = crate::boxes::future::NyashFutureBox::new(); let arc_box: Arc = Arc::new(future_box); - + // Convert to BID handle let (bid_type, handle) = box_to_bid_handle(&arc_box, &mut registry).unwrap(); assert_eq!(handle.type_id, 7); // FutureBox type ID @@ -221,22 +253,28 @@ mod tests { BidType::Handle { type_id, .. } => assert_eq!(type_id, 7), _ => panic!("Expected Handle type"), } - + // Round-trip test let retrieved = bid_handle_to_box(handle, ®istry).unwrap(); - + // Verify it's still a FutureBox - assert!(retrieved.as_any().downcast_ref::().is_some()); - + assert!(retrieved + .as_any() + .downcast_ref::() + .is_some()); + // Test with result set - if let Some(future) = arc_box.as_any().downcast_ref::() { + if let Some(future) = arc_box + .as_any() + .downcast_ref::() + { let string_result = crate::boxes::string_box::StringBox::new("Future Result"); future.set_result(Box::new(string_result)); - + // Verify state assert!(future.ready()); let result = future.get(); assert_eq!(result.to_string_box().value, "Future Result"); } } -} \ No newline at end of file +} diff --git a/src/bid/error.rs b/src/bid/error.rs index f7bf9696..e4561692 100644 --- a/src/bid/error.rs +++ b/src/bid/error.rs @@ -4,31 +4,31 @@ pub enum BidError { /// Operation successful Success = 0, - + /// Buffer too small (need to call again with larger buffer) ShortBuffer = -1, - + /// Invalid type ID InvalidType = -2, - + /// Invalid method ID InvalidMethod = -3, - + /// Invalid arguments InvalidArgs = -4, - + /// Plugin internal error PluginError = -5, - + /// Memory allocation failed OutOfMemory = -6, - + /// UTF-8 encoding error InvalidUtf8 = -7, - + /// Handle not found InvalidHandle = -8, - + /// Version mismatch VersionMismatch = -9, } @@ -50,7 +50,7 @@ impl BidError { _ => BidError::PluginError, // Unknown errors map to plugin error } } - + /// Get human-readable error message pub fn message(&self) -> &'static str { match self { @@ -77,4 +77,4 @@ impl std::fmt::Display for BidError { impl std::error::Error for BidError {} /// Result type for BID operations -pub type BidResult = Result; \ No newline at end of file +pub type BidResult = Result; diff --git a/src/bid/generic_plugin_box.rs b/src/bid/generic_plugin_box.rs index 91dcf2d1..0543b0ad 100644 --- a/src/bid/generic_plugin_box.rs +++ b/src/bid/generic_plugin_box.rs @@ -1,137 +1,151 @@ #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] mod plugin_impl { -use crate::bid::{BidError, BidResult, LoadedPlugin}; -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; -use std::any::Any; -use std::fmt; + use crate::bid::{BidError, BidResult, LoadedPlugin}; + use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; + use std::any::Any; + use std::fmt; -/// 汎用プラグインBoxインスタンス -/// 任意のプラグインBoxを統一的に扱える -pub struct GenericPluginBox { - base: BoxBase, - plugin: &'static LoadedPlugin, - instance_id: u32, - box_name: String, -} - -impl GenericPluginBox { - /// 汎用的なプラグインBoxを作成(birth呼び出し) - pub fn birth(plugin: &'static LoadedPlugin, box_name: String) -> BidResult { - eprintln!("🔍 GenericPluginBox::birth for '{}'", box_name); - - // birthメソッド(method_id = 0)を呼び出し - let mut out = Vec::new(); - plugin.handle.invoke(plugin.type_id, 0, 0, &[], &mut out)?; - - // インスタンスIDを取得 - let instance_id = if out.len() == 4 { - u32::from_le_bytes([out[0], out[1], out[2], out[3]]) - } else { - return Err(BidError::InvalidArgs); - }; - - eprintln!("✅ Created {} instance with ID: {}", box_name, instance_id); - - Ok(Self { - base: BoxBase::new(), - plugin, - instance_id, - box_name, - }) + /// 汎用プラグインBoxインスタンス + /// 任意のプラグインBoxを統一的に扱える + pub struct GenericPluginBox { + base: BoxBase, + plugin: &'static LoadedPlugin, + instance_id: u32, + box_name: String, } - - /// 汎用メソッド呼び出し - pub fn call_method(&self, method_name: &str, args: &[u8]) -> BidResult> { - eprintln!("🔍 GenericPluginBox::call_method '{}' on {}", method_name, self.box_name); - - // プラグインからメソッドIDを動的取得 - match self.plugin.find_method(method_name) { - Ok(Some((method_id, _signature))) => { - eprintln!("✅ Found method '{}' with ID: {}", method_name, method_id); - - let mut out = Vec::new(); - self.plugin.handle.invoke( - self.plugin.type_id, - method_id, - self.instance_id, - args, - &mut out - )?; - - Ok(out) + + impl GenericPluginBox { + /// 汎用的なプラグインBoxを作成(birth呼び出し) + pub fn birth(plugin: &'static LoadedPlugin, box_name: String) -> BidResult { + eprintln!("🔍 GenericPluginBox::birth for '{}'", box_name); + + // birthメソッド(method_id = 0)を呼び出し + let mut out = Vec::new(); + plugin.handle.invoke(plugin.type_id, 0, 0, &[], &mut out)?; + + // インスタンスIDを取得 + let instance_id = if out.len() == 4 { + u32::from_le_bytes([out[0], out[1], out[2], out[3]]) + } else { + return Err(BidError::InvalidArgs); + }; + + eprintln!("✅ Created {} instance with ID: {}", box_name, instance_id); + + Ok(Self { + base: BoxBase::new(), + plugin, + instance_id, + box_name, + }) + } + + /// 汎用メソッド呼び出し + pub fn call_method(&self, method_name: &str, args: &[u8]) -> BidResult> { + eprintln!( + "🔍 GenericPluginBox::call_method '{}' on {}", + method_name, self.box_name + ); + + // プラグインからメソッドIDを動的取得 + match self.plugin.find_method(method_name) { + Ok(Some((method_id, _signature))) => { + eprintln!("✅ Found method '{}' with ID: {}", method_name, method_id); + + let mut out = Vec::new(); + self.plugin.handle.invoke( + self.plugin.type_id, + method_id, + self.instance_id, + args, + &mut out, + )?; + + Ok(out) + } + Ok(None) => { + eprintln!("❌ Method '{}' not found in {}", method_name, self.box_name); + Err(BidError::InvalidArgs) + } + Err(e) => Err(e), } - Ok(None) => { - eprintln!("❌ Method '{}' not found in {}", method_name, self.box_name); - Err(BidError::InvalidArgs) - } - Err(e) => Err(e) } } -} -impl Drop for GenericPluginBox { - fn drop(&mut self) { - // finiメソッド(method_id = u32::MAX)を呼び出し - let _ = self.plugin.handle.invoke( - self.plugin.type_id, - u32::MAX, - self.instance_id, - &[], - &mut Vec::new(), - ); + impl Drop for GenericPluginBox { + fn drop(&mut self) { + // finiメソッド(method_id = u32::MAX)を呼び出し + let _ = self.plugin.handle.invoke( + self.plugin.type_id, + u32::MAX, + self.instance_id, + &[], + &mut Vec::new(), + ); + } } -} -impl BoxCore for GenericPluginBox { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { self.base.parent_type_id } - fn fmt_box(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}(plugin)", self.box_name) + impl BoxCore for GenericPluginBox { + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + self.base.parent_type_id + } + fn fmt_box(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}(plugin)", self.box_name) + } + 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 GenericPluginBox { - fn to_string_box(&self) -> StringBox { - StringBox::new(format!("{}(plugin)", self.box_name)) + impl NyashBox for GenericPluginBox { + fn to_string_box(&self) -> StringBox { + StringBox::new(format!("{}(plugin)", self.box_name)) + } + + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + if let Some(other_plugin) = other.as_any().downcast_ref::() { + BoolBox::new( + self.box_name == other_plugin.box_name + && self.instance_id == other_plugin.instance_id, + ) + } else { + BoolBox::new(false) + } + } + + fn clone_box(&self) -> Box { + // v2 plugin system migration: simplified clone for now + // TODO: Implement proper cloning through v2 plugin loader + Box::new(StringBox::new(format!("{}(plugin-clone)", self.box_name))) + } + + fn share_box(&self) -> Box { + self.clone_box() + } } - - fn equals(&self, other: &dyn NyashBox) -> BoolBox { - if let Some(other_plugin) = other.as_any().downcast_ref::() { - BoolBox::new( - self.box_name == other_plugin.box_name && - self.instance_id == other_plugin.instance_id + + impl fmt::Debug for GenericPluginBox { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "GenericPluginBox({}, instance={})", + self.box_name, self.instance_id ) - } else { - BoolBox::new(false) } } - - fn clone_box(&self) -> Box { - // v2 plugin system migration: simplified clone for now - // TODO: Implement proper cloning through v2 plugin loader - Box::new(StringBox::new(format!("{}(plugin-clone)", self.box_name))) - } - - fn share_box(&self) -> Box { - self.clone_box() - } -} -impl fmt::Debug for GenericPluginBox { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "GenericPluginBox({}, instance={})", self.box_name, self.instance_id) + impl fmt::Display for GenericPluginBox { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.fmt_box(f) + } } -} - -impl fmt::Display for GenericPluginBox { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.fmt_box(f) - } -} - } // mod plugin_impl #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] diff --git a/src/bid/loader.rs b/src/bid/loader.rs index 0566730c..c1a57596 100644 --- a/src/bid/loader.rs +++ b/src/bid/loader.rs @@ -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")))] use libloading::{Library, Symbol}; use std::path::{Path, PathBuf}; @@ -15,18 +18,21 @@ impl LoadedPlugin { /// Load a plugin dynamic library from file path and initialize it pub fn load_from_file(path: &Path) -> BidResult { // Load library - let library = unsafe { Library::new(path) } - .map_err(|_| BidError::PluginError)?; + let library = unsafe { Library::new(path) }.map_err(|_| BidError::PluginError)?; // Resolve symbols unsafe { let abi: Symbol u32> = library .get(PLUGIN_ABI_SYMBOL.as_bytes()) .map_err(|_| BidError::PluginError)?; - let init: Symbol i32> = library + let init: Symbol< + unsafe extern "C" fn(*const NyashHostVtable, *mut NyashPluginInfo) -> i32, + > = library .get(PLUGIN_INIT_SYMBOL.as_bytes()) .map_err(|_| BidError::PluginError)?; - let invoke: Symbol 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()) .map_err(|_| BidError::PluginError)?; let shutdown: Symbol = library @@ -49,7 +55,12 @@ impl LoadedPlugin { handle.initialize(&host, &mut info)?; let type_id = info.type_id; - Ok(Self { library, handle, type_id, plugin_info: info }) + Ok(Self { + library, + handle, + type_id, + plugin_info: info, + }) } } @@ -61,7 +72,7 @@ impl LoadedPlugin { /// Get all available methods for this plugin pub fn get_methods(&self) -> BidResult> { let mut methods = Vec::new(); - + unsafe { let methods_slice = self.plugin_info.methods_slice()?; for method_info in methods_slice { @@ -73,7 +84,7 @@ impl LoadedPlugin { )); } } - + Ok(methods) } @@ -111,7 +122,12 @@ static HOST_VTABLE_STORAGE: std::sync::LazyLock = 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 diff --git a/src/bid/metadata.rs b/src/bid/metadata.rs index af528fa4..b8d261a5 100644 --- a/src/bid/metadata.rs +++ b/src/bid/metadata.rs @@ -1,6 +1,6 @@ use super::{BidError, BidResult}; -use std::os::raw::{c_char}; use std::ffi::{CStr, CString}; +use std::os::raw::c_char; /// Host function table provided to plugins #[repr(C)] @@ -14,11 +14,18 @@ pub struct NyashHostVtable { impl NyashHostVtable { /// Create a vtable with no-op stubs (for tests) 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 w(_h: u64) {} 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, + } } } @@ -27,10 +34,10 @@ impl NyashHostVtable { pub struct NyashMethodInfo { /// Method ID (unique within the Box type) pub method_id: u32, - + /// Method name (null-terminated C string) pub method_name: *const c_char, - + /// Type signature hash for validation pub signature_hash: u32, } @@ -42,25 +49,28 @@ unsafe impl Sync for NyashMethodInfo {} impl NyashMethodInfo { /// Create method info with safe string handling - pub fn new(method_id: u32, method_name: &str, signature_hash: u32) -> BidResult<(Self, CString)> { - let c_name = CString::new(method_name) - .map_err(|_| BidError::InvalidUtf8)?; - + pub fn new( + method_id: u32, + method_name: &str, + signature_hash: u32, + ) -> BidResult<(Self, CString)> { + let c_name = CString::new(method_name).map_err(|_| BidError::InvalidUtf8)?; + let info = Self { method_id, method_name: c_name.as_ptr(), signature_hash, }; - + Ok((info, c_name)) } - + /// Get method name as string (unsafe: requires valid pointer) pub unsafe fn name(&self) -> BidResult<&str> { if self.method_name.is_null() { return Err(BidError::InvalidArgs); } - + CStr::from_ptr(self.method_name) .to_str() .map_err(|_| BidError::InvalidUtf8) @@ -72,13 +82,13 @@ impl NyashMethodInfo { pub struct NyashPluginInfo { /// Box type ID (e.g., 6 for FileBox) pub type_id: u32, - + /// Type name (null-terminated C string) pub type_name: *const c_char, - + /// Number of methods pub method_count: u32, - + /// Method information array pub methods: *const NyashMethodInfo, } @@ -98,24 +108,24 @@ impl NyashPluginInfo { methods: std::ptr::null(), } } - + /// Get type name as string (unsafe: requires valid pointer) pub unsafe fn name(&self) -> BidResult<&str> { if self.type_name.is_null() { return Err(BidError::InvalidArgs); } - + CStr::from_ptr(self.type_name) .to_str() .map_err(|_| BidError::InvalidUtf8) } - + /// Get methods as slice (unsafe: requires valid pointer and count) pub unsafe fn methods_slice(&self) -> BidResult<&[NyashMethodInfo]> { if self.methods.is_null() || self.method_count == 0 { return Ok(&[]); } - + Ok(std::slice::from_raw_parts( self.methods, self.method_count as usize, @@ -128,13 +138,13 @@ impl NyashPluginInfo { pub enum PluginState { /// Plugin loaded but not initialized Loaded, - + /// Plugin initialized and ready Ready, - + /// Plugin shutting down ShuttingDown, - + /// Plugin unloaded Unloaded, } @@ -143,7 +153,7 @@ pub enum PluginState { pub struct PluginMetadata { pub info: NyashPluginInfo, pub state: PluginState, - + // Keep CStrings alive for C interop #[allow(dead_code)] type_name_holder: Option, @@ -159,16 +169,15 @@ impl PluginMetadata { methods: Vec<(u32, &str, u32)>, // (id, name, hash) ) -> BidResult { // Create type name - let type_name_holder = CString::new(type_name) - .map_err(|_| BidError::InvalidUtf8)?; - + let type_name_holder = CString::new(type_name).map_err(|_| BidError::InvalidUtf8)?; + // Create method infos let mut method_holders = Vec::new(); for (id, name, hash) in methods { let (info, holder) = NyashMethodInfo::new(id, name, hash)?; method_holders.push((info, holder)); } - + // Build plugin info let info = NyashPluginInfo { type_id, @@ -180,7 +189,7 @@ impl PluginMetadata { method_holders.as_ptr() as *const NyashMethodInfo }, }; - + Ok(Self { info, state: PluginState::Loaded, @@ -193,7 +202,7 @@ impl PluginMetadata { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_plugin_metadata_creation() { let methods = vec![ @@ -202,29 +211,36 @@ mod tests { (3, "write", 0x11223344), (4, "close", 0xABCDEF00), ]; - + let metadata = PluginMetadata::new(6, "FileBox", methods).unwrap(); - + assert_eq!(metadata.info.type_id, 6); assert_eq!(metadata.info.method_count, 4); assert_eq!(metadata.state, PluginState::Loaded); - + unsafe { assert_eq!(metadata.info.name().unwrap(), "FileBox"); - + let methods = metadata.info.methods_slice().unwrap(); assert_eq!(methods.len(), 4); assert_eq!(methods[0].method_id, 1); assert_eq!(methods[0].name().unwrap(), "open"); } } - + #[test] 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 w(_h: u64) {} 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, + }; } } diff --git a/src/bid/mod.rs b/src/bid/mod.rs index 4c9a9f42..d9e18e0d 100644 --- a/src/bid/mod.rs +++ b/src/bid/mod.rs @@ -1,27 +1,27 @@ // BID-FFI: Box Interface Definition with Foreign Function Interface // 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 plugins; +pub mod error; #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] 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 plugin_box; // legacy - FileBox専用実装 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 error::*; #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] 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 plugin_box::*; // legacy pub use generic_plugin_box::*; diff --git a/src/bid/plugin_api.rs b/src/bid/plugin_api.rs index 8943fc2e..3bacb0fb 100644 --- a/src/bid/plugin_api.rs +++ b/src/bid/plugin_api.rs @@ -14,10 +14,8 @@ pub type PluginAbiFn = unsafe extern "C" fn() -> u32; /// - host: Host function table for plugin to use /// - info: Plugin information to be filled by plugin /// Returns: 0 on success, negative error code on failure -pub type PluginInitFn = unsafe extern "C" fn( - host: *const NyashHostVtable, - info: *mut NyashPluginInfo, -) -> i32; +pub type PluginInitFn = + unsafe extern "C" fn(host: *const NyashHostVtable, info: *mut NyashPluginInfo) -> i32; /// Invoke a plugin method /// Parameters: @@ -65,27 +63,19 @@ impl PluginHandle { } Ok(()) } - + /// Initialize plugin with host vtable - pub fn initialize( - &self, - host: &NyashHostVtable, - info: &mut NyashPluginInfo, - ) -> BidResult<()> { - let result = unsafe { - (self.init)( - host as *const NyashHostVtable, - info as *mut NyashPluginInfo, - ) - }; - + pub fn initialize(&self, host: &NyashHostVtable, info: &mut NyashPluginInfo) -> BidResult<()> { + let result = + unsafe { (self.init)(host as *const NyashHostVtable, info as *mut NyashPluginInfo) }; + if result != 0 { Err(BidError::from_raw(result)) } else { Ok(()) } } - + /// Invoke a plugin method pub fn invoke( &self, @@ -108,16 +98,16 @@ impl PluginHandle { &mut required_size, ) }; - + // Check for error (except buffer too small) if result != 0 && result != -1 { return Err(BidError::from_raw(result)); } - + // Allocate buffer if needed if required_size > 0 { result_buffer.resize(required_size, 0); - + // Second call: get actual data let mut actual_size = required_size; let result = unsafe { @@ -131,18 +121,18 @@ impl PluginHandle { &mut actual_size, ) }; - + if result != 0 { return Err(BidError::from_raw(result)); } - + // Trim to actual size result_buffer.truncate(actual_size); } - + Ok(()) } - + /// Shutdown plugin pub fn shutdown(&self) { unsafe { @@ -162,7 +152,7 @@ impl HostVtableBuilder { vtable: NyashHostVtable::empty(), } } - + pub fn with_alloc(self, _f: F) -> Self where F: Fn(usize) -> *mut std::os::raw::c_void + 'static, @@ -171,21 +161,21 @@ impl HostVtableBuilder { // and create a proper extern "C" function. This is simplified. self } - + pub fn with_free(self, _f: F) -> Self where F: Fn(*mut std::os::raw::c_void) + 'static, { self } - + pub fn with_log(self, _f: F) -> Self where F: Fn(&str) + 'static, { self } - + pub fn build(self) -> NyashHostVtable { self.vtable } @@ -194,12 +184,12 @@ impl HostVtableBuilder { #[cfg(test)] mod tests { use super::*; - + // Mock plugin functions for testing unsafe extern "C" fn mock_abi() -> u32 { 1 // BID-1 } - + unsafe extern "C" fn mock_init( _host: *const NyashHostVtable, info: *mut NyashPluginInfo, @@ -210,7 +200,7 @@ mod tests { } 0 } - + unsafe extern "C" fn mock_invoke( _type_id: u32, _method_id: u32, @@ -225,9 +215,9 @@ mod tests { } 0 } - + unsafe extern "C" fn mock_shutdown() {} - + #[test] fn test_plugin_handle() { let handle = PluginHandle { @@ -236,18 +226,18 @@ mod tests { invoke: mock_invoke, shutdown: mock_shutdown, }; - + // Check ABI assert!(handle.check_abi().is_ok()); - + // Initialize let host = NyashHostVtable::empty(); let mut info = NyashPluginInfo::empty(); assert!(handle.initialize(&host, &mut info).is_ok()); assert_eq!(info.type_id, 99); - + // Invoke let mut result = Vec::new(); assert!(handle.invoke(99, 1, 0, &[], &mut result).is_ok()); } -} \ No newline at end of file +} diff --git a/src/bid/plugins/filebox/mod.rs b/src/bid/plugins/filebox/mod.rs index 72068d48..521b5256 100644 --- a/src/bid/plugins/filebox/mod.rs +++ b/src/bid/plugins/filebox/mod.rs @@ -1,5 +1,5 @@ //! FileBox Plugin - BID-FFI File Operations -//! +//! //! Provides file I/O operations through the BID-FFI plugin interface. //! Everything is Box philosophy applied to file operations! @@ -40,61 +40,72 @@ impl FileBoxRegistry { next_handle: 1, } } - + pub fn open(&mut self, path: &str, mode: FileMode) -> Result { let file = match mode { 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::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; self.next_handle += 1; - + let state = FileBoxState { file, path: path.to_string(), mode, }; - + self.files.insert(handle_id, Arc::new(Mutex::new(state))); - + Ok(BidHandle::new(BoxTypeId::FileBox as u32, handle_id)) } - + pub fn read(&self, handle: BidHandle, size: usize) -> Result, std::io::Error> { let handle_id = handle.instance_id; - let file_state = self.files.get(&handle_id) - .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle"))?; - + let file_state = self.files.get(&handle_id).ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle") + })?; + let mut state = file_state.lock().unwrap(); let mut buffer = vec![0u8; size]; let bytes_read = state.file.read(&mut buffer)?; buffer.truncate(bytes_read); - + Ok(buffer) } - + pub fn write(&self, handle: BidHandle, data: &[u8]) -> Result { let handle_id = handle.instance_id; - let file_state = self.files.get(&handle_id) - .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle"))?; - + let file_state = self.files.get(&handle_id).ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle") + })?; + let mut state = file_state.lock().unwrap(); state.file.write(data) } - + pub fn close(&mut self, handle: BidHandle) -> Result<(), std::io::Error> { let handle_id = handle.instance_id; - self.files.remove(&handle_id) - .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle"))?; + self.files.remove(&handle_id).ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::NotFound, "Invalid file handle") + })?; Ok(()) } } /// Global registry instance -static FILEBOX_REGISTRY: Lazy>> = +static FILEBOX_REGISTRY: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(FileBoxRegistry::new()))); /// Get or create the global registry @@ -113,7 +124,7 @@ impl FileBoxPlugin { registry: get_registry(), } } - + /// Open a file and return its handle pub fn open(&self, path: &str, mode: &str) -> Result { let file_mode = match mode { @@ -123,30 +134,34 @@ impl FileBoxPlugin { "rw" | "r+" => FileMode::ReadWrite, _ => return Err(format!("Invalid file mode: {}", mode)), }; - + 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)) } - + /// Read data from file pub fn read(&self, handle: BidHandle, size: usize) -> Result, String> { let registry = self.registry.lock().unwrap(); - registry.read(handle, size) + registry + .read(handle, size) .map_err(|e| format!("Failed to read file: {}", e)) } - + /// Write data to file pub fn write(&self, handle: BidHandle, data: &[u8]) -> Result { let registry = self.registry.lock().unwrap(); - registry.write(handle, data) + registry + .write(handle, data) .map_err(|e| format!("Failed to write file: {}", e)) } - + /// Close file pub fn close(&self, handle: BidHandle) -> Result<(), String> { let mut registry = self.registry.lock().unwrap(); - registry.close(handle) + registry + .close(handle) .map_err(|e| format!("Failed to close file: {}", e)) } } @@ -155,41 +170,41 @@ impl FileBoxPlugin { mod tests { use super::*; use std::fs; - + #[test] fn test_filebox_plugin() { let plugin = FileBoxPlugin::new(); - + // Create a test file let test_path = "test_filebox_plugin.txt"; let test_content = "Hello, FileBox Plugin!"; fs::write(test_path, test_content).unwrap(); - + // Test open let handle = plugin.open(test_path, "r").unwrap(); assert_eq!(handle.type_id, BoxTypeId::FileBox as u32); - + // Test read let data = plugin.read(handle, 100).unwrap(); assert_eq!(String::from_utf8(data).unwrap(), test_content); - + // Test close plugin.close(handle).unwrap(); - + // Test write mode let write_handle = plugin.open(test_path, "w").unwrap(); let new_content = b"New content!"; let written = plugin.write(write_handle, new_content).unwrap(); assert_eq!(written, new_content.len()); plugin.close(write_handle).unwrap(); - + // Verify new content let read_handle = plugin.open(test_path, "r").unwrap(); let data = plugin.read(read_handle, 100).unwrap(); assert_eq!(&data[..], new_content); plugin.close(read_handle).unwrap(); - + // Cleanup fs::remove_file(test_path).unwrap(); } -} \ No newline at end of file +} diff --git a/src/bid/plugins/mod.rs b/src/bid/plugins/mod.rs index c718cdca..4a77bb70 100644 --- a/src/bid/plugins/mod.rs +++ b/src/bid/plugins/mod.rs @@ -1,8 +1,8 @@ //! BID-FFI Plugins -//! +//! //! Collection of built-in and loadable plugins for Nyash. pub mod filebox; // Re-export plugin types -pub use filebox::FileBoxPlugin; \ No newline at end of file +pub use filebox::FileBoxPlugin; diff --git a/src/bid/tlv.rs b/src/bid/tlv.rs index 9b5da2dd..36073d3d 100644 --- a/src/bid/tlv.rs +++ b/src/bid/tlv.rs @@ -1,22 +1,22 @@ -use super::{BidError, BidResult, BidHandle, BidTag, BID_VERSION}; +use super::{BidError, BidHandle, BidResult, BidTag, BID_VERSION}; use std::mem; /// BID-1 TLV Header #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct BidTlvHeader { - pub version: u16, // BID version (1) - pub argc: u16, // Argument count + pub version: u16, // BID version (1) + pub argc: u16, // Argument count } /// TLV Entry structure #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct TlvEntry { - pub tag: u8, // Type tag - pub reserved: u8, // Reserved for future use (0) - pub size: u16, // Payload size - // Payload follows immediately after + pub tag: u8, // Type tag + pub reserved: u8, // Reserved for future use (0) + pub size: u16, // Payload size + // Payload follows immediately after } /// TLV encoder for BID-1 format @@ -32,37 +32,39 @@ impl TlvEncoder { buffer: Vec::with_capacity(256), entry_count: 0, }; - + // Reserve space for header - encoder.buffer.extend_from_slice(&[0; mem::size_of::()]); + encoder + .buffer + .extend_from_slice(&[0; mem::size_of::()]); encoder } - + /// Encode a boolean value pub fn encode_bool(&mut self, value: bool) -> BidResult<()> { self.encode_entry(BidTag::Bool, &[if value { 1 } else { 0 }]) } - + /// Encode a 32-bit integer pub fn encode_i32(&mut self, value: i32) -> BidResult<()> { self.encode_entry(BidTag::I32, &value.to_le_bytes()) } - + /// Encode a 64-bit integer pub fn encode_i64(&mut self, value: i64) -> BidResult<()> { self.encode_entry(BidTag::I64, &value.to_le_bytes()) } - + /// Encode a 32-bit float pub fn encode_f32(&mut self, value: f32) -> BidResult<()> { self.encode_entry(BidTag::F32, &value.to_le_bytes()) } - + /// Encode a 64-bit float pub fn encode_f64(&mut self, value: f64) -> BidResult<()> { self.encode_entry(BidTag::F64, &value.to_le_bytes()) } - + /// Encode a string pub fn encode_string(&mut self, value: &str) -> BidResult<()> { let bytes = value.as_bytes(); @@ -71,7 +73,7 @@ impl TlvEncoder { } self.encode_entry(BidTag::String, bytes) } - + /// Encode binary data pub fn encode_bytes(&mut self, value: &[u8]) -> BidResult<()> { if value.len() > u16::MAX as usize { @@ -79,17 +81,17 @@ impl TlvEncoder { } self.encode_entry(BidTag::Bytes, value) } - + /// Encode a handle pub fn encode_handle(&mut self, handle: BidHandle) -> BidResult<()> { self.encode_entry(BidTag::Handle, &handle.to_u64().to_le_bytes()) } - + /// Encode void (no payload) pub fn encode_void(&mut self) -> BidResult<()> { self.encode_entry(BidTag::Void, &[]) } - + /// Internal: encode a TLV entry fn encode_entry(&mut self, tag: BidTag, payload: &[u8]) -> BidResult<()> { let entry = TlvEntry { @@ -97,19 +99,19 @@ impl TlvEncoder { reserved: 0, size: payload.len() as u16, }; - + // Write entry header self.buffer.push(entry.tag); self.buffer.push(entry.reserved); self.buffer.extend_from_slice(&entry.size.to_le_bytes()); - + // Write payload self.buffer.extend_from_slice(payload); - + self.entry_count += 1; Ok(()) } - + /// Finalize the encoding and return the buffer pub fn finish(mut self) -> Vec { // Update header @@ -117,7 +119,7 @@ impl TlvEncoder { version: BID_VERSION, argc: self.entry_count, }; - + // Write header at the beginning self.buffer[0..2].copy_from_slice(&header.version.to_le_bytes()); self.buffer[2..4].copy_from_slice(&header.argc.to_le_bytes()); @@ -138,58 +140,59 @@ impl<'a> TlvDecoder<'a> { if data.len() < mem::size_of::() { return Err(BidError::InvalidArgs); } - + // Read header safely let version = u16::from_le_bytes([data[0], data[1]]); let argc = u16::from_le_bytes([data[2], data[3]]); let header = BidTlvHeader { version, argc }; - + if header.version != BID_VERSION { return Err(BidError::VersionMismatch); } - + Ok(Self { data, position: mem::size_of::(), header, }) } - + /// Get the argument count pub fn arg_count(&self) -> u16 { self.header.argc } - + /// Decode the next entry pub fn decode_next(&mut self) -> BidResult> { if self.position >= self.data.len() { return Ok(None); } - + // Read entry header if self.position + mem::size_of::() > self.data.len() { return Err(BidError::InvalidArgs); } - + // Read entry safely let tag = self.data[self.position]; let reserved = self.data[self.position + 1]; - let size = u16::from_le_bytes([ - self.data[self.position + 2], - self.data[self.position + 3], - ]); - let entry = TlvEntry { tag, reserved, size }; + let size = u16::from_le_bytes([self.data[self.position + 2], self.data[self.position + 3]]); + let entry = TlvEntry { + tag, + reserved, + size, + }; self.position += mem::size_of::(); - + // Read payload let payload_end = self.position + entry.size as usize; if payload_end > self.data.len() { return Err(BidError::InvalidArgs); } - + let payload = &self.data[self.position..payload_end]; self.position = payload_end; - + // Convert tag let tag = match entry.tag { 1 => BidTag::Bool, @@ -206,10 +209,10 @@ impl<'a> TlvDecoder<'a> { 22 => BidTag::Array, _ => return Err(BidError::InvalidType), }; - + Ok(Some((tag, payload))) } - + /// Decode a boolean from payload pub fn decode_bool(payload: &[u8]) -> BidResult { if payload.len() != 1 { @@ -217,15 +220,17 @@ impl<'a> TlvDecoder<'a> { } Ok(payload[0] != 0) } - + /// Decode an i32 from payload pub fn decode_i32(payload: &[u8]) -> BidResult { if payload.len() != 4 { 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 pub fn decode_i64(payload: &[u8]) -> BidResult { if payload.len() != 8 { @@ -235,7 +240,7 @@ impl<'a> TlvDecoder<'a> { bytes.copy_from_slice(payload); Ok(i64::from_le_bytes(bytes)) } - + /// Decode a handle from payload pub fn decode_handle(payload: &[u8]) -> BidResult { if payload.len() != 8 { @@ -245,15 +250,17 @@ impl<'a> TlvDecoder<'a> { bytes.copy_from_slice(payload); Ok(BidHandle::from_u64(u64::from_le_bytes(bytes))) } - + /// Decode an f32 from payload pub fn decode_f32(payload: &[u8]) -> BidResult { if payload.len() != 4 { 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 pub fn decode_f64(payload: &[u8]) -> BidResult { if payload.len() != 8 { @@ -263,7 +270,7 @@ impl<'a> TlvDecoder<'a> { bytes.copy_from_slice(payload); Ok(f64::from_le_bytes(bytes)) } - + /// Decode a string from payload pub fn decode_string(payload: &[u8]) -> BidResult<&str> { std::str::from_utf8(payload).map_err(|_| BidError::InvalidUtf8) @@ -273,7 +280,7 @@ impl<'a> TlvDecoder<'a> { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_encode_decode_primitives() { let mut encoder = TlvEncoder::new(); @@ -281,44 +288,44 @@ mod tests { encoder.encode_i32(42).unwrap(); encoder.encode_i64(9876543210).unwrap(); encoder.encode_string("Hello Nyash!").unwrap(); - + let data = encoder.finish(); let mut decoder = TlvDecoder::new(&data).unwrap(); - + assert_eq!(decoder.arg_count(), 4); - + // Decode bool let (tag, payload) = decoder.decode_next().unwrap().unwrap(); assert_eq!(tag, BidTag::Bool); assert_eq!(TlvDecoder::decode_bool(payload).unwrap(), true); - + // Decode i32 let (tag, payload) = decoder.decode_next().unwrap().unwrap(); assert_eq!(tag, BidTag::I32); assert_eq!(TlvDecoder::decode_i32(payload).unwrap(), 42); - + // Decode i64 let (tag, payload) = decoder.decode_next().unwrap().unwrap(); assert_eq!(tag, BidTag::I64); assert_eq!(TlvDecoder::decode_i64(payload).unwrap(), 9876543210); - + // Decode string let (tag, payload) = decoder.decode_next().unwrap().unwrap(); assert_eq!(tag, BidTag::String); assert_eq!(TlvDecoder::decode_string(payload).unwrap(), "Hello Nyash!"); } - + #[test] fn test_encode_decode_handle() { let mut encoder = TlvEncoder::new(); let handle = BidHandle::new(6, 12345); encoder.encode_handle(handle).unwrap(); - + let data = encoder.finish(); let mut decoder = TlvDecoder::new(&data).unwrap(); - + let (tag, payload) = decoder.decode_next().unwrap().unwrap(); assert_eq!(tag, BidTag::Handle); assert_eq!(TlvDecoder::decode_handle(payload).unwrap(), handle); } -} \ No newline at end of file +} diff --git a/src/bid/types.rs b/src/bid/types.rs index e8d8caee..ffb56e62 100644 --- a/src/bid/types.rs +++ b/src/bid/types.rs @@ -2,32 +2,32 @@ #[derive(Clone, Debug, PartialEq)] pub enum BidType { // === Primitives (pass by value across FFI) === - Bool, // i32 (0=false, 1=true) - I32, // 32-bit signed integer - I64, // 64-bit signed integer - F32, // 32-bit floating point - F64, // 64-bit floating point - + Bool, // i32 (0=false, 1=true) + I32, // 32-bit signed integer + I64, // 64-bit signed integer + F32, // 32-bit floating point + F64, // 64-bit floating point + // === Composite types (pass as ptr+len) === - String, // UTF-8 string (ptr: usize, len: usize) - Bytes, // Binary data (ptr: usize, len: usize) - + String, // UTF-8 string (ptr: usize, len: usize) + Bytes, // Binary data (ptr: usize, len: usize) + // === Handle design (ChatGPT recommendation) === Handle { - type_id: u32, // Box type ID (1=StringBox, 6=FileBox, etc.) - instance_id: u32, // Instance identifier + type_id: u32, // Box type ID (1=StringBox, 6=FileBox, etc.) + instance_id: u32, // Instance identifier }, - + // === Meta types === - Void, // No return value - + Void, // No return value + // === Phase 2 reserved (TLV tags reserved) === #[allow(dead_code)] - Option(Box), // TLV tag=21 + Option(Box), // TLV tag=21 #[allow(dead_code)] Result(Box, Box), // TLV tag=20 #[allow(dead_code)] - Array(Box), // TLV tag=22 + Array(Box), // TLV tag=22 } /// Handle representation for efficient Box references @@ -41,14 +41,17 @@ pub struct BidHandle { impl BidHandle { /// Create a new handle 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) pub fn to_u64(&self) -> u64 { ((self.type_id as u64) << 32) | (self.instance_id as u64) } - + /// Unpack from single u64 pub fn from_u64(packed: u64) -> Self { Self { @@ -62,16 +65,16 @@ impl BidHandle { #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum BidTag { - Bool = 1, // payload: 1 byte (0/1) - I32 = 2, // payload: 4 bytes (little-endian) - I64 = 3, // payload: 8 bytes (little-endian) - F32 = 4, // payload: 4 bytes (IEEE 754) - F64 = 5, // payload: 8 bytes (IEEE 754) - String = 6, // payload: UTF-8 bytes - Bytes = 7, // payload: binary data - Handle = 8, // payload: 8 bytes (type_id + instance_id) - Void = 9, // payload: 0 bytes - + Bool = 1, // payload: 1 byte (0/1) + I32 = 2, // payload: 4 bytes (little-endian) + I64 = 3, // payload: 8 bytes (little-endian) + F32 = 4, // payload: 4 bytes (IEEE 754) + F64 = 5, // payload: 8 bytes (IEEE 754) + String = 6, // payload: UTF-8 bytes + Bytes = 7, // payload: binary data + Handle = 8, // payload: 8 bytes (type_id + instance_id) + Void = 9, // payload: 0 bytes + // Phase 2 reserved Result = 20, Option = 21, @@ -94,7 +97,7 @@ impl BidType { _ => panic!("Phase 2 types not yet implemented"), } } - + /// Get the expected payload size (None for variable-length types) pub fn payload_size(&self) -> Option { match self { @@ -120,10 +123,10 @@ pub enum BoxTypeId { BoolBox = 3, FloatBox = 4, ArrayBox = 5, - FileBox = 6, // Plugin example - FutureBox = 7, // Existing async support - P2PBox = 8, // Existing P2P support - // ... more box types + FileBox = 6, // Plugin example + FutureBox = 7, // Existing async support + P2PBox = 8, // Existing P2P support + // ... more box types } // ========== Type Information Management ========== @@ -159,7 +162,7 @@ impl ArgTypeMapping { to, } } - + /// 名前付きの型マッピングを作成 pub fn with_name(name: String, from: String, to: String) -> Self { Self { @@ -168,7 +171,7 @@ impl ArgTypeMapping { to, } } - + /// Nyash型からBIDタグへの変換を決定 /// ハードコーディングを避けるため、型名の組み合わせで判定 pub fn determine_bid_tag(&self) -> Option { @@ -176,20 +179,20 @@ impl ArgTypeMapping { // 文字列の変換パターン ("string", "string") => Some(BidTag::String), ("string", "bytes") => Some(BidTag::Bytes), - + // 数値の変換パターン ("integer", "i32") => Some(BidTag::I32), ("integer", "i64") => Some(BidTag::I64), ("float", "f32") => Some(BidTag::F32), ("float", "f64") => Some(BidTag::F64), - + // ブール値 ("bool", "bool") => Some(BidTag::Bool), - + // バイナリデータ ("bytes", "bytes") => Some(BidTag::Bytes), ("array", "bytes") => Some(BidTag::Bytes), // 配列をシリアライズ - + // 未対応の組み合わせ _ => None, } @@ -199,42 +202,49 @@ impl ArgTypeMapping { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_handle_packing() { let handle = BidHandle::new(6, 12345); let packed = handle.to_u64(); let unpacked = BidHandle::from_u64(packed); - + assert_eq!(handle, unpacked); assert_eq!(unpacked.type_id, 6); assert_eq!(unpacked.instance_id, 12345); } - + #[test] fn test_type_tags() { assert_eq!(BidType::Bool.tag(), BidTag::Bool); 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] fn test_arg_type_mapping() { // string → bytes 変換のテスト(writeメソッドで使用) let mapping = ArgTypeMapping::new("string".to_string(), "bytes".to_string()); assert_eq!(mapping.determine_bid_tag(), Some(BidTag::Bytes)); - + // integer → i32 変換のテスト let mapping = ArgTypeMapping::new("integer".to_string(), "i32".to_string()); assert_eq!(mapping.determine_bid_tag(), Some(BidTag::I32)); - + // 名前付きマッピングのテスト let mapping = ArgTypeMapping::with_name( "content".to_string(), "string".to_string(), - "string".to_string() + "string".to_string(), ); assert_eq!(mapping.name, Some("content".to_string())); assert_eq!(mapping.determine_bid_tag(), Some(BidTag::String)); } -} \ No newline at end of file +} diff --git a/src/box_arithmetic.rs b/src/box_arithmetic.rs index 63042a53..985bf2a8 100644 --- a/src/box_arithmetic.rs +++ b/src/box_arithmetic.rs @@ -1,13 +1,13 @@ /*! * Box Operations - Binary and unary operations between boxes - * + * * This module contains the implementation of operation boxes that perform * arithmetic, logical, and comparison operations between different Box types. */ -use crate::box_trait::{NyashBox, BoxCore, StringBox, IntegerBox, BoolBox, BoxBase}; -use std::fmt::{Debug, Display}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox}; use std::any::Any; +use std::fmt::{Debug, Display}; // ===== Binary Operation Boxes ===== @@ -20,53 +20,53 @@ pub struct AddBox { impl AddBox { pub fn new(left: Box, right: Box) -> Self { - Self { - left, - right, + Self { + left, + right, base: BoxBase::new(), } } - + /// Execute the addition operation and return the result pub fn execute(&self) -> Box { use crate::boxes::math_box::FloatBox; - + // 1. Integer + Integer if let (Some(left_int), Some(right_int)) = ( self.left.as_any().downcast_ref::(), - self.right.as_any().downcast_ref::() + self.right.as_any().downcast_ref::(), ) { let result = left_int.value + right_int.value; return Box::new(IntegerBox::new(result)); } - + // 2. Float + Float (or mixed with Integer) if let (Some(left_float), Some(right_float)) = ( self.left.as_any().downcast_ref::(), - self.right.as_any().downcast_ref::() + self.right.as_any().downcast_ref::(), ) { let result = left_float.value + right_float.value; return Box::new(FloatBox::new(result)); } - + // 3. Integer + Float if let (Some(left_int), Some(right_float)) = ( self.left.as_any().downcast_ref::(), - self.right.as_any().downcast_ref::() + self.right.as_any().downcast_ref::(), ) { let result = left_int.value as f64 + right_float.value; return Box::new(FloatBox::new(result)); } - + // 4. Float + Integer if let (Some(left_float), Some(right_int)) = ( self.left.as_any().downcast_ref::(), - self.right.as_any().downcast_ref::() + self.right.as_any().downcast_ref::(), ) { let result = left_float.value + right_int.value as f64; return Box::new(FloatBox::new(result)); } - + // 5. String concatenation (fallback for any types) let left_str = self.left.to_string_box(); let right_str = self.right.to_string_box(); @@ -90,29 +90,26 @@ impl NyashBox for AddBox { let result = self.execute(); result.to_string_box() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_add) = other.as_any().downcast_ref::() { BoolBox::new( - self.left.equals(other_add.left.as_ref()).value && - self.right.equals(other_add.right.as_ref()).value + self.left.equals(other_add.left.as_ref()).value + && self.right.equals(other_add.right.as_ref()).value, ) } else { BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { "AddBox" } - + fn clone_box(&self) -> Box { - Box::new(AddBox::new( - self.left.clone_box(), - self.right.clone_box() - )) + Box::new(AddBox::new(self.left.clone_box(), self.right.clone_box())) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -123,19 +120,19 @@ impl BoxCore for AddBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.to_string_box().value) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -156,19 +153,19 @@ pub struct SubtractBox { impl SubtractBox { pub fn new(left: Box, right: Box) -> Self { - Self { - left, - right, + Self { + left, + right, base: BoxBase::new(), } } - + /// Execute the subtraction operation and return the result pub fn execute(&self) -> Box { // For now, only handle integer subtraction if let (Some(left_int), Some(right_int)) = ( self.left.as_any().downcast_ref::(), - self.right.as_any().downcast_ref::() + self.right.as_any().downcast_ref::(), ) { let result = left_int.value - right_int.value; Box::new(IntegerBox::new(result)) @@ -180,7 +177,8 @@ impl SubtractBox { } else { 0 }; - let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::() { + let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::() + { int_box.value } else { 0 @@ -206,23 +204,28 @@ impl NyashBox for SubtractBox { let result = self.execute(); result.to_string_box() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_sub) = other.as_any().downcast_ref::() { BoolBox::new( - self.left.equals(other_sub.left.as_ref()).value && - self.right.equals(other_sub.right.as_ref()).value + self.left.equals(other_sub.left.as_ref()).value + && self.right.equals(other_sub.right.as_ref()).value, ) } else { BoolBox::new(false) } } - - fn type_name(&self) -> &'static str { "SubtractBox" } - fn clone_box(&self) -> Box { - Box::new(SubtractBox::new(self.left.clone_box(), self.right.clone_box())) + + fn type_name(&self) -> &'static str { + "SubtractBox" } - + fn clone_box(&self) -> Box { + Box::new(SubtractBox::new( + self.left.clone_box(), + self.right.clone_box(), + )) + } + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -230,13 +233,21 @@ impl NyashBox for SubtractBox { } impl BoxCore for SubtractBox { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { self.base.parent_type_id } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + self.base.parent_type_id + } fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.to_string_box().value) } - 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 Display for SubtractBox { @@ -254,19 +265,19 @@ pub struct MultiplyBox { impl MultiplyBox { pub fn new(left: Box, right: Box) -> Self { - Self { - left, - right, + Self { + left, + right, base: BoxBase::new(), } } - + /// Execute the multiplication operation and return the result pub fn execute(&self) -> Box { // For now, only handle integer multiplication if let (Some(left_int), Some(right_int)) = ( self.left.as_any().downcast_ref::(), - self.right.as_any().downcast_ref::() + self.right.as_any().downcast_ref::(), ) { let result = left_int.value * right_int.value; Box::new(IntegerBox::new(result)) @@ -277,7 +288,8 @@ impl MultiplyBox { } else { 0 }; - let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::() { + let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::() + { int_box.value } else { 0 @@ -303,23 +315,28 @@ impl NyashBox for MultiplyBox { let result = self.execute(); result.to_string_box() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_mul) = other.as_any().downcast_ref::() { BoolBox::new( - self.left.equals(other_mul.left.as_ref()).value && - self.right.equals(other_mul.right.as_ref()).value + self.left.equals(other_mul.left.as_ref()).value + && self.right.equals(other_mul.right.as_ref()).value, ) } else { BoolBox::new(false) } } - - fn type_name(&self) -> &'static str { "MultiplyBox" } - fn clone_box(&self) -> Box { - Box::new(MultiplyBox::new(self.left.clone_box(), self.right.clone_box())) + + fn type_name(&self) -> &'static str { + "MultiplyBox" } - + fn clone_box(&self) -> Box { + Box::new(MultiplyBox::new( + self.left.clone_box(), + self.right.clone_box(), + )) + } + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -327,13 +344,21 @@ impl NyashBox for MultiplyBox { } impl BoxCore for MultiplyBox { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { self.base.parent_type_id } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + self.base.parent_type_id + } fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.to_string_box().value) } - 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 Display for MultiplyBox { @@ -351,21 +376,21 @@ pub struct DivideBox { impl DivideBox { pub fn new(left: Box, right: Box) -> Self { - Self { - left, - right, + Self { + left, + right, base: BoxBase::new(), } } - + /// Execute the division operation and return the result pub fn execute(&self) -> Box { use crate::boxes::math_box::FloatBox; - + // Handle integer division, but return float result if let (Some(left_int), Some(right_int)) = ( self.left.as_any().downcast_ref::(), - self.right.as_any().downcast_ref::() + self.right.as_any().downcast_ref::(), ) { if right_int.value == 0 { // Return error for division by zero @@ -380,7 +405,8 @@ impl DivideBox { } else { 0 }; - let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::() { + let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::() + { int_box.value } else { 1 // Avoid division by zero @@ -409,23 +435,28 @@ impl NyashBox for DivideBox { let result = self.execute(); result.to_string_box() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_div) = other.as_any().downcast_ref::() { BoolBox::new( - self.left.equals(other_div.left.as_ref()).value && - self.right.equals(other_div.right.as_ref()).value + self.left.equals(other_div.left.as_ref()).value + && self.right.equals(other_div.right.as_ref()).value, ) } else { BoolBox::new(false) } } - - fn type_name(&self) -> &'static str { "DivideBox" } - fn clone_box(&self) -> Box { - Box::new(DivideBox::new(self.left.clone_box(), self.right.clone_box())) + + fn type_name(&self) -> &'static str { + "DivideBox" } - + fn clone_box(&self) -> Box { + Box::new(DivideBox::new( + self.left.clone_box(), + self.right.clone_box(), + )) + } + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -433,13 +464,21 @@ impl NyashBox for DivideBox { } impl BoxCore for DivideBox { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { self.base.parent_type_id } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + self.base.parent_type_id + } fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.to_string_box().value) } - 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 Display for DivideBox { @@ -457,19 +496,19 @@ pub struct ModuloBox { impl ModuloBox { pub fn new(left: Box, right: Box) -> Self { - Self { - left, - right, + Self { + left, + right, base: BoxBase::new(), } } - + /// Execute the modulo operation and return the result pub fn execute(&self) -> Box { // Handle integer modulo operation if let (Some(left_int), Some(right_int)) = ( self.left.as_any().downcast_ref::(), - self.right.as_any().downcast_ref::() + self.right.as_any().downcast_ref::(), ) { if right_int.value == 0 { // Return error for modulo by zero @@ -484,7 +523,8 @@ impl ModuloBox { } else { 0 }; - let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::() { + let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::() + { int_box.value } else { 1 // Avoid modulo by zero @@ -508,16 +548,20 @@ impl Debug for ModuloBox { } impl BoxCore for ModuloBox { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { self.base.parent_type_id } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + self.base.parent_type_id + } fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "ModuloBox[{}]", self.box_id()) } - + fn as_any(&self) -> &dyn std::any::Any { self } - + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self } @@ -528,26 +572,29 @@ impl NyashBox for ModuloBox { let result = self.execute(); result.to_string_box() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_modulo) = other.as_any().downcast_ref::() { BoolBox::new( - self.left.equals(other_modulo.left.as_ref()).value && - self.right.equals(other_modulo.right.as_ref()).value + self.left.equals(other_modulo.left.as_ref()).value + && self.right.equals(other_modulo.right.as_ref()).value, ) } else { BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { "ModuloBox" } - + fn clone_box(&self) -> Box { - 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と同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -568,65 +615,65 @@ impl CompareBox { pub fn equals(left: &dyn NyashBox, right: &dyn NyashBox) -> BoolBox { left.equals(right) } - + /// Compare two boxes for less than pub fn less(left: &dyn NyashBox, right: &dyn NyashBox) -> BoolBox { // Try integer comparison first if let (Some(left_int), Some(right_int)) = ( left.as_any().downcast_ref::(), - right.as_any().downcast_ref::() + right.as_any().downcast_ref::(), ) { return BoolBox::new(left_int.value < right_int.value); } - + // Fall back to string comparison let left_str = left.to_string_box(); let right_str = right.to_string_box(); BoolBox::new(left_str.value < right_str.value) } - + /// Compare two boxes for greater than pub fn greater(left: &dyn NyashBox, right: &dyn NyashBox) -> BoolBox { // Try integer comparison first if let (Some(left_int), Some(right_int)) = ( left.as_any().downcast_ref::(), - right.as_any().downcast_ref::() + right.as_any().downcast_ref::(), ) { return BoolBox::new(left_int.value > right_int.value); } - + // Fall back to string comparison let left_str = left.to_string_box(); let right_str = right.to_string_box(); BoolBox::new(left_str.value > right_str.value) } - + /// Compare two boxes for less than or equal pub fn less_equal(left: &dyn NyashBox, right: &dyn NyashBox) -> BoolBox { // Try integer comparison first if let (Some(left_int), Some(right_int)) = ( left.as_any().downcast_ref::(), - right.as_any().downcast_ref::() + right.as_any().downcast_ref::(), ) { return BoolBox::new(left_int.value <= right_int.value); } - + // Fall back to string comparison let left_str = left.to_string_box(); let right_str = right.to_string_box(); BoolBox::new(left_str.value <= right_str.value) } - + /// Compare two boxes for greater than or equal pub fn greater_equal(left: &dyn NyashBox, right: &dyn NyashBox) -> BoolBox { // Try integer comparison first if let (Some(left_int), Some(right_int)) = ( left.as_any().downcast_ref::(), - right.as_any().downcast_ref::() + right.as_any().downcast_ref::(), ) { return BoolBox::new(left_int.value >= right_int.value); } - + // Fall back to string comparison let left_str = left.to_string_box(); let right_str = right.to_string_box(); @@ -644,7 +691,7 @@ mod tests { let right = Box::new(IntegerBox::new(32)) as Box; let add_box = AddBox::new(left, right); let result = add_box.execute(); - + assert_eq!(result.to_string_box().value, "42"); } @@ -654,7 +701,7 @@ mod tests { let right = Box::new(StringBox::new("World!".to_string())) as Box; let add_box = AddBox::new(left, right); let result = add_box.execute(); - + assert_eq!(result.to_string_box().value, "Hello, World!"); } @@ -664,7 +711,7 @@ mod tests { let right = Box::new(IntegerBox::new(8)) as Box; let sub_box = SubtractBox::new(left, right); let result = sub_box.execute(); - + assert_eq!(result.to_string_box().value, "42"); } @@ -674,7 +721,7 @@ mod tests { let right = Box::new(IntegerBox::new(7)) as Box; let mul_box = MultiplyBox::new(left, right); let result = mul_box.execute(); - + assert_eq!(result.to_string_box().value, "42"); } @@ -684,7 +731,7 @@ mod tests { let right = Box::new(IntegerBox::new(2)) as Box; let div_box = DivideBox::new(left, right); let result = div_box.execute(); - + // Division returns float assert_eq!(result.to_string_box().value, "42"); } @@ -695,7 +742,7 @@ mod tests { let right = Box::new(IntegerBox::new(0)) as Box; let div_box = DivideBox::new(left, right); let result = div_box.execute(); - + assert!(result.to_string_box().value.contains("Division by zero")); } @@ -705,7 +752,7 @@ mod tests { let right = Box::new(IntegerBox::new(3)) as Box; let mod_box = ModuloBox::new(left, right); let result = mod_box.execute(); - + assert_eq!(result.to_string_box().value, "1"); } @@ -715,28 +762,28 @@ mod tests { let right = Box::new(IntegerBox::new(0)) as Box; let mod_box = ModuloBox::new(left, right); let result = mod_box.execute(); - + assert!(result.to_string_box().value.contains("Modulo by zero")); } #[test] fn test_modulo_chip8_pattern() { // Test Chip-8 style bit operations using modulo - let left = Box::new(IntegerBox::new(4096)) as Box; // 0x1000 + let left = Box::new(IntegerBox::new(4096)) as Box; // 0x1000 let right = Box::new(IntegerBox::new(4096)) as Box; // 0x1000 let mod_box = ModuloBox::new(left, right); let result = mod_box.execute(); - - assert_eq!(result.to_string_box().value, "0"); // 4096 % 4096 = 0 + + assert_eq!(result.to_string_box().value, "0"); // 4096 % 4096 = 0 } #[test] fn test_compare_box() { let left = IntegerBox::new(10); let right = IntegerBox::new(20); - + assert_eq!(CompareBox::less(&left, &right).value, true); assert_eq!(CompareBox::greater(&left, &right).value, false); assert_eq!(CompareBox::equals(&left, &right).value, false); } -} \ No newline at end of file +} diff --git a/src/box_factory/builtin.rs b/src/box_factory/builtin.rs index 51d98d19..37f2ead2 100644 --- a/src/box_factory/builtin.rs +++ b/src/box_factory/builtin.rs @@ -7,14 +7,16 @@ */ use super::BoxFactory; -use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox}; +use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox}; use crate::interpreter::RuntimeError; /// Factory for builtin Box types pub struct BuiltinBoxFactory; impl BuiltinBoxFactory { - pub fn new() -> Self { Self } + pub fn new() -> Self { + Self + } } impl BoxFactory for BuiltinBoxFactory { @@ -57,18 +59,27 @@ impl BoxFactory for BuiltinBoxFactory { "NullBox" => Ok(Box::new(crate::boxes::null_box::NullBox::new())), // 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> { vec![ // Primitive wrappers - "StringBox", "IntegerBox", "BoolBox", + "StringBox", + "IntegerBox", + "BoolBox", // Collections/common - "ArrayBox", "MapBox", "ConsoleBox", "NullBox", + "ArrayBox", + "MapBox", + "ConsoleBox", + "NullBox", ] } - fn is_builtin_factory(&self) -> bool { true } + fn is_builtin_factory(&self) -> bool { + true + } } diff --git a/src/box_factory/mod.rs b/src/box_factory/mod.rs index 164eff73..49d55a0b 100644 --- a/src/box_factory/mod.rs +++ b/src/box_factory/mod.rs @@ -1,9 +1,9 @@ /*! * Unified Box Factory Architecture - * + * * Phase 9.78: 統合BoxFactoryアーキテクチャ * すべてのBox生成(ビルトイン、ユーザー定義、プラグイン)を統一的に扱う - * + * * Design principles: * - "Everything is Box" 哲学の実装レベルでの体現 * - birth/finiライフサイクルの明確な責務分離 @@ -23,15 +23,15 @@ pub trait BoxFactory: Send + Sync { name: &str, args: &[Box], ) -> Result, RuntimeError>; - + /// Check if this factory is currently available fn is_available(&self) -> bool { true } - + /// Get list of Box types this factory can create fn box_types(&self) -> Vec<&str>; - + /// Check if this factory supports birth/fini lifecycle fn supports_birth(&self) -> bool { true @@ -47,7 +47,7 @@ pub trait BoxFactory: Send + Sync { pub struct UnifiedBoxRegistry { /// Ordered list of factories (priority: builtin > user > plugin) pub factories: Vec>, - + /// Quick lookup cache for performance type_cache: RwLock>, // maps type name to factory index } @@ -60,13 +60,13 @@ impl UnifiedBoxRegistry { type_cache: RwLock::new(HashMap::new()), } } - + /// Register a new factory pub fn register(&mut self, factory: Arc) { // Get all types this factory can create let types = factory.box_types(); let factory_index = self.factories.len(); - + // Update cache let mut cache = self.type_cache.write().unwrap(); // Reserved core types that must remain builtin-owned @@ -105,10 +105,10 @@ impl UnifiedBoxRegistry { } } } - + self.factories.push(factory); } - + /// Create a Box using the unified interface pub fn create_box( &self, @@ -119,8 +119,12 @@ impl UnifiedBoxRegistry { if std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1") { use crate::runtime::{get_global_registry, BoxProvider}; // Allowlist types for override: env NYASH_PLUGIN_OVERRIDE_TYPES="ArrayBox,MapBox" (default: none) - let allow: Vec = 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() + let allow: Vec = 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() } else { vec![] }; @@ -128,8 +132,11 @@ impl UnifiedBoxRegistry { let v2 = get_global_registry(); if let Some(provider) = v2.get_provider(name) { if let BoxProvider::Plugin(_lib) = provider { - return v2.create_box(name, args) - .map_err(|e| RuntimeError::InvalidOperation { message: format!("Plugin Box creation failed: {}", e) }); + return v2.create_box(name, args).map_err(|e| { + RuntimeError::InvalidOperation { + message: format!("Plugin Box creation failed: {}", e), + } + }); } } } @@ -144,19 +151,19 @@ impl UnifiedBoxRegistry { } } drop(cache); - + // Linear search through all factories for factory in &self.factories { if !factory.is_available() { continue; } - + // For factories that advertise types, check if they support this type let box_types = factory.box_types(); if !box_types.is_empty() && !box_types.contains(&name) { continue; } - + // Try to create the box (factories with empty box_types() will always be tried) match factory.create_box(name, args) { Ok(boxed) => return Ok(boxed), @@ -185,19 +192,25 @@ impl UnifiedBoxRegistry { let cache = self.type_cache.read().unwrap(); if let Some(&idx) = cache.get(name) { 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 for factory in &self.factories { - if !factory.is_available() { continue; } + if !factory.is_available() { + continue; + } let types = factory.box_types(); - if !types.is_empty() && types.contains(&name) { return true; } + if !types.is_empty() && types.contains(&name) { + return true; + } } false } - + /// Get all available Box types pub fn available_types(&self) -> Vec { let mut types = Vec::new(); @@ -214,15 +227,15 @@ impl UnifiedBoxRegistry { } } +pub mod builtin; +pub mod plugin; /// Re-export submodules pub mod user_defined; -pub mod plugin; -pub mod builtin; #[cfg(test)] mod tests { use super::*; - + #[test] fn test_registry_creation() { let registry = UnifiedBoxRegistry::new(); diff --git a/src/box_factory/plugin.rs b/src/box_factory/plugin.rs index bc9dfcd1..6a2a3101 100644 --- a/src/box_factory/plugin.rs +++ b/src/box_factory/plugin.rs @@ -1,6 +1,6 @@ /*! * Plugin Box Factory - * + * * Handles creation of plugin-based Box types through BID/FFI system * Integrates with v2 plugin system (BoxFactoryRegistry) */ @@ -29,9 +29,10 @@ impl BoxFactory for PluginBoxFactory { ) -> Result, RuntimeError> { // Use the existing v2 plugin system let registry = get_global_registry(); - + if let Some(_provider) = registry.get_provider(name) { - registry.create_box(name, args) + registry + .create_box(name, args) .map_err(|e| RuntimeError::InvalidOperation { message: format!("Plugin Box creation failed: {}", e), }) @@ -41,17 +42,17 @@ impl BoxFactory for PluginBoxFactory { }) } } - + fn box_types(&self) -> Vec<&str> { // TODO: Get list from BoxFactoryRegistry // For now, return empty as registry doesn't expose this yet vec![] } - + fn is_available(&self) -> bool { // Check if any plugins are loaded let _registry = get_global_registry(); // TODO: Add method to check if registry has any providers true } -} \ No newline at end of file +} diff --git a/src/box_factory/user_defined.rs b/src/box_factory/user_defined.rs index 160d42c9..8cf3d2f8 100644 --- a/src/box_factory/user_defined.rs +++ b/src/box_factory/user_defined.rs @@ -1,14 +1,14 @@ /*! * User-Defined Box Factory - * + * * Handles creation of user-defined Box types through InstanceBox * Manages inheritance, fields, methods, and birth/fini lifecycle */ use super::BoxFactory; use crate::box_trait::NyashBox; -use crate::interpreter::{RuntimeError, SharedState}; use crate::instance_v2::InstanceBox; +use crate::interpreter::{RuntimeError, SharedState}; /// Factory for user-defined Box types pub struct UserDefinedBoxFactory { @@ -17,9 +17,7 @@ pub struct UserDefinedBoxFactory { impl UserDefinedBoxFactory { pub fn new(shared_state: SharedState) -> Self { - Self { - shared_state, - } + Self { shared_state } } } @@ -34,31 +32,31 @@ impl BoxFactory for UserDefinedBoxFactory { let box_decls = self.shared_state.box_declarations.read().unwrap(); box_decls.get(name).cloned() }; - + let box_decl = box_decl.ok_or_else(|| RuntimeError::InvalidOperation { message: format!("Unknown Box type: {}", name), })?; - + // Create InstanceBox with fields and methods let instance = InstanceBox::from_declaration( name.to_string(), box_decl.fields.clone(), box_decl.methods.clone(), ); - + // TODO: Execute birth/init constructor with args // For now, just return the instance Ok(Box::new(instance)) } - + fn box_types(&self) -> Vec<&str> { // Can't return borrowed strings from temporary RwLock guard // For now, return empty - this method isn't critical vec![] } - + fn is_available(&self) -> bool { // Always available when SharedState is present true } -} \ No newline at end of file +} diff --git a/src/box_operators.rs b/src/box_operators.rs index 823abc10..45f09577 100644 --- a/src/box_operators.rs +++ b/src/box_operators.rs @@ -1,20 +1,19 @@ /*! * Box Operator Implementations - Trait-Based Operator Overloading - * + * * This module implements the new trait-based operator system for basic Box types. * It provides implementations of NyashAdd, NyashSub, etc. for IntegerBox, StringBox, * and other fundamental types. - * + * * Based on AI consultation decision (2025-08-10): Rust-style traits with * 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::operator_traits::{ - NyashAdd, NyashSub, NyashMul, NyashDiv, - DynamicAdd, DynamicSub, DynamicMul, DynamicDiv, - OperatorError + DynamicAdd, DynamicDiv, DynamicMul, DynamicSub, NyashAdd, NyashDiv, NyashMul, NyashSub, + OperatorError, }; // ===== IntegerBox Operator Implementations ===== @@ -22,7 +21,7 @@ use crate::operator_traits::{ /// IntegerBox + IntegerBox -> IntegerBox impl NyashAdd for IntegerBox { type Output = IntegerBox; - + fn add(self, rhs: IntegerBox) -> Self::Output { IntegerBox::new(self.value + rhs.value) } @@ -31,7 +30,7 @@ impl NyashAdd for IntegerBox { /// IntegerBox - IntegerBox -> IntegerBox impl NyashSub for IntegerBox { type Output = IntegerBox; - + fn sub(self, rhs: IntegerBox) -> Self::Output { IntegerBox::new(self.value - rhs.value) } @@ -40,7 +39,7 @@ impl NyashSub for IntegerBox { /// IntegerBox * IntegerBox -> IntegerBox impl NyashMul for IntegerBox { type Output = IntegerBox; - + fn mul(self, rhs: IntegerBox) -> Self::Output { IntegerBox::new(self.value * rhs.value) } @@ -49,7 +48,7 @@ impl NyashMul for IntegerBox { /// IntegerBox / IntegerBox -> IntegerBox (with zero check) impl NyashDiv for IntegerBox { type Output = Result; - + fn div(self, rhs: IntegerBox) -> Self::Output { if rhs.value == 0 { Err(OperatorError::DivisionByZero) @@ -66,19 +65,24 @@ impl DynamicAdd for IntegerBox { if let Some(other_int) = other.as_any().downcast_ref::() { return Some(Box::new(IntegerBox::new(self.value + other_int.value))); } - + // IntegerBox + FloatBox -> FloatBox if let Some(other_float) = other.as_any().downcast_ref::() { - 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 // This preserves the existing AddBox behavior let left_str = self.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 { matches!(other_type, "IntegerBox" | "FloatBox" | "StringBox") } @@ -90,15 +94,17 @@ impl DynamicSub for IntegerBox { if let Some(other_int) = other.as_any().downcast_ref::() { return Some(Box::new(IntegerBox::new(self.value - other_int.value))); } - + // IntegerBox - FloatBox -> FloatBox if let Some(other_float) = other.as_any().downcast_ref::() { - 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 } - + fn can_sub_with(&self, other_type: &str) -> bool { matches!(other_type, "IntegerBox" | "FloatBox") } @@ -110,23 +116,26 @@ impl DynamicMul for IntegerBox { if let Some(other_int) = other.as_any().downcast_ref::() { return Some(Box::new(IntegerBox::new(self.value * other_int.value))); } - + // IntegerBox * FloatBox -> FloatBox if let Some(other_float) = other.as_any().downcast_ref::() { - 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 if let Some(other_str) = other.as_any().downcast_ref::() { - 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); return Some(Box::new(StringBox::new(repeated))); } } - + None } - + fn can_mul_with(&self, other_type: &str) -> bool { matches!(other_type, "IntegerBox" | "FloatBox" | "StringBox") } @@ -139,20 +148,24 @@ impl DynamicDiv for IntegerBox { if other_int.value == 0 { 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 if let Some(other_float) = other.as_any().downcast_ref::() { if other_float.value == 0.0 { 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 } - + fn can_div_with(&self, other_type: &str) -> bool { matches!(other_type, "IntegerBox" | "FloatBox") } @@ -163,7 +176,7 @@ impl DynamicDiv for IntegerBox { /// FloatBox + FloatBox -> FloatBox impl NyashAdd for FloatBox { type Output = FloatBox; - + fn add(self, rhs: FloatBox) -> Self::Output { FloatBox::new(self.value + rhs.value) } @@ -172,7 +185,7 @@ impl NyashAdd for FloatBox { /// FloatBox - FloatBox -> FloatBox impl NyashSub for FloatBox { type Output = FloatBox; - + fn sub(self, rhs: FloatBox) -> Self::Output { FloatBox::new(self.value - rhs.value) } @@ -181,7 +194,7 @@ impl NyashSub for FloatBox { /// FloatBox * FloatBox -> FloatBox impl NyashMul for FloatBox { type Output = FloatBox; - + fn mul(self, rhs: FloatBox) -> Self::Output { FloatBox::new(self.value * rhs.value) } @@ -190,7 +203,7 @@ impl NyashMul for FloatBox { /// FloatBox / FloatBox -> FloatBox (with zero check) impl NyashDiv for FloatBox { type Output = Result; - + fn div(self, rhs: FloatBox) -> Self::Output { if rhs.value == 0.0 { Err(OperatorError::DivisionByZero) @@ -208,18 +221,21 @@ impl DynamicAdd for FloatBox { if let Some(other_float) = other.as_any().downcast_ref::() { return Some(Box::new(FloatBox::new(self.value + other_float.value))); } - + // FloatBox + IntegerBox -> FloatBox if let Some(other_int) = other.as_any().downcast_ref::() { return Some(Box::new(FloatBox::new(self.value + other_int.value as f64))); } - + // Fallback: Convert both to strings and concatenate let left_str = self.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 { matches!(other_type, "FloatBox" | "IntegerBox" | "StringBox") } @@ -231,15 +247,15 @@ impl DynamicSub for FloatBox { if let Some(other_float) = other.as_any().downcast_ref::() { return Some(Box::new(FloatBox::new(self.value - other_float.value))); } - + // FloatBox - IntegerBox -> FloatBox if let Some(other_int) = other.as_any().downcast_ref::() { return Some(Box::new(FloatBox::new(self.value - other_int.value as f64))); } - + None // Subtraction not supported for other types } - + fn can_sub_with(&self, other_type: &str) -> bool { matches!(other_type, "FloatBox" | "IntegerBox") } @@ -251,15 +267,15 @@ impl DynamicMul for FloatBox { if let Some(other_float) = other.as_any().downcast_ref::() { return Some(Box::new(FloatBox::new(self.value * other_float.value))); } - + // FloatBox * IntegerBox -> FloatBox if let Some(other_int) = other.as_any().downcast_ref::() { return Some(Box::new(FloatBox::new(self.value * other_int.value as f64))); } - + None } - + fn can_mul_with(&self, other_type: &str) -> bool { matches!(other_type, "FloatBox" | "IntegerBox") } @@ -274,7 +290,7 @@ impl DynamicDiv for FloatBox { } return Some(Box::new(FloatBox::new(self.value / other_float.value))); } - + // FloatBox / IntegerBox -> FloatBox if let Some(other_int) = other.as_any().downcast_ref::() { if other_int.value == 0 { @@ -282,10 +298,10 @@ impl DynamicDiv for FloatBox { } return Some(Box::new(FloatBox::new(self.value / other_int.value as f64))); } - + None } - + fn can_div_with(&self, other_type: &str) -> bool { matches!(other_type, "FloatBox" | "IntegerBox") } @@ -296,7 +312,7 @@ impl DynamicDiv for FloatBox { /// StringBox + StringBox -> StringBox (concatenation) impl NyashAdd for StringBox { type Output = StringBox; - + fn add(self, rhs: StringBox) -> Self::Output { StringBox::new(format!("{}{}", self.value, rhs.value)) } @@ -305,9 +321,10 @@ impl NyashAdd for StringBox { /// StringBox * IntegerBox -> StringBox (repetition) impl NyashMul for StringBox { type Output = StringBox; - + 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)) } else { StringBox::new(String::new()) // Empty string for invalid repetition @@ -320,14 +337,20 @@ impl DynamicAdd for StringBox { fn try_add(&self, other: &dyn NyashBox) -> Option> { // StringBox + StringBox if let Some(other_str) = other.as_any().downcast_ref::() { - 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 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 { true // StringBox can concatenate with anything via to_string_box() } @@ -337,7 +360,7 @@ impl DynamicSub for StringBox { fn try_sub(&self, _other: &dyn NyashBox) -> Option> { None // Subtraction not defined for strings } - + fn can_sub_with(&self, _other_type: &str) -> bool { false } @@ -347,15 +370,16 @@ impl DynamicMul for StringBox { fn try_mul(&self, other: &dyn NyashBox) -> Option> { // StringBox * IntegerBox -> Repeated string if let Some(other_int) = other.as_any().downcast_ref::() { - 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); return Some(Box::new(StringBox::new(repeated))); } } - + None } - + fn can_mul_with(&self, other_type: &str) -> bool { matches!(other_type, "IntegerBox") } @@ -365,7 +389,7 @@ impl DynamicDiv for StringBox { fn try_div(&self, _other: &dyn NyashBox) -> Option> { None // Division not defined for strings } - + fn can_div_with(&self, _other_type: &str) -> bool { false } @@ -376,7 +400,7 @@ impl DynamicDiv for StringBox { /// BoolBox + BoolBox -> IntegerBox (logical OR as addition) impl NyashAdd for BoolBox { type Output = IntegerBox; - + fn add(self, rhs: BoolBox) -> Self::Output { let result = (self.value as i64) + (rhs.value as i64); IntegerBox::new(result) @@ -390,19 +414,22 @@ impl DynamicAdd for BoolBox { let result = (self.value as i64) + (other_bool.value as i64); return Some(Box::new(IntegerBox::new(result))); } - + // BoolBox + IntegerBox if let Some(other_int) = other.as_any().downcast_ref::() { let result = (self.value as i64) + other_int.value; return Some(Box::new(IntegerBox::new(result))); } - + // Fallback to string concatenation let left_str = self.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 { matches!(other_type, "BoolBox" | "IntegerBox" | "StringBox") } @@ -415,16 +442,16 @@ impl DynamicSub for BoolBox { let result = (self.value as i64) - (other_bool.value as i64); return Some(Box::new(IntegerBox::new(result))); } - + // BoolBox - IntegerBox if let Some(other_int) = other.as_any().downcast_ref::() { let result = (self.value as i64) - other_int.value; return Some(Box::new(IntegerBox::new(result))); } - + None } - + fn can_sub_with(&self, other_type: &str) -> bool { matches!(other_type, "BoolBox" | "IntegerBox") } @@ -437,16 +464,16 @@ impl DynamicMul for BoolBox { let result = (self.value as i64) * (other_bool.value as i64); return Some(Box::new(IntegerBox::new(result))); } - + // BoolBox * IntegerBox if let Some(other_int) = other.as_any().downcast_ref::() { let result = (self.value as i64) * other_int.value; return Some(Box::new(IntegerBox::new(result))); } - + None } - + fn can_mul_with(&self, other_type: &str) -> bool { matches!(other_type, "BoolBox" | "IntegerBox") } @@ -462,10 +489,10 @@ impl DynamicDiv for BoolBox { let result = (self.value as i64) / other_int.value; return Some(Box::new(IntegerBox::new(result))); } - + None } - + fn can_div_with(&self, other_type: &str) -> bool { matches!(other_type, "IntegerBox") } @@ -485,39 +512,42 @@ impl OperatorResolver { ) -> Result, OperatorError> { // Try to cast to concrete types first and use their DynamicAdd implementation // This approach uses the concrete types rather than trait objects - + // Check if left implements DynamicAdd by trying common types if let Some(int_box) = left.as_any().downcast_ref::() { if let Some(result) = int_box.try_add(right) { return Ok(result); } } - + if let Some(str_box) = left.as_any().downcast_ref::() { if let Some(result) = str_box.try_add(right) { return Ok(result); } } - - if let Some(float_box) = right.as_any().downcast_ref::() { + + if let Some(float_box) = right + .as_any() + .downcast_ref::() + { if let Some(result) = float_box.try_add(right) { return Ok(result); } } - + if let Some(bool_box) = left.as_any().downcast_ref::() { if let Some(result) = bool_box.try_add(right) { return Ok(result); } } - + Err(OperatorError::UnsupportedOperation { operator: "+".to_string(), left_type: left.type_name().to_string(), right_type: right.type_name().to_string(), }) } - + /// Resolve subtraction operation with hybrid dispatch pub fn resolve_sub( left: &dyn NyashBox, @@ -529,20 +559,23 @@ impl OperatorResolver { return Ok(result); } } - - if let Some(float_box) = left.as_any().downcast_ref::() { + + if let Some(float_box) = left + .as_any() + .downcast_ref::() + { if let Some(result) = float_box.try_sub(right) { return Ok(result); } } - + Err(OperatorError::UnsupportedOperation { operator: "-".to_string(), left_type: left.type_name().to_string(), right_type: right.type_name().to_string(), }) } - + /// Resolve multiplication operation with hybrid dispatch pub fn resolve_mul( left: &dyn NyashBox, @@ -554,32 +587,35 @@ impl OperatorResolver { return Ok(result); } } - + if let Some(str_box) = left.as_any().downcast_ref::() { if let Some(result) = str_box.try_mul(right) { return Ok(result); } } - - if let Some(float_box) = left.as_any().downcast_ref::() { + + if let Some(float_box) = left + .as_any() + .downcast_ref::() + { if let Some(result) = float_box.try_mul(right) { return Ok(result); } } - + if let Some(bool_box) = left.as_any().downcast_ref::() { if let Some(result) = bool_box.try_mul(right) { return Ok(result); } } - + Err(OperatorError::UnsupportedOperation { operator: "*".to_string(), left_type: left.type_name().to_string(), right_type: right.type_name().to_string(), }) } - + /// Resolve division operation with hybrid dispatch pub fn resolve_div( left: &dyn NyashBox, @@ -594,8 +630,11 @@ impl OperatorResolver { return Err(OperatorError::DivisionByZero); } } - - if let Some(float_box) = left.as_any().downcast_ref::() { + + if let Some(float_box) = left + .as_any() + .downcast_ref::() + { if let Some(result) = float_box.try_div(right) { return Ok(result); } else { @@ -603,7 +642,7 @@ impl OperatorResolver { return Err(OperatorError::DivisionByZero); } } - + if let Some(bool_box) = left.as_any().downcast_ref::() { if let Some(result) = bool_box.try_div(right) { return Ok(result); @@ -611,7 +650,7 @@ impl OperatorResolver { return Err(OperatorError::DivisionByZero); } } - + Err(OperatorError::UnsupportedOperation { operator: "/".to_string(), left_type: left.type_name().to_string(), @@ -652,7 +691,7 @@ mod tests { fn test_dynamic_addition() { let a = IntegerBox::new(10); let b = StringBox::new("20"); - + // Test dynamic dispatch let result = a.try_add(&b).unwrap(); let result_str = result.to_string_box(); @@ -672,9 +711,9 @@ mod tests { let int_box = IntegerBox::new(42); assert!(int_box.can_add_with("IntegerBox")); assert!(int_box.can_add_with("StringBox")); - + let str_box = StringBox::new("test"); assert!(str_box.can_add_with("IntegerBox")); assert!(str_box.can_add_with("StringBox")); } -} \ No newline at end of file +} diff --git a/src/box_trait.rs b/src/box_trait.rs index 4920ada2..6df148d3 100644 --- a/src/box_trait.rs +++ b/src/box_trait.rs @@ -1,17 +1,17 @@ /*! * Nyash Box Trait System - Everything is Box in Rust - * + * * This module implements the core "Everything is Box" philosophy using Rust's * ownership system and trait system. Every value in Nyash is a Box that * implements the NyashBox trait. */ -use std::fmt::{Debug, Display}; use std::any::Any; -use std::sync::Arc; -use std::sync::atomic::{AtomicU64, Ordering}; +use std::fmt::{Debug, Display}; use std::fs; use std::path::Path; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::Arc; // 🔥 新しい型エイリアス - 将来的にBoxを全て置き換える pub type SharedNyashBox = Arc; @@ -26,14 +26,38 @@ pub fn next_box_id() -> u64 { /// 🔥 Phase 8.8: pack透明化システム - ビルトインBox判定リスト /// ユーザーは`pack`を一切意識せず、`from BuiltinBox()`で自動的に内部のpack機能が呼ばれる pub const BUILTIN_BOXES: &[&str] = &[ - "StringBox", "IntegerBox", "BoolBox", "NullBox", - "ArrayBox", "MapBox", "FileBox", "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" + "StringBox", + "IntegerBox", + "BoolBox", + "NullBox", + "ArrayBox", + "MapBox", + "FileBox", + "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透明化システムの核心 @@ -59,7 +83,7 @@ impl BoxBase { parent_type_id: None, // ビルトインBox: 継承なし } } - + /// ビルトインBox継承用コンストラクタ pub fn with_parent_type(parent_type_id: std::any::TypeId) -> Self { Self { @@ -75,16 +99,16 @@ impl BoxBase { pub trait BoxCore: Send + Sync { /// ボックスの一意ID取得 fn box_id(&self) -> u64; - + /// 継承元の型ID取得(ビルトインBox継承用) fn parent_type_id(&self) -> Option; - + /// Display実装のための統一フォーマット fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result; - + /// Any変換(ダウンキャスト用) fn as_any(&self) -> &dyn Any; - + /// Anyミュータブル変換(ダウンキャスト用) fn as_any_mut(&mut self) -> &mut dyn Any; } @@ -94,34 +118,40 @@ pub trait BoxCore: Send + Sync { pub trait NyashBox: BoxCore + Debug { /// Convert this box to a string representation (equivalent to Python's toString()) fn to_string_box(&self) -> StringBox; - + /// Check equality with another box (equivalent to Python's equals()) fn equals(&self, other: &dyn NyashBox) -> BoolBox; - + /// Get the type name of this box for debugging fn type_name(&self) -> &'static str { std::any::type_name::() } - + /// Clone this box (equivalent to Python's copy()) fn clone_box(&self) -> Box; - + /// Share this box (state-preserving reference sharing) fn share_box(&self) -> Box; - + /// 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. fn clone_or_share(&self) -> Box { - if self.is_identity() { self.share_box() } else { self.clone_box() } + if self.is_identity() { + self.share_box() + } else { + self.clone_box() + } } - + /// Arc参照を返す新しいcloneメソッド(参照共有) fn clone_arc(&self) -> SharedNyashBox { Arc::from(self.clone_box()) } - + // 🌟 TypeBox革命: Get type information as a Box // Everything is Box極限実現 - 型情報もBoxとして取得! // TODO: 次のステップで完全実装 @@ -144,22 +174,23 @@ impl StringBox { base: BoxBase::new(), } } - + pub fn empty() -> Self { Self::new("") } - + // ===== String Methods for Nyash ===== - + /// Split string by delimiter and return ArrayBox pub fn split(&self, delimiter: &str) -> Box { let parts: Vec = self.value.split(delimiter).map(|s| s.to_string()).collect(); - let array_elements: Vec> = parts.into_iter() + let array_elements: Vec> = parts + .into_iter() .map(|s| Box::new(StringBox::new(s)) as Box) .collect(); Box::new(ArrayBox::new_with_elements(array_elements)) } - + /// Find substring and return position (or -1 if not found) pub fn find(&self, search: &str) -> Box { match self.value.find(search) { @@ -167,46 +198,49 @@ impl StringBox { None => Box::new(IntegerBox::new(-1)), } } - + /// Replace all occurrences of old with new pub fn replace(&self, old: &str, new: &str) -> Box { Box::new(StringBox::new(self.value.replace(old, new))) } - + /// Trim whitespace from both ends pub fn trim(&self) -> Box { Box::new(StringBox::new(self.value.trim())) } - + /// Convert to uppercase pub fn to_upper(&self) -> Box { Box::new(StringBox::new(self.value.to_uppercase())) } - + /// Convert to lowercase pub fn to_lower(&self) -> Box { Box::new(StringBox::new(self.value.to_lowercase())) } - + /// Check if string contains substring pub fn contains(&self, search: &str) -> Box { Box::new(BoolBox::new(self.value.contains(search))) } - + /// Check if string starts with prefix pub fn starts_with(&self, prefix: &str) -> Box { Box::new(BoolBox::new(self.value.starts_with(prefix))) } - + /// Check if string ends with suffix pub fn ends_with(&self, suffix: &str) -> Box { Box::new(BoolBox::new(self.value.ends_with(suffix))) } - + /// Join array elements using this string as delimiter pub fn join(&self, array_box: Box) -> Box { if let Some(array) = array_box.as_any().downcast_ref::() { - let strings: Vec = array.items.read().unwrap() + let strings: Vec = array + .items + .read() + .unwrap() .iter() .map(|element| element.to_string_box().value) .collect(); @@ -216,12 +250,12 @@ impl StringBox { Box::new(StringBox::new(array_box.to_string_box().value)) } } - + /// Get string length pub fn length(&self) -> Box { Box::new(IntegerBox::new(self.value.len() as i64)) } - + /// Convert string to integer (parse as i64) pub fn to_integer(&self) -> Box { match self.value.trim().parse::() { @@ -232,7 +266,7 @@ impl StringBox { } } } - + /// Get character at index pub fn get(&self, index: usize) -> Option> { if let Some(ch) = self.value.chars().nth(index) { @@ -241,7 +275,7 @@ impl StringBox { None } } - + /// Get substring from start to end (exclusive) pub fn substring(&self, start: usize, end: usize) -> Box { let chars: Vec = self.value.chars().collect(); @@ -256,19 +290,19 @@ impl BoxCore for StringBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.value) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -278,7 +312,7 @@ impl NyashBox for StringBox { fn to_string_box(&self) -> StringBox { self.clone() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_string) = other.as_any().downcast_ref::() { BoolBox::new(self.value == other_string.value) @@ -286,15 +320,15 @@ impl NyashBox for StringBox { BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { "StringBox" } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -316,12 +350,12 @@ pub struct IntegerBox { impl IntegerBox { pub fn new(value: i64) -> Self { - Self { - value, - base: BoxBase::new() + Self { + value, + base: BoxBase::new(), } } - + pub fn zero() -> Self { Self::new(0) } @@ -331,19 +365,19 @@ impl BoxCore for IntegerBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.value) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -353,7 +387,7 @@ impl NyashBox for IntegerBox { fn to_string_box(&self) -> StringBox { StringBox::new(self.value.to_string()) } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_int) = other.as_any().downcast_ref::() { BoolBox::new(self.value == other_int.value) @@ -361,15 +395,15 @@ impl NyashBox for IntegerBox { BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { "IntegerBox" } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -391,16 +425,16 @@ pub struct BoolBox { impl BoolBox { pub fn new(value: bool) -> Self { - Self { - value, - base: BoxBase::new() + Self { + value, + base: BoxBase::new(), } } - + pub fn true_box() -> Self { Self::new(true) } - + pub fn false_box() -> Self { Self::new(false) } @@ -410,19 +444,19 @@ impl BoxCore for BoolBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", if self.value { "true" } else { "false" }) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -432,7 +466,7 @@ impl NyashBox for BoolBox { fn to_string_box(&self) -> StringBox { StringBox::new(if self.value { "true" } else { "false" }) } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_bool) = other.as_any().downcast_ref::() { BoolBox::new(self.value == other_bool.value) @@ -440,15 +474,15 @@ impl NyashBox for BoolBox { BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { "BoolBox" } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -469,8 +503,8 @@ pub struct VoidBox { impl VoidBox { pub fn new() -> Self { - Self { - base: BoxBase::new() + Self { + base: BoxBase::new(), } } } @@ -485,19 +519,19 @@ impl BoxCore for VoidBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "void") } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -507,19 +541,19 @@ impl NyashBox for VoidBox { fn to_string_box(&self) -> StringBox { StringBox::new("void") } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::()) } - + fn type_name(&self) -> &'static str { "VoidBox" } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -549,9 +583,9 @@ impl FileBox { base: BoxBase::new(), } } - + // ===== File Methods for Nyash ===== - + /// Read file contents as string pub fn read(&self) -> Box { match fs::read_to_string(&self.path) { @@ -559,7 +593,7 @@ impl FileBox { Err(_) => Box::new(VoidBox::new()), // Return void on error for now } } - + /// Write content to file pub fn write(&self, content: Box) -> Box { let content_str = content.to_string_box().value; @@ -568,12 +602,12 @@ impl FileBox { Err(_) => Box::new(BoolBox::new(false)), } } - + /// Check if file exists pub fn exists(&self) -> Box { Box::new(BoolBox::new(Path::new(&self.path).exists())) } - + /// Delete file pub fn delete(&self) -> Box { match fs::remove_file(&self.path) { @@ -581,7 +615,7 @@ impl FileBox { Err(_) => Box::new(BoolBox::new(false)), } } - + /// Copy file to destination pub fn copy(&self, dest_path: &str) -> Box { match fs::copy(&self.path, dest_path) { @@ -595,19 +629,19 @@ impl BoxCore for FileBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "", self.path) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -617,7 +651,7 @@ impl NyashBox for FileBox { fn to_string_box(&self) -> StringBox { StringBox::new(format!("", self.path)) } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_file) = other.as_any().downcast_ref::() { BoolBox::new(self.path == other_file.path) @@ -625,15 +659,15 @@ impl NyashBox for FileBox { BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { "FileBox" } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -668,19 +702,19 @@ impl BoxCore for ErrorBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}: {}", self.error_type, self.message) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -690,23 +724,25 @@ impl NyashBox for ErrorBox { fn to_string_box(&self) -> StringBox { StringBox::new(format!("{}: {}", self.error_type, self.message)) } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_error) = other.as_any().downcast_ref::() { - 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 { BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { "ErrorBox" } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -719,17 +755,18 @@ impl Display for ErrorBox { } } - // 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 // 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)] mod tests { use super::*; - + #[test] fn test_string_box_creation() { let s = StringBox::new("Hello, Rust!"); @@ -737,7 +774,7 @@ mod tests { assert_eq!(s.type_name(), "StringBox"); assert_eq!(s.to_string_box().value, "Hello, Rust!"); } - + #[test] fn test_integer_box_creation() { let i = IntegerBox::new(42); @@ -745,7 +782,7 @@ mod tests { assert_eq!(i.type_name(), "IntegerBox"); assert_eq!(i.to_string_box().value, "42"); } - + #[test] fn test_bool_box_creation() { let b = BoolBox::new(true); @@ -753,48 +790,48 @@ mod tests { assert_eq!(b.type_name(), "BoolBox"); assert_eq!(b.to_string_box().value, "true"); } - + #[test] fn test_box_equality() { let s1 = StringBox::new("test"); let s2 = StringBox::new("test"); let s3 = StringBox::new("different"); - + assert!(s1.equals(&s2).value); assert!(!s1.equals(&s3).value); } - + #[test] fn test_add_box_integers() { let left = Box::new(IntegerBox::new(5)) as Box; let right = Box::new(IntegerBox::new(3)) as Box; let add = AddBox::new(left, right); - + let result = add.execute(); let result_int = result.as_any().downcast_ref::().unwrap(); assert_eq!(result_int.value, 8); } - + #[test] fn test_add_box_strings() { let left = Box::new(StringBox::new("Hello, ")) as Box; let right = Box::new(StringBox::new("Rust!")) as Box; let add = AddBox::new(left, right); - + let result = add.execute(); let result_str = result.as_any().downcast_ref::().unwrap(); assert_eq!(result_str.value, "Hello, Rust!"); } - + #[test] fn test_box_ids_unique() { let s1 = StringBox::new("test"); let s2 = StringBox::new("test"); - + // Same content but different IDs assert_ne!(s1.box_id(), s2.box_id()); } - + #[test] fn test_void_box() { let v = VoidBox::new(); diff --git a/src/boxes/aot_compiler_box.rs b/src/boxes/aot_compiler_box.rs index 200bf0c5..86e534ea 100644 --- a/src/boxes/aot_compiler_box.rs +++ b/src/boxes/aot_compiler_box.rs @@ -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; #[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 { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { 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 } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + 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 { - fn to_string_box(&self) -> StringBox { StringBox::new("AotCompilerBox".to_string()) } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::()) } - fn type_name(&self) -> &'static str { "AotCompilerBox" } - fn clone_box(&self) -> Box { Box::new(Self { base: self.base.clone() }) } - fn share_box(&self) -> Box { self.clone_box() } + fn to_string_box(&self) -> StringBox { + StringBox::new("AotCompilerBox".to_string()) + } + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + BoolBox::new(other.as_any().is::()) + } + fn type_name(&self) -> &'static str { + "AotCompilerBox" + } + fn clone_box(&self) -> Box { + Box::new(Self { + base: self.base.clone(), + }) + } + fn share_box(&self) -> Box { + self.clone_box() + } } impl AotCompilerBox { @@ -28,26 +58,32 @@ impl AotCompilerBox { pub fn compile(&self, file: &str, out: &str) -> Box { let mut cmd = match std::env::current_exe() { 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) - let c = cmd.arg("--backend").arg("vm") // ensures runner path - .arg("--compile-native") - .arg("-o").arg(out) - .arg(file) - .envs(std::env::vars()); + let c = cmd + .arg("--backend") + .arg("vm") // ensures runner path + .arg("--compile-native") + .arg("-o") + .arg(out) + .arg(file) + .envs(std::env::vars()); match c.output() { Ok(o) => { let mut s = String::new(); s.push_str(&String::from_utf8_lossy(&o.stdout)); s.push_str(&String::from_utf8_lossy(&o.stderr)); 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)) } - Err(e) => Box::new(StringBox::new(format!("ERR: spawn compile-native: {}", e))) + Err(e) => Box::new(StringBox::new(format!("ERR: spawn compile-native: {}", e))), } } } - diff --git a/src/boxes/aot_config_box.rs b/src/boxes/aot_config_box.rs index 955ca2a5..589a1cfa 100644 --- a/src/boxes/aot_config_box.rs +++ b/src/boxes/aot_config_box.rs @@ -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; #[derive(Debug, Clone)] @@ -10,44 +10,104 @@ pub struct AotConfigBox { pub plugin_paths: Option, } -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 { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { 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 } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + 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 { - fn to_string_box(&self) -> StringBox { self.summary() } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::()) } - fn type_name(&self) -> &'static str { "AotConfigBox" } - fn clone_box(&self) -> Box { 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 { self.clone_box() } + fn to_string_box(&self) -> StringBox { + self.summary() + } + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + BoolBox::new(other.as_any().is::()) + } + fn type_name(&self) -> &'static str { + "AotConfigBox" + } + fn clone_box(&self) -> Box { + 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 { + self.clone_box() + } } impl AotConfigBox { - pub fn set_output(&mut self, path: &str) -> Box { self.output_file = Some(path.to_string()); Box::new(VoidBox::new()) } - pub fn set_obj_out(&mut self, path: &str) -> Box { self.emit_obj_out = Some(path.to_string()); Box::new(VoidBox::new()) } - pub fn set_plugin_paths(&mut self, paths: &str) -> Box { self.plugin_paths = Some(paths.to_string()); Box::new(VoidBox::new()) } - pub fn clear(&mut self) -> Box { self.output_file = None; self.emit_obj_out = None; self.plugin_paths = None; Box::new(VoidBox::new()) } + pub fn set_output(&mut self, path: &str) -> Box { + self.output_file = Some(path.to_string()); + Box::new(VoidBox::new()) + } + pub fn set_obj_out(&mut self, path: &str) -> Box { + self.emit_obj_out = Some(path.to_string()); + Box::new(VoidBox::new()) + } + pub fn set_plugin_paths(&mut self, paths: &str) -> Box { + self.plugin_paths = Some(paths.to_string()); + Box::new(VoidBox::new()) + } + pub fn clear(&mut self) -> Box { + 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 pub fn apply(&self) -> Box { - if let Some(p) = &self.output_file { std::env::set_var("NYASH_AOT_OUT", 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); } + if let Some(p) = &self.output_file { + std::env::set_var("NYASH_AOT_OUT", 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()) } pub fn summary(&self) -> StringBox { let s = format!( "output={} obj_out={} plugin_paths={}", - self.output_file.clone().unwrap_or_else(|| "".to_string()), - self.emit_obj_out.clone().unwrap_or_else(|| "".to_string()), - self.plugin_paths.clone().unwrap_or_else(|| "".to_string()), + self.output_file + .clone() + .unwrap_or_else(|| "".to_string()), + self.emit_obj_out + .clone() + .unwrap_or_else(|| "".to_string()), + self.plugin_paths + .clone() + .unwrap_or_else(|| "".to_string()), ); StringBox::new(s) } diff --git a/src/boxes/array/mod.rs b/src/boxes/array/mod.rs index cb0ca834..9a48cfd7 100644 --- a/src/boxes/array/mod.rs +++ b/src/boxes/array/mod.rs @@ -2,39 +2,39 @@ // Nyashの箱システムによる配列・リスト操作を提供します。 // RwLockパターンで内部可変性を実現(Phase 9.75-B Arc削除) -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::sync::{Arc, RwLock}; use std::fmt::Display; +use std::sync::{Arc, RwLock}; pub struct ArrayBox { - pub items: Arc>>>, // Arc追加 + pub items: Arc>>>, // Arc追加 base: BoxBase, } impl ArrayBox { /// 新しいArrayBoxを作成 pub fn new() -> Self { - ArrayBox { - items: Arc::new(RwLock::new(Vec::new())), // Arc::new追加 + ArrayBox { + items: Arc::new(RwLock::new(Vec::new())), // Arc::new追加 base: BoxBase::new(), } } - + /// 要素を持つArrayBoxを作成 pub fn new_with_elements(elements: Vec>) -> Self { - ArrayBox { - items: Arc::new(RwLock::new(elements)), // Arc::new追加 + ArrayBox { + items: Arc::new(RwLock::new(elements)), // Arc::new追加 base: BoxBase::new(), } } - + /// 要素を追加 pub fn push(&self, item: Box) -> Box { self.items.write().unwrap().push(item); Box::new(StringBox::new("ok")) } - + /// 最後の要素を取り出す pub fn pop(&self) -> Box { match self.items.write().unwrap().pop() { @@ -42,7 +42,7 @@ impl ArrayBox { None => Box::new(crate::boxes::null_box::NullBox::new()), } } - + /// 要素数を取得 pub fn length(&self) -> Box { Box::new(IntegerBox::new(self.items.read().unwrap().len() as i64)) @@ -52,7 +52,7 @@ impl ArrayBox { pub fn len(&self) -> usize { self.items.read().unwrap().len() } - + /// インデックスで要素を取得 pub fn get(&self, index: Box) -> Box { if let Some(idx_box) = index.as_any().downcast_ref::() { @@ -61,7 +61,11 @@ impl ArrayBox { match items.get(idx) { Some(item) => { #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] - if item.as_any().downcast_ref::().is_some() { + if item + .as_any() + .downcast_ref::() + .is_some() + { return item.share_box(); } item.clone_box() @@ -72,7 +76,7 @@ impl ArrayBox { Box::new(StringBox::new("Error: get() requires integer index")) } } - + /// インデックスで要素を設定 pub fn set(&self, index: Box, value: Box) -> Box { if let Some(idx_box) = index.as_any().downcast_ref::() { @@ -92,7 +96,7 @@ impl ArrayBox { Box::new(StringBox::new("Error: set() requires integer index")) } } - + /// 要素を削除 pub fn remove(&self, index: Box) -> Box { if let Some(idx_box) = index.as_any().downcast_ref::() { @@ -107,7 +111,7 @@ impl ArrayBox { Box::new(StringBox::new("Error: remove() requires integer index")) } } - + /// 指定された値のインデックスを検索 pub fn indexOf(&self, value: Box) -> Box { let items = self.items.read().unwrap(); @@ -118,7 +122,7 @@ impl ArrayBox { } Box::new(IntegerBox::new(-1)) } - + /// 指定された値が含まれているか確認 pub fn contains(&self, value: Box) -> Box { let items = self.items.read().unwrap(); @@ -129,13 +133,13 @@ impl ArrayBox { } Box::new(BoolBox::new(false)) } - + /// 配列を空にする pub fn clear(&self) -> Box { self.items.write().unwrap().clear(); Box::new(StringBox::new("ok")) } - + /// 文字列結合 pub fn join(&self, delimiter: Box) -> Box { if let Some(sep_box) = delimiter.as_any().downcast_ref::() { @@ -149,66 +153,78 @@ impl ArrayBox { Box::new(StringBox::new("Error: join() requires string separator")) } } - + /// 配列をソート(昇順) pub fn sort(&self) -> Box { let mut items = self.items.write().unwrap(); - + // Numeric values first, then string values items.sort_by(|a, b| { use std::cmp::Ordering; - + // Try to compare as numbers first if let (Some(a_int), Some(b_int)) = ( a.as_any().downcast_ref::(), - b.as_any().downcast_ref::() + b.as_any().downcast_ref::(), ) { return a_int.value.cmp(&b_int.value); } - + // Try FloatBox comparison if let (Some(a_float), Some(b_float)) = ( - a.as_any().downcast_ref::(), - b.as_any().downcast_ref::() + a.as_any() + .downcast_ref::(), + b.as_any() + .downcast_ref::(), ) { - 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 if let (Some(a_int), Some(b_float)) = ( a.as_any().downcast_ref::(), - b.as_any().downcast_ref::() + b.as_any() + .downcast_ref::(), ) { - 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)) = ( - a.as_any().downcast_ref::(), - b.as_any().downcast_ref::() + a.as_any() + .downcast_ref::(), + b.as_any().downcast_ref::(), ) { - 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 let a_str = a.to_string_box().value; let b_str = b.to_string_box().value; a_str.cmp(&b_str) }); - + Box::new(StringBox::new("ok")) } - + /// 配列を反転 pub fn reverse(&self) -> Box { let mut items = self.items.write().unwrap(); items.reverse(); Box::new(StringBox::new("ok")) } - + /// 部分配列を取得 pub fn slice(&self, start: Box, end: Box) -> Box { let items = self.items.read().unwrap(); - + // Extract start and end indices let start_idx = if let Some(start_int) = start.as_any().downcast_ref::() { if start_int.value < 0 { @@ -217,9 +233,11 @@ impl ArrayBox { start_int.value as usize } } 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::() { if end_int.value < 0 { items.len() @@ -227,26 +245,32 @@ impl ArrayBox { (end_int.value as usize).min(items.len()) } } 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 if start_idx > items.len() || start_idx > end_idx { return Box::new(ArrayBox::new()); } - + // Create slice let slice_items: Vec> = items[start_idx..end_idx] .iter() .map(|item| { #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] - if item.as_any().downcast_ref::().is_some() { + if item + .as_any() + .downcast_ref::() + .is_some() + { return item.share_box(); } item.clone_box() }) .collect(); - + Box::new(ArrayBox::new_with_elements(slice_items)) } } @@ -256,18 +280,23 @@ impl Clone for ArrayBox { fn clone(&self) -> Self { // ディープコピー(独立インスタンス) let items_guard = self.items.read().unwrap(); - let cloned_items: Vec> = items_guard.iter() + let cloned_items: Vec> = items_guard + .iter() .map(|item| { #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] - if item.as_any().downcast_ref::().is_some() { + if item + .as_any() + .downcast_ref::() + .is_some() + { return item.share_box(); } item.clone_box() - }) // 要素もディープコピー(ハンドルは共有) + }) // 要素もディープコピー(ハンドルは共有) .collect(); - + ArrayBox { - items: Arc::new(RwLock::new(cloned_items)), // 新しいArc + items: Arc::new(RwLock::new(cloned_items)), // 新しいArc base: BoxBase::new(), } } @@ -277,23 +306,24 @@ impl BoxCore for ArrayBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let items = self.items.read().unwrap(); - let strings: Vec = items.iter() + let strings: Vec = items + .iter() .map(|item| item.to_string_box().value) .collect(); write!(f, "[{}]", strings.join(", ")) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -306,49 +336,50 @@ impl Display for ArrayBox { } impl NyashBox for ArrayBox { - fn is_identity(&self) -> bool { true } + fn is_identity(&self) -> bool { + true + } fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 🎯 状態共有の核心実装 fn share_box(&self) -> Box { let new_instance = ArrayBox { - items: Arc::clone(&self.items), // Arcクローンで状態共有 - base: BoxBase::new(), // 新しいID + items: Arc::clone(&self.items), // Arcクローンで状態共有 + base: BoxBase::new(), // 新しいID }; Box::new(new_instance) } fn to_string_box(&self) -> StringBox { let items = self.items.read().unwrap(); - let strings: Vec = items.iter() + let strings: Vec = items + .iter() .map(|item| item.to_string_box().value) .collect(); StringBox::new(format!("[{}]", strings.join(", "))) } - fn type_name(&self) -> &'static str { "ArrayBox" } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_array) = other.as_any().downcast_ref::() { let self_items = self.items.read().unwrap(); let other_items = other_array.items.read().unwrap(); - + if self_items.len() != other_items.len() { return BoolBox::new(false); } - + for (a, b) in self_items.iter().zip(other_items.iter()) { if !a.equals(b.as_ref()).value { return BoolBox::new(false); } } - + BoolBox::new(true) } else { BoolBox::new(false) diff --git a/src/boxes/audio_box.rs b/src/boxes/audio_box.rs index f77aed5a..d2be6532 100644 --- a/src/boxes/audio_box.rs +++ b/src/boxes/audio_box.rs @@ -1,56 +1,56 @@ /*! * AudioBox - 音声再生・合成Box - * + * * ## 📝 概要 * Web Audio APIを使用してブラウザでの音声再生、 * 合成、エフェクト処理を統一的に管理するBox。 * ゲーム、音楽アプリ、オーディオビジュアライザー開発に最適。 - * + * * ## 🛠️ 利用可能メソッド - * + * * ### 🔊 基本再生 * - `loadAudio(url)` - 音声ファイル読み込み * - `play()` - 再生開始 * - `pause()` - 一時停止 * - `stop()` - 停止 * - `setVolume(volume)` - 音量設定 (0.0-1.0) - * + * * ### 🎵 音声合成 * - `createTone(frequency, duration)` - 純音生成 * - `createNoise(type, duration)` - ノイズ生成 * - `createBeep()` - システム音 - * + * * ### 📊 解析・ビジュアライザー * - `getFrequencyData()` - 周波数解析データ取得 * - `getWaveformData()` - 波形データ取得 * - `getVolume()` - 現在の音量レベル - * + * * ### 🎛️ エフェクト * - `addReverb(room)` - リバーブエフェクト * - `addFilter(type, frequency)` - フィルター適用 * - `addDistortion(amount)` - ディストーション - * + * * ## 💡 使用例 * ```nyash * local audio, visualizer * audio = new AudioBox() - * + * * // 効果音再生 * audio.loadAudio("sounds/explosion.wav") * audio.setVolume(0.7) * audio.play() - * + * * // 音声合成 * audio.createTone(440, 1000) // A4音を1秒 * audio.createBeep() // システム音 - * + * * // オーディオビジュアライザー * local freqData = audio.getFrequencyData() * // freqDataを使用してcanvasに描画 * ``` */ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use std::any::Any; #[cfg(target_arch = "wasm32")] @@ -58,8 +58,8 @@ use wasm_bindgen::prelude::*; #[cfg(target_arch = "wasm32")] use web_sys::{ - AudioContext, AudioBuffer, AudioBufferSourceNode, GainNode, - AnalyserNode, AudioDestinationNode, PeriodicWave, OscillatorNode + AnalyserNode, AudioBuffer, AudioBufferSourceNode, AudioContext, AudioDestinationNode, GainNode, + OscillatorNode, PeriodicWave, }; /// 音声管理Box @@ -82,7 +82,7 @@ impl AudioBox { /// 音量を設定 (0.0 - 1.0) pub fn set_volume(&mut self, volume: f64) { self.volume = volume.max(0.0).min(1.0); - + #[cfg(target_arch = "wasm32")] { if let Some(gain) = &self.gain_node { @@ -104,21 +104,24 @@ impl AudioBox { if let Ok(gain) = context.create_gain() { // 周波数設定 oscillator.frequency().set_value(frequency as f32); - + // 音量設定 gain.gain().set_value(self.volume as f32); - + // ノード接続 - oscillator.connect_with_audio_node(&gain).unwrap_or_default(); - gain.connect_with_audio_node(&context.destination()).unwrap_or_default(); - + oscillator + .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 end_time = start_time + duration_ms / 1000.0; - + oscillator.start_with_when(start_time).unwrap_or_default(); oscillator.stop_with_when(end_time).unwrap_or_default(); - + return true; } } @@ -138,7 +141,7 @@ impl AudioBox { if let Some(context) = &self.context { let sample_rate = context.sample_rate() as usize; let length = ((duration_ms / 1000.0) * sample_rate as f64) as u32; - + if let Ok(buffer) = context.create_buffer(1, length, sample_rate as f32) { if let Ok(channel_data) = buffer.get_channel_data(0) { // ホワイトノイズデータ生成 @@ -146,15 +149,16 @@ impl AudioBox { let noise = (js_sys::Math::random() - 0.5) * 2.0; // -1.0 to 1.0 channel_data.set_index(i, noise as f32); } - + if let Ok(source) = context.create_buffer_source() { source.set_buffer(Some(&buffer)); - + if let Ok(gain) = context.create_gain() { gain.gain().set_value(self.volume as f32); 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(); return true; } @@ -171,7 +175,7 @@ impl AudioBox { if let Some(analyser) = &self.analyser_node { let buffer_length = analyser.frequency_bin_count() as usize; let mut data_array = vec![0u8; buffer_length]; - + // 周波数データを取得 analyser.get_byte_frequency_data(&mut data_array); return data_array; @@ -185,7 +189,7 @@ impl AudioBox { if let Some(analyser) = &self.analyser_node { let buffer_length = analyser.fft_size() as usize; let mut data_array = vec![0u8; buffer_length]; - + // 時間領域データを取得 analyser.get_byte_time_domain_data(&mut data_array); return data_array; @@ -201,7 +205,10 @@ impl AudioBox { #[cfg(not(target_arch = "wasm32"))] /// Non-WASM環境用のダミー実装 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 } @@ -220,13 +227,17 @@ impl AudioBox { #[cfg(not(target_arch = "wasm32"))] pub fn get_frequency_data(&self) -> Vec { // シミュレーション用のダミーデータ - (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"))] pub fn get_waveform_data(&self) -> Vec { // シミュレーション用のダミーデータ - (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() } /// オーディオコンテキストの状態を確認 @@ -260,19 +271,23 @@ impl BoxCore for AudioBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + 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 { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -282,20 +297,23 @@ impl NyashBox for AudioBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } 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 { "AudioBox" } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_audio) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_audio.base.id) @@ -309,4 +327,4 @@ impl std::fmt::Display for AudioBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/bool_box.rs b/src/boxes/bool_box.rs index f68bd195..5b573a57 100644 --- a/src/boxes/bool_box.rs +++ b/src/boxes/bool_box.rs @@ -1,44 +1,44 @@ /*! ✅ BoolBox - 真偽値Box - * + * * ## 📝 概要 * true/false値を扱うためのBox。 * JavaScript Boolean型のように直感的な論理演算が可能。 - * + * * ## 🛠️ 利用可能メソッド * - `toString()` - 文字列変換 ("true" / "false") * - `not()` - 論理NOT (演算子: not) * - `and(other)` - 論理AND (演算子: and) * - `or(other)` - 論理OR (演算子: or) * - `equals(other)` - 等価比較 (演算子: ==) - * + * * ## 💡 使用例 * ```nyash * local flag, result, text * flag = true - * + * * result = not flag // false * result = flag and true // true * result = flag or false // true * text = flag.toString() // "true" - * + * * // 条件分岐での利用 * if (flag) { * print("Flag is true!") * } * ``` - * + * * ## 🔄 型変換 * - 数値への変換: true → 1, false → 0 * - 文字列への変換: "true" / "false" * - 空文字・null・0は false として扱われる - * + * * ## ⚡ 論理演算子実装済み * - `not condition` - NOT演算子 * - `a and b` - AND演算子 * - `a or b` - OR演算子 */ -use crate::box_trait::{NyashBox, BoxCore, BoxBase}; +use crate::box_trait::{BoxBase, BoxCore, NyashBox}; use std::any::Any; use std::fmt::Display; @@ -51,16 +51,16 @@ pub struct BoolBox { impl BoolBox { pub fn new(value: bool) -> Self { - Self { - value, + Self { + value, base: BoxBase::new(), } } - + pub fn true_box() -> Self { Self::new(true) } - + pub fn false_box() -> Self { Self::new(false) } @@ -70,7 +70,7 @@ impl NyashBox for BoolBox { fn to_string_box(&self) -> crate::box_trait::StringBox { crate::box_trait::StringBox::new(if self.value { "true" } else { "false" }) } - + fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox { if let Some(other_bool) = other.as_any().downcast_ref::() { crate::box_trait::BoolBox::new(self.value == other_bool.value) @@ -78,16 +78,15 @@ impl NyashBox for BoolBox { crate::box_trait::BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { "BoolBox" } - - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -98,19 +97,19 @@ impl BoxCore for BoolBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", if self.value { "true" } else { "false" }) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -120,4 +119,4 @@ impl Display for BoolBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/buffer/mod.rs b/src/boxes/buffer/mod.rs index 1b309d83..c58787db 100644 --- a/src/boxes/buffer/mod.rs +++ b/src/boxes/buffer/mod.rs @@ -1,9 +1,9 @@ /*! 📊 BufferBox - バイナリデータ処理Box - * + * * ## 📝 概要 * バイナリデータの読み書きを扱うBox。 * ファイル操作、ネットワーク通信、画像処理などで使用。 - * + * * ## 🛠️ 利用可能メソッド * - `write(data)` - バイトデータ書き込み * - `read(count)` - 指定バイト数読み取り @@ -12,37 +12,37 @@ * - `length()` - データサイズ取得 * - `append(buffer)` - 他のBufferを追加 * - `slice(start, end)` - 部分データ取得 - * + * * ## 💡 使用例 * ```nyash * local buffer * buffer = new BufferBox() - * + * * // データ書き込み * buffer.write([72, 101, 108, 108, 111]) // "Hello" * print("Size: " + buffer.length()) - * + * * // データ読み取り * local data * data = buffer.readAll() * ``` */ -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 std::any::Any; -use std::sync::{Arc, RwLock}; // Arc追加 use std::fmt::Display; +use std::sync::{Arc, RwLock}; // Arc追加 pub struct BufferBox { - data: Arc>>, // Arc追加 + data: Arc>>, // Arc追加 base: BoxBase, } impl BufferBox { pub fn new() -> Self { - BufferBox { - data: Arc::new(RwLock::new(Vec::new())), // Arc::new追加 + BufferBox { + data: Arc::new(RwLock::new(Vec::new())), // Arc::new追加 base: BoxBase::new(), } } @@ -56,18 +56,21 @@ impl BufferBox { pub fn len(&self) -> usize { self.data.read().unwrap().len() } - + pub fn from_vec(data: Vec) -> Self { - BufferBox { - data: Arc::new(RwLock::new(data)), // Arc::new追加 + BufferBox { + data: Arc::new(RwLock::new(data)), // Arc::new追加 base: BoxBase::new(), } } - + /// データを書き込む pub fn write(&self, data: Box) -> Box { // ArrayBoxから変換 - use crate::boxes::array::ArrayBox directly - if let Some(array_box) = data.as_any().downcast_ref::() { + if let Some(array_box) = data + .as_any() + .downcast_ref::() + { let mut buffer = self.data.write().unwrap(); let items = array_box.items.read().unwrap(); for item in items.iter() { @@ -80,10 +83,13 @@ impl BufferBox { Box::new(IntegerBox::new(buffer.len() as i64)) } else { 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 + ))) } } - + /// すべてのデータを読み取る pub fn readAll(&self) -> Box { let buffer = self.data.read().unwrap(); @@ -93,14 +99,14 @@ impl BufferBox { } Box::new(array) } - + /// 指定バイト数読み取る pub fn read(&self, count: Box) -> Box { if let Some(count_int) = count.as_any().downcast_ref::() { let mut buffer = self.data.write().unwrap(); let count = count_int.value.min(buffer.len() as i64) as usize; let array = ArrayBox::new(); - + // 先頭からcount個取り出す let bytes: Vec = buffer.drain(0..count).collect(); for byte in bytes { @@ -111,18 +117,18 @@ impl BufferBox { Box::new(StringBox::new("Error: read() requires integer count")) } } - + /// バッファをクリア pub fn clear(&self) -> Box { self.data.write().unwrap().clear(); Box::new(StringBox::new("ok")) } - + /// データサイズを取得 pub fn length(&self) -> Box { Box::new(IntegerBox::new(self.data.read().unwrap().len() as i64)) } - + /// 他のBufferBoxを追加 pub fn append(&self, other: Box) -> Box { if let Some(other_buffer) = other.as_any().downcast_ref::() { @@ -134,17 +140,17 @@ impl BufferBox { Box::new(StringBox::new("Error: append() requires BufferBox")) } } - + /// 部分データ取得 pub fn slice(&self, start: Box, end: Box) -> Box { if let (Some(start_int), Some(end_int)) = ( start.as_any().downcast_ref::(), - end.as_any().downcast_ref::() + end.as_any().downcast_ref::(), ) { let data = self.data.read().unwrap(); let start = (start_int.value as usize).min(data.len()); let end = (end_int.value as usize).min(data.len()); - + if start <= end { let slice_data = data[start..end].to_vec(); Box::new(BufferBox::from_vec(slice_data)) @@ -155,7 +161,7 @@ impl BufferBox { Box::new(StringBox::new("Error: slice() requires integer indices")) } } - + /// ⭐ Phase 10: Zero-copy detection - check if buffer is shared with another buffer pub fn is_shared_with(&self, other: Box) -> Box { if let Some(other_buffer) = other.as_any().downcast_ref::() { @@ -167,17 +173,17 @@ impl BufferBox { Box::new(BoolBox::new(false)) } } - + /// ⭐ Phase 10: Share reference - create a zero-copy shared reference to this buffer's data pub fn share_reference(&self, _data: Box) -> Box { // Create a new BufferBox that shares the same Arc as this buffer let shared_buffer = BufferBox { data: Arc::clone(&self.data), // Share THIS buffer's data - base: BoxBase::new(), // New ID but shared data + base: BoxBase::new(), // New ID but shared data }; Box::new(shared_buffer) } - + /// ⭐ Phase 10: Memory footprint - get current memory usage in bytes pub fn memory_footprint(&self) -> Box { let data = self.data.read().unwrap(); @@ -192,7 +198,7 @@ impl Clone for BufferBox { // ディープコピー(独立インスタンス) let data_guard = self.data.read().unwrap(); BufferBox { - data: Arc::new(RwLock::new(data_guard.clone())), // 新しいArc + data: Arc::new(RwLock::new(data_guard.clone())), // 新しいArc base: BoxBase::new(), } } @@ -202,20 +208,20 @@ impl BoxCore for BufferBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let data = self.data.read().unwrap(); write!(f, "BufferBox({} bytes)", data.len()) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -231,12 +237,12 @@ impl NyashBox for BufferBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - - /// 🎯 状态共享的核心实现 + + /// 🎯 状态共享的核心实现 fn share_box(&self) -> Box { let new_instance = BufferBox { - data: Arc::clone(&self.data), // Arcクローンで状態共有 - base: BoxBase::new(), // 新しいID + data: Arc::clone(&self.data), // Arcクローンで状態共有 + base: BoxBase::new(), // 新しいID }; Box::new(new_instance) } @@ -246,12 +252,10 @@ impl NyashBox for BufferBox { StringBox::new(format!("BufferBox({} bytes)", data.len())) } - fn type_name(&self) -> &'static str { "BufferBox" } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_buffer) = other.as_any().downcast_ref::() { // RwLock内容を比較 @@ -264,7 +268,7 @@ impl NyashBox for BufferBox { } } -// Debug implementation for BufferBox +// Debug implementation for BufferBox impl std::fmt::Debug for BufferBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let data = self.data.read().unwrap(); diff --git a/src/boxes/canvas_event_box.rs b/src/boxes/canvas_event_box.rs index 3ca7258c..dda73483 100644 --- a/src/boxes/canvas_event_box.rs +++ b/src/boxes/canvas_event_box.rs @@ -1,45 +1,45 @@ /*! * CanvasEventBox - Canvas入力イベント管理Box - * + * * ## 📝 概要 * HTML5 Canvasでのマウス・タッチ・キーボードイベントを * Nyashから利用可能にするBox。ゲーム開発、インタラクティブ * アプリケーション開発に必須の入力機能を提供。 - * + * * ## 🛠️ 利用可能メソッド - * + * * ### 🖱️ マウスイベント * - `onMouseDown(callback)` - マウスボタン押下 * - `onMouseUp(callback)` - マウスボタン離上 * - `onMouseMove(callback)` - マウス移動 * - `onMouseClick(callback)` - マウスクリック * - `onMouseWheel(callback)` - マウスホイール - * + * * ### 👆 タッチイベント * - `onTouchStart(callback)` - タッチ開始 * - `onTouchMove(callback)` - タッチ移動 * - `onTouchEnd(callback)` - タッチ終了 - * + * * ### ⌨️ キーボードイベント * - `onKeyDown(callback)` - キー押下 * - `onKeyUp(callback)` - キー離上 - * + * * ### 📊 座標取得 * - `getMouseX()` - 現在のマウスX座標 * - `getMouseY()` - 現在のマウスY座標 * - `isPressed(button)` - ボタン押下状態確認 - * + * * ## 💡 使用例 * ```nyash * local events, canvas * events = new CanvasEventBox("game-canvas") * canvas = new WebCanvasBox("game-canvas", 800, 600) - * + * * // マウスクリックで円を描画 * events.onMouseClick(function(x, y) { * canvas.fillCircle(x, y, 10, "red") * }) - * + * * // ドラッグで線を描画 * local isDrawing = false * events.onMouseDown(function(x, y) { @@ -47,31 +47,28 @@ * canvas.beginPath() * canvas.moveTo(x, y) * }) - * + * * events.onMouseMove(function(x, y) { * if (isDrawing) { * canvas.lineTo(x, y) * canvas.stroke("black", 2) * } * }) - * + * * events.onMouseUp(function() { * isDrawing = false * }) * ``` */ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use std::any::Any; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; #[cfg(target_arch = "wasm32")] -use web_sys::{ - HtmlCanvasElement, MouseEvent, TouchEvent, KeyboardEvent, - EventTarget, Element -}; +use web_sys::{Element, EventTarget, HtmlCanvasElement, KeyboardEvent, MouseEvent, TouchEvent}; /// Canvas入力イベント管理Box #[derive(Debug, Clone)] @@ -140,7 +137,8 @@ impl CanvasEventBox { callback.call0(&JsValue::NULL).unwrap_or_default(); }) as Box); - 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(); closure.forget(); // メモリリークを防ぐため通常は適切な管理が必要 } @@ -154,7 +152,8 @@ impl CanvasEventBox { callback.call0(&JsValue::NULL).unwrap_or_default(); }) as Box); - 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(); closure.forget(); } @@ -168,7 +167,8 @@ impl CanvasEventBox { callback.call0(&JsValue::NULL).unwrap_or_default(); }) as Box); - 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(); closure.forget(); } @@ -182,7 +182,8 @@ impl CanvasEventBox { callback.call0(&JsValue::NULL).unwrap_or_default(); }) as Box); - 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(); closure.forget(); } @@ -196,7 +197,8 @@ impl CanvasEventBox { callback.call0(&JsValue::NULL).unwrap_or_default(); }) as Box); - 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(); closure.forget(); } @@ -210,7 +212,8 @@ impl CanvasEventBox { callback.call0(&JsValue::NULL).unwrap_or_default(); }) as Box); - 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(); closure.forget(); } @@ -252,19 +255,19 @@ impl BoxCore for CanvasEventBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "CanvasEventBox({})", self.canvas_id) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -274,7 +277,7 @@ impl NyashBox for CanvasEventBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -287,7 +290,7 @@ impl NyashBox for CanvasEventBox { fn type_name(&self) -> &'static str { "CanvasEventBox" } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_events) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_events.base.id) @@ -301,4 +304,4 @@ impl std::fmt::Display for CanvasEventBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/canvas_loop_box.rs b/src/boxes/canvas_loop_box.rs index 6e288814..7485e053 100644 --- a/src/boxes/canvas_loop_box.rs +++ b/src/boxes/canvas_loop_box.rs @@ -1,29 +1,29 @@ /*! * CanvasLoopBox - アニメーションループ管理Box - * + * * ## 📝 概要 * ゲームや動的コンテンツのためのアニメーションループを * 管理するBox。requestAnimationFrame、フレームレート制御、 * ループ状態管理を統一的に提供。 - * + * * ## 🛠️ 利用可能メソッド - * + * * ### 🎮 ループ制御 * - `start(callback)` - アニメーションループ開始 * - `stop()` - アニメーションループ停止 * - `pause()` - アニメーションループ一時停止 * - `resume()` - アニメーションループ再開 - * + * * ### 📊 フレーム情報 * - `getFPS()` - 現在のFPS取得 * - `getFrameCount()` - 総フレーム数取得 * - `getDeltaTime()` - 前フレームからの経過時間 * - `setTargetFPS(fps)` - 目標FPS設定 - * + * * ### ⏱️ 時間管理 * - `getElapsedTime()` - ループ開始からの経過時間 * - `reset()` - タイマーリセット - * + * * ## 💡 使用例 * ```nyash * local loop, canvas, ball_x, ball_y @@ -31,7 +31,7 @@ * canvas = new WebCanvasBox("game-canvas", 800, 600) * ball_x = 400 * ball_y = 300 - * + * * // ゲームループ * loop.start(function(deltaTime) { * // 更新処理 @@ -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 std::any::Any; @@ -75,7 +75,7 @@ impl CanvasLoopBox { pub fn new() -> Self { let timer = TimerBox::new(); let current_time = timer.now(); - + Self { base: BoxBase::new(), is_running: false, @@ -108,12 +108,16 @@ impl CanvasLoopBox { // アニメーションフレーム用のクロージャを作成 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); - 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); - + closure.forget(); // クロージャの所有権を手放す } @@ -147,9 +151,9 @@ impl CanvasLoopBox { if !self.is_running || self.is_paused { return; } - + self.is_paused = true; - + #[cfg(target_arch = "wasm32")] { if let Some(id) = self.animation_id { @@ -170,12 +174,16 @@ impl CanvasLoopBox { self.last_frame_time = self.timer.now(); // 時間をリセット 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); - 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); - + closure.forget(); } @@ -262,19 +270,23 @@ impl BoxCore for CanvasLoopBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + 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 { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -284,20 +296,23 @@ impl NyashBox for CanvasLoopBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } 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 { "CanvasLoopBox" } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_loop) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_loop.base.id) @@ -311,4 +326,4 @@ impl std::fmt::Display for CanvasLoopBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/console_box.rs b/src/boxes/console_box.rs index d2e0e3c1..9adb5ed5 100644 --- a/src/boxes/console_box.rs +++ b/src/boxes/console_box.rs @@ -1,35 +1,35 @@ /*! 📟 ConsoleBox - コンソール出力Box - * + * * ## 📝 概要 * Webブラウザのコンソール機能を統合したBox。 * WASM環境ではブラウザコンソール、ネイティブ環境では標準出力。 - * + * * ## 🛠️ 利用可能メソッド * - `log(message)` - 通常のメッセージ出力 * - `warn(message)` - 警告メッセージ出力 * - `error(message)` - エラーメッセージ出力 * - `clear()` - コンソール画面クリア - * + * * ## 💡 使用例 * ```nyash * local console * console = new ConsoleBox() - * + * * console.log("Hello, Nyash!") // 通常ログ * console.warn("This is a warning") // 警告 * console.error("Something went wrong") // エラー * console.clear() // クリア - * + * * // デバッグ用途 * local value * value = 42 * console.log("Debug: value = " + value.toString()) * ``` - * + * * ## 🌐 環境別動作 * - **WASM環境**: ブラウザの開発者ツールコンソールに出力 * - **ネイティブ環境**: ターミナル標準出力にプレフィックス付きで出力 - * + * * ## 🔍 デバッグ活用 * ```nyash * // エラーハンドリング @@ -37,7 +37,7 @@ * console.error("Critical error occurred!") * return null * } - * + * * // 実行トレース * console.log("Function start") * // 処理... @@ -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::fmt::Display; @@ -59,24 +59,26 @@ pub struct ConsoleBox { #[cfg(target_arch = "wasm32")] impl ConsoleBox { pub fn new() -> Self { - Self { base: BoxBase::new() } + Self { + base: BoxBase::new(), + } } - + /// Log messages to browser console pub fn log(&self, message: &str) { web_sys::console::log_1(&message.into()); } - + /// Log warning to browser console pub fn warn(&self, message: &str) { web_sys::console::warn_1(&message.into()); } - + /// Log error to browser console pub fn error(&self, message: &str) { web_sys::console::error_1(&message.into()); } - + /// Clear browser console pub fn clear(&self) { web_sys::console::clear(); @@ -88,19 +90,19 @@ impl BoxCore for ConsoleBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "[ConsoleBox - Browser Console Interface]") } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -111,27 +113,26 @@ impl NyashBox for ConsoleBox { fn to_string_box(&self) -> StringBox { StringBox::new("[ConsoleBox - Browser Console Interface]") } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::()) } - + fn type_name(&self) -> &'static str { "ConsoleBox" } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - } -// Non-WASM版 - モックアップ実装 +// Non-WASM版 - モックアップ実装 #[cfg(not(target_arch = "wasm32"))] #[derive(Debug, Clone)] pub struct ConsoleBox { @@ -141,22 +142,24 @@ pub struct ConsoleBox { #[cfg(not(target_arch = "wasm32"))] impl ConsoleBox { pub fn new() -> Self { - Self { base: BoxBase::new() } + Self { + base: BoxBase::new(), + } } - + /// Mock log method for non-WASM environments pub fn log(&self, message: &str) { println!("[Console LOG] {}", message); } - + pub fn warn(&self, message: &str) { println!("[Console WARN] {}", message); } - + pub fn error(&self, message: &str) { println!("[Console ERROR] {}", message); } - + pub fn clear(&self) { println!("[Console CLEAR]"); } @@ -167,19 +170,19 @@ impl BoxCore for ConsoleBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "[ConsoleBox - Mock Implementation]") } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -190,27 +193,25 @@ impl NyashBox for ConsoleBox { fn to_string_box(&self) -> StringBox { StringBox::new("[ConsoleBox - Mock Implementation]") } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::()) } - + fn type_name(&self) -> &'static str { "ConsoleBox" } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - } - // Display implementations for both WASM and non-WASM versions #[cfg(target_arch = "wasm32")] impl Display for ConsoleBox { diff --git a/src/boxes/debug_box.rs b/src/boxes/debug_box.rs index b586615f..9fed8efc 100644 --- a/src/boxes/debug_box.rs +++ b/src/boxes/debug_box.rs @@ -1,57 +1,57 @@ /*! 🔍 DebugBox - デバッグ支援Box - * + * * ## 📝 概要 * プロフェッショナル開発向けデバッグ機能を提供するBox。 * メモリ使用量監視、実行トレース、ブレークポイントなど * 高度なデバッグ機能を完備。 - * + * * ## 🛠️ 利用可能メソッド - * + * * ### 🎯 基本デバッグ * - `startTracking()` - デバッグ追跡開始 * - `stopTracking()` - デバッグ追跡停止 * - `trackBox(box, name)` - 特定Boxを追跡対象に追加 * - `watch(box, name)` - リアルタイム監視 * - `clear()` - 全デバッグ情報クリア - * + * * ### 📊 レポート・分析 * - `dumpAll()` - 全追跡データダンプ * - `memoryReport()` - メモリ使用量レポート * - `showCallStack()` - 関数呼び出しスタック表示 * - `saveToFile(filename)` - デバッグ情報をファイル保存 - * + * * ### 🎮 高度機能 * - `setBreakpoint(function)` - ブレークポイント設定 * - `traceCall(function, args)` - 関数呼び出しトレース * - `isTracking()` - 追跡状態確認 * - `getTrackedCount()` - 追跡中Box数取得 - * + * * ## 💡 使用例 * ```nyash * local debug, user, product * debug = new DebugBox() - * + * * // デバッグ開始 * debug.startTracking() - * + * * // オブジェクトを追跡 * user = new User("Alice", 25) * debug.trackBox(user, "user_alice") - * + * * product = new Product("Book", 1500) * debug.trackBox(product, "book_product") - * + * * // リアルタイム監視 * debug.watch(user.age, "user_age") - * + * * // レポート生成 * print(debug.memoryReport()) * print(debug.dumpAll()) - * + * * // ファイルに保存 * debug.saveToFile("debug_report.txt") * ``` - * + * * ## 🎮 実用例 - パフォーマンス診断 * ```nyash * static box PerformanceTest { @@ -76,13 +76,13 @@ * } * } * ``` - * + * * ## ⚡ ベストプラクティス * ```nyash * // エラーハンドリング付きデバッグ * local debug * debug = new DebugBox() - * + * * try { * debug.startTracking() * // 問題のあるコード @@ -92,20 +92,20 @@ * print("Debug info saved to error_dump.txt") * } * ``` - * + * * ## ⚠️ 注意 * - 本格運用時はtrackingを無効にしてパフォーマンス向上 * - 大量データ追跡時はメモリ消費に注意 * - 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::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)] pub struct DebugBox { @@ -156,23 +156,27 @@ impl DebugBox { Ok(Box::new(VoidBox::new())) } - pub fn track_box(&self, box_value: &dyn NyashBox, name: &str) -> Result, RuntimeError> { + pub fn track_box( + &self, + box_value: &dyn NyashBox, + name: &str, + ) -> Result, RuntimeError> { let enabled = self.tracking_enabled.read().unwrap(); if !*enabled { return Ok(Box::new(VoidBox::new())); } let mut tracked = self.tracked_boxes.write().unwrap(); - + let info = TrackedBoxInfo { box_type: box_value.type_name().to_string(), created_at: Local::now().format("%Y-%m-%d %H:%M:%S").to_string(), fields: self.get_box_fields(box_value), value_repr: box_value.to_string_box().value, }; - + tracked.insert(name.to_string(), info); - + Ok(Box::new(VoidBox::new())) } @@ -192,9 +196,12 @@ impl DebugBox { pub fn dump_all(&self) -> Result, RuntimeError> { let tracked = self.tracked_boxes.read().unwrap(); 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())); - + for (name, info) in tracked.iter() { output.push_str(&format!("Box: {}\n", name)); output.push_str(&format!(" Type: {}\n", info.box_type)); @@ -203,28 +210,31 @@ impl DebugBox { output.push_str(&format!(" Value: {}\n", info.value_repr)); output.push_str("\n"); } - + Ok(Box::new(StringBox::new(output))) } pub fn save_to_file(&self, filename: &str) -> Result, RuntimeError> { let dump_result = self.dump_all()?; let content = dump_result.to_string_box().value; - + // Write to file using std::fs - std::fs::write(filename, content) - .map_err(|e| RuntimeError::InvalidOperation { - message: format!("Failed to write debug file: {}", e), - })?; - + std::fs::write(filename, content).map_err(|e| RuntimeError::InvalidOperation { + message: format!("Failed to write debug file: {}", e), + })?; + println!("[DEBUG] Saved debug info to {}", filename); Ok(Box::new(VoidBox::new())) } - pub fn watch(&self, box_value: &dyn NyashBox, name: &str) -> Result, RuntimeError> { + pub fn watch( + &self, + box_value: &dyn NyashBox, + name: &str, + ) -> Result, RuntimeError> { let value_str = box_value.to_string_box().value; let type_name = box_value.type_name(); - + println!("[DEBUG] Watching {} ({}): {}", name, type_name, value_str); Ok(Box::new(VoidBox::new())) } @@ -233,18 +243,18 @@ impl DebugBox { let tracked = self.tracked_boxes.read().unwrap(); let mut report = String::from("=== Memory Report ===\n"); report.push_str(&format!("Tracked boxes: {}\n", tracked.len())); - + // Count by type let mut type_counts: HashMap = HashMap::new(); for info in tracked.values() { *type_counts.entry(info.box_type.clone()).or_insert(0) += 1; } - + report.push_str("\nBoxes by type:\n"); for (box_type, count) in type_counts.iter() { report.push_str(&format!(" {}: {}\n", box_type, count)); } - + Ok(Box::new(StringBox::new(report))) } @@ -256,45 +266,50 @@ impl DebugBox { Ok(Box::new(VoidBox::new())) } - pub fn trace_call(&self, function_name: &str, args: Vec) -> Result, RuntimeError> { + pub fn trace_call( + &self, + function_name: &str, + args: Vec, + ) -> Result, RuntimeError> { let mut stack = self.call_stack.write().unwrap(); stack.push(CallInfo { function_name: function_name.to_string(), args, timestamp: Local::now().format("%H:%M:%S.%3f").to_string(), }); - + // Keep only last 100 calls to prevent memory issues if stack.len() > 100 { stack.remove(0); } - + Ok(Box::new(VoidBox::new())) } pub fn show_call_stack(&self) -> Result, RuntimeError> { let stack = self.call_stack.read().unwrap(); let mut output = String::from("=== Call Stack ===\n"); - + for (i, call) in stack.iter().enumerate() { - output.push_str(&format!("{}: [{}] {}({})\n", - i, - call.timestamp, + output.push_str(&format!( + "{}: [{}] {}({})\n", + i, + call.timestamp, call.function_name, call.args.join(", ") )); } - + Ok(Box::new(StringBox::new(output))) } pub fn clear(&self) -> Result, RuntimeError> { let mut tracked = self.tracked_boxes.write().unwrap(); tracked.clear(); - + let mut stack = self.call_stack.write().unwrap(); stack.clear(); - + println!("[DEBUG] Cleared all debug information"); Ok(Box::new(VoidBox::new())) } @@ -306,13 +321,18 @@ impl DebugBox { pub fn get_tracked_count(&self) -> Result, RuntimeError> { 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 --- pub fn trace_plugin_calls(&self, on: bool) -> Result, RuntimeError> { 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())) } @@ -329,7 +349,7 @@ impl Clone for DebugBox { let breakpoints = self.breakpoints.read().unwrap(); let call_stack = self.call_stack.read().unwrap(); let tracking_enabled = self.tracking_enabled.read().unwrap(); - + DebugBox { base: BoxBase::new(), // New unique ID for cloned instance tracking_enabled: RwLock::new(*tracking_enabled), @@ -345,20 +365,20 @@ impl BoxCore for DebugBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let tracked = self.tracked_boxes.read().unwrap(); write!(f, "DebugBox[{} tracked]", tracked.len()) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -377,7 +397,7 @@ impl NyashBox for DebugBox { let tracked = self.tracked_boxes.read().unwrap(); StringBox::new(format!("DebugBox[{} tracked]", tracked.len())) } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_debug) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_debug.base.id) @@ -385,19 +405,17 @@ impl NyashBox for DebugBox { BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { "DebugBox" } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - - } diff --git a/src/boxes/debug_config_box.rs b/src/boxes/debug_config_box.rs index 8c651559..09c1534d 100644 --- a/src/boxes/debug_config_box.rs +++ b/src/boxes/debug_config_box.rs @@ -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; #[derive(Debug, Clone)] @@ -20,13 +20,19 @@ impl DebugConfigBox { Self { base: BoxBase::new(), 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_runtime: std::env::var("NYASH_JIT_EVENTS_RUNTIME").ok().as_deref() == Some("1"), + jit_events_compile: std::env::var("NYASH_JIT_EVENTS_COMPILE").ok().as_deref() + == 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_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_dot_path: std::env::var("NYASH_JIT_DOT").ok().filter(|s| !s.is_empty()), - jit_events_path: std::env::var("NYASH_JIT_EVENTS_PATH").ok().filter(|s| !s.is_empty()), + jit_dot_path: std::env::var("NYASH_JIT_DOT") + .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_json" => self.jit_stats_json = 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()) } @@ -47,7 +53,7 @@ impl DebugConfigBox { match name { "jit_dot" | "jit_dot_path" => self.jit_dot_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()) } @@ -75,17 +81,29 @@ impl DebugConfigBox { } pub fn apply(&self) -> Box { - 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_COMPILE", self.jit_events_compile); setb("NYASH_JIT_EVENTS_RUNTIME", self.jit_events_runtime); setb("NYASH_JIT_STATS", self.jit_stats); setb("NYASH_JIT_STATS_JSON", self.jit_stats_json); setb("NYASH_JIT_DUMP", self.jit_dump); - if let Some(p) = &self.jit_dot_path { std::env::set_var("NYASH_JIT_DOT", p); } - else { 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 let Some(p) = &self.jit_dot_path { + std::env::set_var("NYASH_JIT_DOT", p); + } else { + 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 (self.jit_events || self.jit_events_compile || self.jit_events_runtime) && std::env::var("NYASH_JIT_THRESHOLD").is_err() @@ -108,17 +126,40 @@ impl DebugConfigBox { } impl BoxCore for DebugConfigBox { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { 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 } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + 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 { - fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::()) } - fn type_name(&self) -> &'static str { "DebugConfigBox" } - fn clone_box(&self) -> Box { Box::new(Self { base: self.base.clone(), ..self.clone() }) } - fn share_box(&self) -> Box { self.clone_box() } - fn to_string_box(&self) -> StringBox { StringBox::new("DebugConfigBox".to_string()) } + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + BoolBox::new(other.as_any().is::()) + } + fn type_name(&self) -> &'static str { + "DebugConfigBox" + } + fn clone_box(&self) -> Box { + Box::new(Self { + base: self.base.clone(), + ..self.clone() + }) + } + fn share_box(&self) -> Box { + self.clone_box() + } + fn to_string_box(&self) -> StringBox { + StringBox::new("DebugConfigBox".to_string()) + } } diff --git a/src/boxes/egui_box.rs b/src/boxes/egui_box.rs index 71e76f3e..878630c7 100644 --- a/src/boxes/egui_box.rs +++ b/src/boxes/egui_box.rs @@ -3,11 +3,11 @@ /*! 🖼️ EguiBox - デスクトップGUIアプリBox * Everything is Box哲学によるGUIフレームワーク統合 * 「なんでもBoxにできる」化け物言語の第一歩! - * + * * ## 📝 概要 * Rustの人気GUI框架eframeを使ったネイティブデスクトップアプリ作成。 * Nyashコードから直接GUI操作が可能! - * + * * ## 🛠️ 利用可能メソッド * - `setTitle(title)` - ウィンドウタイトル設定 * - `setSize(width, height)` - ウィンドウサイズ設定 @@ -15,7 +15,7 @@ * - `addText(text)` - テキスト表示追加 * - `addButton(label)` - ボタン追加 * - `close()` - ウィンドウ閉じる - * + * * ## 💡 使用例 * ```nyash * // 基本的なGUIアプリ @@ -27,20 +27,20 @@ * app.addButton("Click Me") * app.run() // GUIアプリ開始 * ``` - * + * * ## ⚠️ 注意 * - デスクトップ環境でのみ利用可能(WASM環境では無効) * - `run()`はブロッキング動作(アプリ終了まで制御を返さない) */ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use crate::interpreter::RuntimeError; +use eframe::{self, egui, epaint::Vec2}; use std::any::Any; use std::sync::{Arc, RwLock}; -use eframe::{self, egui, epaint::Vec2}; /// EguiBox - GUI アプリケーションを包むBox -/// +/// /// # 使用例 /// ```nyash /// app = new EguiBox() @@ -89,16 +89,16 @@ impl EguiBox { update_fn: None, } } - + /// アプリケーション状態を設定 pub fn set_app_state(&mut self, state: T) { *self.app_state.write().unwrap() = Box::new(state); } - + /// 更新関数を設定 - pub fn set_update_fn(&mut self, f: F) - where - F: Fn(&mut Box, &egui::Context) + Send + Sync + 'static + pub fn set_update_fn(&mut self, f: F) + where + F: Fn(&mut Box, &egui::Context) + Send + Sync + 'static, { self.update_fn = Some(Arc::new(f)); } @@ -122,19 +122,23 @@ impl BoxCore for EguiBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + 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 { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -148,21 +152,21 @@ impl std::fmt::Display for EguiBox { impl NyashBox for EguiBox { fn to_string_box(&self) -> StringBox { - StringBox::new( - format!("EguiBox('{}', {}x{})", self.title, self.size.x, self.size.y) - ) + StringBox::new(format!( + "EguiBox('{}', {}x{})", + self.title, self.size.x, self.size.y + )) } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_egui) = other.as_any().downcast_ref::() { BoolBox::new(self.title == other_egui.title && self.size == other_egui.size) @@ -170,11 +174,10 @@ impl NyashBox for EguiBox { BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { "EguiBox" } - } // EguiBoxのメソッド実装(実際にはインタープリターから呼ばれない) @@ -187,28 +190,24 @@ impl EguiBox { // we would need a more sophisticated state sharing mechanism let app_state = Arc::new(RwLock::new(Box::new(()) as Box)); drop(state_snapshot); - + let update_fn = Arc::clone(update_fn); - + let options = eframe::NativeOptions { viewport: egui::ViewportBuilder::default() .with_inner_size(self.size) .with_title(&self.title), ..Default::default() }; - + let app = NyashApp { app_state, update_fn, }; - + // 注意: これはブロッキング呼び出し - let _ = eframe::run_native( - &self.title, - options, - Box::new(|_cc| Ok(Box::new(app))), - ); - + let _ = eframe::run_native(&self.title, options, Box::new(|_cc| Ok(Box::new(app)))); + Ok(()) } else { Err(RuntimeError::InvalidOperation { @@ -221,18 +220,18 @@ impl EguiBox { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_egui_box_creation() { let gui = EguiBox::new(); assert_eq!(gui.title, "Nyash GUI Application"); assert_eq!(gui.size, Vec2::new(800.0, 600.0)); } - + #[test] fn test_egui_box_to_string() { let gui = EguiBox::new(); let s = gui.to_string_box(); assert_eq!(s.value, "EguiBox('Nyash GUI Application', 800x600)"); } -} \ No newline at end of file +} diff --git a/src/boxes/file/mod.rs b/src/boxes/file/mod.rs index 982bee0a..80134432 100644 --- a/src/boxes/file/mod.rs +++ b/src/boxes/file/mod.rs @@ -2,10 +2,10 @@ // Nyashの箱システムによるファイル入出力を提供します。 // 参考: 既存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::fs::{File, OpenOptions}; -use std::io::{Read, Write, Result}; +use std::io::{Read, Result, Write}; use std::sync::RwLock; #[derive(Debug)] @@ -38,8 +38,12 @@ impl FileBox { Err(_) => { // Fallback: create with empty file handle - only for dispatch use std::fs::OpenOptions; - let file = OpenOptions::new().create(true).write(true).read(true) - .open("/dev/null").unwrap_or_else(|_| File::open("/dev/null").unwrap()); + let file = OpenOptions::new() + .create(true) + .write(true) + .read(true) + .open("/dev/null") + .unwrap_or_else(|_| File::open("/dev/null").unwrap()); FileBox { file: RwLock::new(file), path: String::new(), @@ -48,28 +52,32 @@ impl FileBox { } } } - + pub fn open(path: &str) -> Result { - let file = OpenOptions::new().read(true).write(true).create(true).open(path)?; - Ok(FileBox { + let file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(path)?; + Ok(FileBox { file: RwLock::new(file), path: path.to_string(), base: BoxBase::new(), }) } - + pub fn read_to_string(&self) -> Result { let mut file = self.file.write().unwrap(); let mut s = String::new(); file.read_to_string(&mut s)?; Ok(s) } - + pub fn write_all(&self, buf: &[u8]) -> Result<()> { let mut file = self.file.write().unwrap(); file.write_all(buf) } - + /// ファイルの内容を読み取る pub fn read(&self) -> Box { match self.read_to_string() { @@ -77,7 +85,7 @@ impl FileBox { Err(e) => Box::new(StringBox::new(&format!("Error reading file: {}", e))), } } - + /// ファイルに内容を書き込む pub fn write(&self, content: Box) -> Box { let content_str = content.to_string_box().value; @@ -86,13 +94,13 @@ impl FileBox { Err(e) => Box::new(StringBox::new(&format!("Error writing file: {}", e))), } } - + /// ファイルが存在するかチェック pub fn exists(&self) -> Box { use std::path::Path; Box::new(BoolBox::new(Path::new(&self.path).exists())) } - + /// ファイルを削除 pub fn delete(&self) -> Box { match std::fs::remove_file(&self.path) { @@ -100,7 +108,7 @@ impl FileBox { Err(e) => Box::new(StringBox::new(&format!("Error deleting file: {}", e))), } } - + /// ファイルをコピー pub fn copy(&self, dest: &str) -> Box { match std::fs::copy(&self.path, dest) { @@ -114,19 +122,19 @@ impl BoxCore for FileBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "FileBox({})", self.path) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -137,10 +145,10 @@ impl NyashBox for FileBox { // Note: Cannot truly clone a File handle, so create a new one to the same path match FileBox::open(&self.path) { 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 } } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -150,12 +158,10 @@ impl NyashBox for FileBox { StringBox::new(format!("FileBox({})", self.path)) } - fn type_name(&self) -> &'static str { "FileBox" } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_file) = other.as_any().downcast_ref::() { BoolBox::new(self.path == other_file.path) diff --git a/src/boxes/function_box.rs b/src/boxes/function_box.rs index d9fcdf37..9a834aba 100644 --- a/src/boxes/function_box.rs +++ b/src/boxes/function_box.rs @@ -1,8 +1,8 @@ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; use crate::ast::ASTNode; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; +use std::any::Any; use std::collections::HashMap; use std::sync::Weak; -use std::any::Any; #[derive(Debug)] pub struct ClosureEnv { @@ -11,7 +11,12 @@ pub struct 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)] @@ -24,32 +29,70 @@ pub struct FunctionBox { impl FunctionBox { pub fn new(params: Vec, body: Vec) -> 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, body: Vec, env: ClosureEnv) -> Self { - Self { params, body, env, base: BoxBase::new() } + Self { + params, + body, + env, + base: BoxBase::new(), + } } } impl BoxCore for FunctionBox { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { 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 box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + 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 { - fn clone_box(&self) -> Box { Box::new(self.clone()) } - fn share_box(&self) -> Box { 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 clone_box(&self) -> Box { + Box::new(self.clone()) + } + fn share_box(&self) -> Box { + 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 { if let Some(o) = other.as_any().downcast_ref::() { 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 { let me_value = self.me_value.as_ref().map(|w| Weak::clone(w)); let mut captures: HashMap> = 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 } } } impl Clone for FunctionBox { 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(), + } } } diff --git a/src/boxes/future/mod.rs b/src/boxes/future/mod.rs index e300c829..f03f3c61 100644 --- a/src/boxes/future/mod.rs +++ b/src/boxes/future/mod.rs @@ -2,9 +2,9 @@ // Nyashの箱システムによる非同期処理の基盤を提供します。 // 参考: 既存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::sync::{Mutex, Condvar, Arc, Weak}; +use std::sync::{Arc, Condvar, Mutex, Weak}; #[derive(Debug)] pub struct NyashFutureBox { @@ -33,7 +33,10 @@ pub struct FutureWeak { impl Clone for NyashFutureBox { fn clone(&self) -> Self { - Self { inner: self.inner.clone(), base: BoxBase::new() } + Self { + inner: self.inner.clone(), + base: BoxBase::new(), + } } } @@ -41,13 +44,16 @@ impl NyashFutureBox { pub fn new() -> Self { Self { inner: Arc::new(Inner { - state: Mutex::new(FutureState { result: None, ready: false }), + state: Mutex::new(FutureState { + result: None, + ready: false, + }), cv: Condvar::new(), }), base: BoxBase::new(), } } - + /// Set the result of the future pub fn set_result(&self, value: Box) { let mut st = self.inner.state.lock().unwrap(); @@ -55,7 +61,7 @@ impl NyashFutureBox { st.ready = true; self.inner.cv.notify_all(); } - + /// Get the result (blocks until ready) pub fn get(&self) -> Box { let mut st = self.inner.state.lock().unwrap(); @@ -64,21 +70,25 @@ impl NyashFutureBox { } st.result.as_ref().unwrap().clone_box() } - + /// Check if the future is ready pub fn ready(&self) -> bool { self.inner.state.lock().unwrap().ready } /// 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 { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -98,12 +108,10 @@ impl NyashBox for NyashFutureBox { } } - fn type_name(&self) -> &'static str { "NyashFutureBox" } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_future) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_future.base.id) @@ -117,7 +125,7 @@ impl BoxCore for NyashFutureBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } @@ -135,11 +143,11 @@ impl BoxCore for NyashFutureBox { write!(f, "Future(pending)") } } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -164,6 +172,8 @@ impl FutureBox { impl FutureWeak { /// Try to upgrade and check readiness pub(crate) fn is_ready(&self) -> Option { - self.inner.upgrade().map(|arc| arc.state.lock().unwrap().ready) + self.inner + .upgrade() + .map(|arc| arc.state.lock().unwrap().ready) } } diff --git a/src/boxes/gc_config_box.rs b/src/boxes/gc_config_box.rs index 610131db..7e4bae7d 100644 --- a/src/boxes/gc_config_box.rs +++ b/src/boxes/gc_config_box.rs @@ -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; #[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 { pub fn new() -> Self { @@ -18,7 +23,7 @@ impl GcConfigBox { "counting" => self.counting = on, "trace" => self.trace = 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()) } @@ -32,31 +37,59 @@ impl GcConfigBox { Box::new(BoolBox::new(v)) } pub fn apply(&self) -> Box { - 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_TRACE", self.trace); setb("NYASH_GC_BARRIER_STRICT", self.barrier_strict); Box::new(VoidBox::new()) } pub fn summary(&self) -> Box { - 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)) } } impl BoxCore for GcConfigBox { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { 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 } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + 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 { - fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::()) } - fn type_name(&self) -> &'static str { "GcConfigBox" } - fn clone_box(&self) -> Box { Box::new(self.clone()) } - fn share_box(&self) -> Box { self.clone_box() } - fn to_string_box(&self) -> StringBox { StringBox::new(self.summary().to_string_box().value) } + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + BoolBox::new(other.as_any().is::()) + } + fn type_name(&self) -> &'static str { + "GcConfigBox" + } + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + fn share_box(&self) -> Box { + self.clone_box() + } + fn to_string_box(&self) -> StringBox { + StringBox::new(self.summary().to_string_box().value) + } } - diff --git a/src/boxes/http/mod.rs b/src/boxes/http/mod.rs index 0f973909..fac1f11b 100644 --- a/src/boxes/http/mod.rs +++ b/src/boxes/http/mod.rs @@ -1,11 +1,11 @@ //! HttpClientBox 🌐 - HTTP通信 // Nyashの箱システムによるHTTP通信を提供します。 // 参考: 既存Boxの設計思想 -// +// // NOTE: HTTPサポートは現在開発中です。 // reqwestクレートの依存関係のため、一時的に無効化されています。 -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use std::any::Any; #[derive(Debug, Clone)] @@ -15,33 +15,38 @@ pub struct HttpClientBox { impl HttpClientBox { pub fn new() -> Self { - HttpClientBox { - base: BoxBase::new() + HttpClientBox { + base: BoxBase::new(), } } - + /// HTTP GETリクエスト(スタブ) pub fn http_get(&self, _url: Box) -> Box { Box::new(StringBox::new("HTTP support is currently disabled")) } - + /// HTTP POSTリクエスト(スタブ) pub fn post(&self, _url: Box, _body: Box) -> Box { Box::new(StringBox::new("HTTP support is currently disabled")) } - + /// HTTP PUT リクエスト(スタブ) pub fn put(&self, _url: Box, _body: Box) -> Box { Box::new(StringBox::new("HTTP support is currently disabled")) } - + /// HTTP DELETE リクエスト(スタブ) pub fn delete(&self, _url: Box) -> Box { Box::new(StringBox::new("HTTP support is currently disabled")) } - + /// ヘッダー付きHTTPリクエスト(スタブ) - pub fn request(&self, _method: Box, _url: Box, _options: Box) -> Box { + pub fn request( + &self, + _method: Box, + _url: Box, + _options: Box, + ) -> Box { Box::new(StringBox::new("HTTP support is currently disabled")) } } @@ -50,7 +55,7 @@ impl NyashBox for HttpClientBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -60,12 +65,10 @@ impl NyashBox for HttpClientBox { StringBox::new(format!("HttpClientBox(id: {})", self.base.id)) } - fn type_name(&self) -> &'static str { "HttpClientBox" } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_http) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_http.base.id) @@ -79,7 +82,7 @@ impl BoxCore for HttpClientBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } @@ -87,11 +90,11 @@ impl BoxCore for HttpClientBox { fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "HttpClientBox(id: {})", self.base.id) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -101,4 +104,4 @@ impl std::fmt::Display for HttpClientBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/http_message_box.rs b/src/boxes/http_message_box.rs index 9e5003fe..b9d12ffd 100644 --- a/src/boxes/http_message_box.rs +++ b/src/boxes/http_message_box.rs @@ -1,36 +1,36 @@ /*! 📬 HTTPRequestBox & HTTPResponseBox - HTTP メッセージ処理 - * + * * ## 📝 概要 * HTTP/1.1 プロトコルのリクエスト・レスポンス処理を提供するBox群 * SocketBox と連携して完全なHTTPサーバー・クライアント機能を実現 - * + * * ## 🛠️ HTTPRequestBox - リクエスト処理 * ### HTTP Method & URL * - `getMethod()` - HTTP メソッド取得 (GET, POST, etc.) * - `getPath()` - URL パス取得 * - `getQueryString()` - クエリ文字列取得 - * + * * ### Headers * - `getHeader(name)` - 特定ヘッダー取得 * - `getAllHeaders()` - 全ヘッダー取得(MapBox) * - `hasHeader(name)` - ヘッダー存在確認 - * + * * ### Body & Content * - `getBody()` - リクエストボディ取得 * - `getContentType()` - Content-Type取得 * - `getContentLength()` - Content-Length取得 - * + * * ## 🛠️ HTTPResponseBox - レスポンス生成 * ### Status & Headers * - `setStatus(code, message)` - ステータス設定 * - `setHeader(name, value)` - ヘッダー設定 * - `setContentType(type)` - Content-Type設定 - * + * * ### Body & Output * - `setBody(content)` - レスポンスボディ設定 * - `appendBody(content)` - ボディ追加 * - `toHttpString()` - HTTP形式文字列生成 - * + * * ## 💡 使用例 * ```nyash * // Request parsing @@ -38,7 +38,7 @@ * local request = HTTPRequestBox.parse(rawRequest) * print("Method: " + request.getMethod()) * print("Path: " + request.getPath()) - * + * * // Response generation * local response = new HTTPResponseBox() * response.setStatus(200, "OK") @@ -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 std::any::Any; use std::collections::HashMap; @@ -77,32 +77,32 @@ impl HTTPRequestBox { http_version: "HTTP/1.1".to_string(), } } - + /// 生のHTTPリクエスト文字列を解析 pub fn parse(raw_request: Box) -> Self { let request_str = raw_request.to_string_box().value; let mut request = HTTPRequestBox::new(); - + let lines: Vec<&str> = request_str.lines().collect(); if lines.is_empty() { return request; } - + // Parse request line: "GET /path HTTP/1.1" let request_line_parts: Vec<&str> = lines[0].split_whitespace().collect(); if request_line_parts.len() >= 3 { request.method = request_line_parts[0].to_string(); - + // Split path and query string let url_parts: Vec<&str> = request_line_parts[1].splitn(2, '?').collect(); request.path = url_parts[0].to_string(); if url_parts.len() > 1 { request.query_string = url_parts[1].to_string(); } - + request.http_version = request_line_parts[2].to_string(); } - + // Parse headers let mut header_end = 1; for (i, line) in lines.iter().enumerate().skip(1) { @@ -110,37 +110,37 @@ impl HTTPRequestBox { header_end = i + 1; break; } - + if let Some(colon_pos) = line.find(':') { let name = line[..colon_pos].trim().to_lowercase(); let value = line[colon_pos + 1..].trim().to_string(); request.headers.insert(name, value); } } - + // Parse body (everything after headers) if header_end < lines.len() { request.body = lines[header_end..].join("\n"); } - + request } - + /// HTTP メソッド取得 pub fn get_method(&self) -> Box { Box::new(StringBox::new(self.method.clone())) } - + /// URL パス取得 pub fn get_path(&self) -> Box { Box::new(StringBox::new(self.path.clone())) } - + /// クエリ文字列取得 pub fn get_query_string(&self) -> Box { Box::new(StringBox::new(self.query_string.clone())) } - + /// 特定ヘッダー取得 pub fn get_header(&self, name: Box) -> Box { let header_name = name.to_string_box().value.to_lowercase(); @@ -149,7 +149,7 @@ impl HTTPRequestBox { None => Box::new(StringBox::new("".to_string())), } } - + /// 全ヘッダー取得(MapBox形式) pub fn get_all_headers(&self) -> Box { let headers_map = MapBox::new(); @@ -160,31 +160,29 @@ impl HTTPRequestBox { } Box::new(headers_map) } - + /// ヘッダー存在確認 pub fn has_header(&self, name: Box) -> Box { let header_name = name.to_string_box().value.to_lowercase(); Box::new(BoolBox::new(self.headers.contains_key(&header_name))) } - + /// リクエストボディ取得 pub fn get_body(&self) -> Box { Box::new(StringBox::new(self.body.clone())) } - + /// Content-Type取得 pub fn get_content_type(&self) -> Box { self.get_header(Box::new(StringBox::new("content-type".to_string()))) } - + /// Content-Length取得 pub fn get_content_length(&self) -> Box { match self.headers.get("content-length") { - Some(length_str) => { - match length_str.parse::() { - Ok(length) => Box::new(IntegerBox::new(length)), - Err(_) => Box::new(IntegerBox::new(0)), - } + Some(length_str) => match length_str.parse::() { + Ok(length) => Box::new(IntegerBox::new(length)), + Err(_) => Box::new(IntegerBox::new(0)), }, None => Box::new(IntegerBox::new(0)), } @@ -195,15 +193,19 @@ impl NyashBox for HTTPRequestBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } fn to_string_box(&self) -> StringBox { - StringBox::new(format!("HTTPRequest({} {} - {} headers)", - self.method, self.path, self.headers.len())) + StringBox::new(format!( + "HTTPRequest({} {} - {} headers)", + self.method, + self.path, + self.headers.len() + )) } fn type_name(&self) -> &'static str { @@ -223,20 +225,25 @@ impl BoxCore for HTTPRequestBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "HTTPRequest({} {} - {} headers)", - self.method, self.path, self.headers.len()) + write!( + f, + "HTTPRequest({} {} - {} headers)", + self.method, + self.path, + self.headers.len() + ) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -270,105 +277,123 @@ impl HTTPResponseBox { http_version: "HTTP/1.1".to_string(), } } - + /// ステータスコード・メッセージ設定 - pub fn set_status(&self, code: Box, message: Box) -> Box { + pub fn set_status( + &self, + code: Box, + message: Box, + ) -> Box { // Note: This would need interior mutability for actual mutation // For now, this is a placeholder for the API structure let _code_val = code.to_string_box().value.parse::().unwrap_or(200); let _message_val = message.to_string_box().value; - + // TODO: Use RefCell or similar for interior mutability Box::new(BoolBox::new(true)) } - + /// ヘッダー設定 - pub fn set_header(&self, name: Box, value: Box) -> Box { + pub fn set_header( + &self, + name: Box, + value: Box, + ) -> Box { let _name_str = name.to_string_box().value; let _value_str = value.to_string_box().value; - + // TODO: Use RefCell for interior mutability Box::new(BoolBox::new(true)) } - + /// Content-Type設定 pub fn set_content_type(&self, content_type: Box) -> Box { let content_type_str = content_type.to_string_box().value; self.set_header( Box::new(StringBox::new("Content-Type".to_string())), - Box::new(StringBox::new(content_type_str)) + Box::new(StringBox::new(content_type_str)), ) } - + /// レスポンスボディ設定 pub fn set_body(&self, content: Box) -> Box { let _content_str = content.to_string_box().value; - + // TODO: Use RefCell for interior mutability Box::new(BoolBox::new(true)) } - + /// ボディ追加 pub fn append_body(&self, content: Box) -> Box { let _content_str = content.to_string_box().value; - + // TODO: Use RefCell for interior mutability Box::new(BoolBox::new(true)) } - + /// HTTP形式文字列生成 pub fn to_http_string(&self) -> Box { let mut response = String::new(); - + // Status line - response.push_str(&format!("{} {} {}\r\n", - self.http_version, self.status_code, self.status_message)); - + response.push_str(&format!( + "{} {} {}\r\n", + self.http_version, self.status_code, self.status_message + )); + // Headers for (name, value) in &self.headers { response.push_str(&format!("{}: {}\r\n", name, value)); } - + // Content-Length if not already set if !self.headers.contains_key("content-length") && !self.body.is_empty() { response.push_str(&format!("Content-Length: {}\r\n", self.body.len())); } - + // Empty line before body response.push_str("\r\n"); - + // Body response.push_str(&self.body); - + Box::new(StringBox::new(response)) } - + /// Quick HTML response creation pub fn create_html_response(content: Box) -> Self { let mut response = HTTPResponseBox::new(); response.status_code = 200; 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 } - + /// Quick JSON response creation pub fn create_json_response(content: Box) -> Self { let mut response = HTTPResponseBox::new(); response.status_code = 200; 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 } - + /// Quick 404 response creation pub fn create_404_response() -> Self { let mut response = HTTPResponseBox::new(); response.status_code = 404; 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 = "

404 - Not Found

".to_string(); response } @@ -378,15 +403,19 @@ impl NyashBox for HTTPResponseBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } fn to_string_box(&self) -> StringBox { - StringBox::new(format!("HTTPResponse({} {} - {} bytes)", - self.status_code, self.status_message, self.body.len())) + StringBox::new(format!( + "HTTPResponse({} {} - {} bytes)", + self.status_code, + self.status_message, + self.body.len() + )) } fn type_name(&self) -> &'static str { @@ -406,20 +435,25 @@ impl BoxCore for HTTPResponseBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "HTTPResponse({} {} - {} bytes)", - self.status_code, self.status_message, self.body.len()) + write!( + f, + "HTTPResponse({} {} - {} bytes)", + self.status_code, + self.status_message, + self.body.len() + ) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -429,4 +463,4 @@ impl std::fmt::Display for HTTPResponseBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/http_server_box.rs b/src/boxes/http_server_box.rs index 5b234883..9251fc6d 100644 --- a/src/boxes/http_server_box.rs +++ b/src/boxes/http_server_box.rs @@ -1,51 +1,51 @@ /*! 🌐 HTTPServerBox - HTTP サーバー実装 - * + * * ## 📝 概要 * TCP SocketBox を基盤とした高性能 HTTP/1.1 サーバー * 並行処理・ルーティング・ミドルウェア対応で実用アプリケーション開発可能 - * + * * ## 🛠️ 利用可能メソッド * ### Server Management * - `bind(address, port)` - サーバーアドレス bind * - `listen(backlog)` - 接続待機開始 * - `start()` - HTTP サーバー開始(ブロッキング) * - `stop()` - サーバー停止 - * + * * ### Routing & Handlers * - `route(path, handler)` - ルート・ハンドラー登録 * - `get(path, handler)` - GET ルート登録 * - `post(path, handler)` - POST ルート登録 * - `put(path, handler)` - PUT ルート登録 * - `delete(path, handler)` - DELETE ルート登録 - * + * * ### Middleware & Configuration * - `use(middleware)` - ミドルウェア登録 * - `setStaticPath(path)` - 静的ファイル配信設定 * - `setTimeout(seconds)` - リクエストタイムアウト設定 - * + * * ## 💡 使用例 * ```nyash * // HTTP Server creation * local server = new HTTPServerBox() * server.bind("0.0.0.0", 8080) - * + * * // Route handlers * server.get("/", APIHandler.home) * server.get("/api/status", APIHandler.status) * server.post("/api/users", APIHandler.createUser) - * + * * // Start server (blocking) * print("🚀 Server starting on port 8080...") * server.start() * ``` */ -use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase}; -use crate::boxes::SocketBox; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox}; use crate::boxes::http_message_box::{HTTPRequestBox, HTTPResponseBox}; +use crate::boxes::SocketBox; use std::any::Any; -use std::sync::RwLock; use std::collections::HashMap; +use std::sync::RwLock; use std::thread; /// HTTP サーバーを提供するBox @@ -66,26 +66,29 @@ impl Clone for HTTPServerBox { // State-preserving clone implementation following PR #87 pattern let socket_guard = self.socket.read().unwrap(); let socket_val = socket_guard.as_ref().map(|s| s.clone()); - + let routes_guard = self.routes.read().unwrap(); - let routes_val: HashMap> = routes_guard.iter() + let routes_val: HashMap> = routes_guard + .iter() .map(|(k, v)| (k.clone(), v.clone_box())) .collect(); - + let middleware_guard = self.middleware.read().unwrap(); - let middleware_val: Vec> = middleware_guard.iter() + let middleware_val: Vec> = middleware_guard + .iter() .map(|item| item.clone_box()) .collect(); - + let running_val = *self.running.read().unwrap(); let static_path_val = self.static_path.read().unwrap().clone(); let timeout_val = *self.timeout_seconds.read().unwrap(); - + let connections_guard = self.active_connections.read().unwrap(); - let connections_val: Vec> = connections_guard.iter() + let connections_val: Vec> = connections_guard + .iter() .map(|item| item.clone_box()) .collect(); - + Self { base: BoxBase::new(), // New unique ID for clone socket: RwLock::new(socket_val), @@ -112,39 +115,43 @@ impl HTTPServerBox { active_connections: RwLock::new(Vec::new()), } } - + /// サーバーアドレスにバインド pub fn bind(&self, address: Box, port: Box) -> Box { let socket = SocketBox::new(); let bind_result = socket.bind(address, port); - + if bind_result.to_string_box().value == "true" { match self.socket.write() { Ok(mut socket_guard) => { *socket_guard = Some(socket); 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 { Box::new(BoolBox::new(false)) } } - + /// 接続待機開始 pub fn listen(&self, _backlog: Box) -> Box { let socket_guard = match self.socket.read() { 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 { // For HTTPServerBox, if we have a socket stored, it means bind() was successful // and the socket should be in listening state. TcpListener::bind already puts // the socket in listening state, so we just need to verify it's working. - + // Try to access the stored listener directly (this is a simplified check) // In a real implementation, we'd store the listener state separately Box::new(BoolBox::new(true)) @@ -152,27 +159,35 @@ impl HTTPServerBox { Box::new(BoolBox::new(false)) } } - + /// HTTP サーバー開始(メインループ) pub fn start(&self) -> Box { // Set running state match self.running.write() { 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() { 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 { // Clone socket for the server loop let server_socket = socket.clone(); drop(socket_guard); - + println!("🚀 HTTP Server starting..."); - + // Main server loop - need to handle RwLock references carefully for threading loop { // Check if server should stop @@ -180,53 +195,54 @@ impl HTTPServerBox { Ok(running_guard) => *running_guard, Err(_) => break, // Exit loop if we can't check running state }; - + if !should_continue { break; } - + // Accept new connection let client_result = server_socket.accept(); - + // Check if we got a valid client connection let client_socket = match client_result.as_any().downcast_ref::() { Some(socket) => socket.clone(), None => continue, // Skip invalid connections }; - + // Add to active connections (with error handling) if let Ok(mut connections) = self.active_connections.write() { connections.push(Box::new(client_socket.clone())); } - + // Handle client in separate thread (simulate nowait) // For RwLock pattern, we need to pass the data needed for the thread let routes_snapshot = match self.routes.read() { Ok(routes_guard) => { - let routes_clone: HashMap> = routes_guard.iter() + let routes_clone: HashMap> = routes_guard + .iter() .map(|(k, v)| (k.clone(), v.clone_box())) .collect(); routes_clone - }, + } Err(_) => continue, // Skip this connection if we can't read routes }; - + thread::spawn(move || { Self::handle_client_request_with_routes(client_socket, routes_snapshot); // Note: Connection cleanup is handled separately to avoid complex lifetime issues }); } - + Box::new(BoolBox::new(true)) } else { Box::new(BoolBox::new(false)) } } - + /// サーバー停止 pub fn stop(&self) -> Box { *self.running.write().unwrap() = false; - + // Close all active connections let mut connections = self.active_connections.write().unwrap(); for connection in connections.iter() { @@ -235,128 +251,128 @@ impl HTTPServerBox { } } connections.clear(); - + // Close server socket if let Some(ref socket) = *self.socket.read().unwrap() { let _ = socket.close(); } - + println!("🛑 HTTP Server stopped"); Box::new(BoolBox::new(true)) } - + /// ルート・ハンドラー登録 pub fn route(&self, path: Box, handler: Box) -> Box { let path_str = path.to_string_box().value; let route_key = format!("ANY {}", path_str); - + self.routes.write().unwrap().insert(route_key, handler); Box::new(BoolBox::new(true)) } - + /// GET ルート登録 pub fn get(&self, path: Box, handler: Box) -> Box { let path_str = path.to_string_box().value; let route_key = format!("GET {}", path_str); - + self.routes.write().unwrap().insert(route_key, handler); Box::new(BoolBox::new(true)) } - + /// POST ルート登録 pub fn post(&self, path: Box, handler: Box) -> Box { let path_str = path.to_string_box().value; let route_key = format!("POST {}", path_str); - + self.routes.write().unwrap().insert(route_key, handler); Box::new(BoolBox::new(true)) } - + /// PUT ルート登録 pub fn put(&self, path: Box, handler: Box) -> Box { let path_str = path.to_string_box().value; let route_key = format!("PUT {}", path_str); - + self.routes.write().unwrap().insert(route_key, handler); Box::new(BoolBox::new(true)) } - + /// DELETE ルート登録 pub fn delete(&self, path: Box, handler: Box) -> Box { let path_str = path.to_string_box().value; let route_key = format!("DELETE {}", path_str); - + self.routes.write().unwrap().insert(route_key, handler); Box::new(BoolBox::new(true)) } - + /// 静的ファイル配信パス設定 pub fn set_static_path(&self, path: Box) -> Box { let path_str = path.to_string_box().value; *self.static_path.write().unwrap() = Some(path_str); Box::new(BoolBox::new(true)) } - + /// リクエストタイムアウト設定 pub fn set_timeout(&self, seconds: Box) -> Box { let timeout_val = seconds.to_string_box().value.parse::().unwrap_or(30); *self.timeout_seconds.write().unwrap() = timeout_val; Box::new(BoolBox::new(true)) } - + /// クライアントリクエスト処理(内部メソッド) fn handle_client_request_with_routes( - client_socket: SocketBox, - routes: HashMap> + client_socket: SocketBox, + routes: HashMap>, ) { // Read HTTP request let raw_request = client_socket.read_http_request(); let request_str = raw_request.to_string_box().value; - + if request_str.trim().is_empty() { let _ = client_socket.close(); return; } - + // Parse HTTP request let request = HTTPRequestBox::parse(raw_request); let method = request.get_method().to_string_box().value; let path = request.get_path().to_string_box().value; - + println!("📬 {} {}", method, path); - + // Find matching route let route_key = format!("{} {}", method, path); let fallback_key = format!("ANY {}", path); - + let response = if let Some(_handler) = routes.get(&route_key) { // Found specific method route // TODO: Actual handler invocation would need method calling infrastructure - HTTPResponseBox::create_json_response( - Box::new(StringBox::new(r#"{"message": "Route found", "method": ""#.to_string() + &method + r#""}"#)) - ) + HTTPResponseBox::create_json_response(Box::new(StringBox::new( + r#"{"message": "Route found", "method": ""#.to_string() + &method + r#""}"#, + ))) } else if let Some(_handler) = routes.get(&fallback_key) { // Found generic route - HTTPResponseBox::create_json_response( - Box::new(StringBox::new(r#"{"message": "Generic route found"}"#)) - ) + HTTPResponseBox::create_json_response(Box::new(StringBox::new( + r#"{"message": "Generic route found"}"#, + ))) } else { // No route found - 404 HTTPResponseBox::create_404_response() }; - + // Send response let response_str = response.to_http_string(); let _ = client_socket.write(response_str); let _ = client_socket.close(); } - + /// アクティブ接続数取得 pub fn get_active_connections(&self) -> Box { let connections = self.active_connections.read().unwrap(); Box::new(IntegerBox::new(connections.len() as i64)) } - + /// サーバー状態取得 pub fn is_running(&self) -> Box { Box::new(BoolBox::new(*self.running.read().unwrap())) @@ -367,7 +383,7 @@ impl NyashBox for HTTPServerBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -377,9 +393,9 @@ impl NyashBox for HTTPServerBox { let running = *self.running.read().unwrap(); let routes_count = self.routes.read().unwrap().len(); let connections_count = self.active_connections.read().unwrap().len(); - + StringBox::new(format!( - "HTTPServer(id: {}, running: {}, routes: {}, connections: {})", + "HTTPServer(id: {}, running: {}, routes: {}, connections: {})", self.base.id, running, routes_count, connections_count )) } @@ -401,7 +417,7 @@ impl BoxCore for HTTPServerBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } @@ -410,15 +426,18 @@ impl BoxCore for HTTPServerBox { let running = *self.running.read().unwrap(); let routes_count = self.routes.read().unwrap().len(); let connections_count = self.active_connections.read().unwrap().len(); - - write!(f, "HTTPServer(id: {}, running: {}, routes: {}, connections: {})", - self.base.id, running, routes_count, connections_count) + + write!( + f, + "HTTPServer(id: {}, running: {}, routes: {}, connections: {})", + self.base.id, running, routes_count, connections_count + ) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -436,4 +455,4 @@ impl Drop for HTTPServerBox { // Ensure server is stopped and resources are cleaned up let _ = self.stop(); } -} \ No newline at end of file +} diff --git a/src/boxes/intent_box.rs b/src/boxes/intent_box.rs index c4ec63dd..15f18d15 100644 --- a/src/boxes/intent_box.rs +++ b/src/boxes/intent_box.rs @@ -1,29 +1,29 @@ /*! 📦 IntentBox - Structured Message Box - * + * * ## 📝 概要 * IntentBoxは構造化メッセージを表現するBoxです。 * P2P通信において、メッセージの種類(name)と内容(payload)を * 明確に分離して管理します。 - * + * * ## 🏗️ 設計 * - **name**: メッセージの種類 ("chat.message", "file.share"等) * - **payload**: JSON形式の任意データ * - **Arc**: 他のBoxと統一されたメモリ管理パターン - * + * * ## 🛠️ 利用可能メソッド * - `new(name, payload)` - 構造化メッセージを作成 * - `getName()` - メッセージ名を取得 * - `getPayload()` - ペイロードを取得 * - `setPayload(data)` - ペイロードを更新 - * + * * ## 💡 使用例 * ```nyash * // チャットメッセージ - * local msg = new IntentBox("chat.message", { - * text: "Hello P2P!", - * from: "alice" + * local msg = new IntentBox("chat.message", { + * text: "Hello P2P!", + * from: "alice" * }) - * + * * // ファイル共有メッセージ * local file_msg = new IntentBox("file.share", { * filename: "document.pdf", @@ -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::sync::RwLock; use std::fmt::Debug; +use std::sync::RwLock; /// IntentBox - 構造化メッセージBox (RwLock pattern) #[derive(Debug)] @@ -51,7 +51,7 @@ impl Clone for IntentBox { fn clone(&self) -> Self { let name_val = self.name.read().unwrap().clone(); let payload_val = self.payload.read().unwrap().clone(); - + Self { base: BoxBase::new(), // New unique ID for clone name: RwLock::new(name_val), @@ -69,19 +69,19 @@ impl IntentBox { payload: RwLock::new(payload), } } - + /// メッセージ名を取得 pub fn get_name(&self) -> Box { let name = self.name.read().unwrap().clone(); Box::new(StringBox::new(name)) } - + /// ペイロードを取得 pub fn get_payload(&self) -> Box { let payload = self.payload.read().unwrap().clone(); Box::new(StringBox::new(payload.to_string())) } - + /// ペイロードを更新 pub fn set_payload(&self, payload: Box) -> Box { let payload_str = payload.to_string_box().value; @@ -89,8 +89,8 @@ impl IntentBox { Ok(json_val) => { *self.payload.write().unwrap() = json_val; Box::new(BoolBox::new(true)) - }, - Err(_) => Box::new(BoolBox::new(false)) + } + Err(_) => Box::new(BoolBox::new(false)), } } } @@ -99,7 +99,7 @@ impl NyashBox for IntentBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -109,7 +109,7 @@ impl NyashBox for IntentBox { let name = self.name.read().unwrap().clone(); StringBox::new(format!("IntentBox[{}]", name)) } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_intent) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_intent.base.id) @@ -117,7 +117,7 @@ impl NyashBox for IntentBox { BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { "IntentBox" } @@ -127,7 +127,7 @@ impl BoxCore for IntentBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } @@ -136,11 +136,11 @@ impl BoxCore for IntentBox { let name = self.name.read().unwrap().clone(); write!(f, "IntentBox[{}]", name) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -151,4 +151,3 @@ impl std::fmt::Display for IntentBox { self.fmt_box(f) } } - diff --git a/src/boxes/jit_config_box.rs b/src/boxes/jit_config_box.rs index b0bace6b..f3d256e2 100644 --- a/src/boxes/jit_config_box.rs +++ b/src/boxes/jit_config_box.rs @@ -1,6 +1,6 @@ -use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, VoidBox, BoxCore, BoxBase}; -use crate::jit::config::JitConfig; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox, VoidBox}; use crate::interpreter::RuntimeError; +use crate::jit::config::JitConfig; use std::any::Any; use std::sync::RwLock; @@ -11,12 +11,19 @@ pub struct 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 pub fn from_runtime_probe(&self) -> Box { let caps = crate::jit::config::probe_capabilities(); 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()) } pub fn set_flag(&self, name: &str, on: bool) -> Result, RuntimeError> { @@ -34,7 +41,11 @@ impl JitConfigBox { "bool_abi" | "native_bool_abi" => cfg.native_bool_abi = on, "ret_b1" | "ret_bool_b1" => cfg.ret_bool_b1 = 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())) } @@ -53,19 +64,28 @@ impl JitConfigBox { "bool_abi" | "native_bool_abi" => cfg.native_bool_abi, "ret_b1" | "ret_bool_b1" => cfg.ret_bool_b1, "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))) } pub fn set_threshold(&self, v: i64) -> Result, RuntimeError> { let mut cfg = self.config.write().unwrap(); - if v <= 0 { cfg.threshold = None; } - else { cfg.threshold = Some(v as u32); } + if v <= 0 { + cfg.threshold = None; + } else { + cfg.threshold = Some(v as u32); + } Ok(Box::new(VoidBox::new())) } pub fn get_threshold(&self) -> Box { 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 { let cfg = self.config.read().unwrap(); @@ -88,20 +108,49 @@ impl JitConfigBox { } pub fn from_json(&self, s: &str) -> Result, RuntimeError> { 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) })?; - if let Some(b) = v.get("exec").and_then(|x| x.as_bool()) { cfg.exec = b; } - if let Some(b) = v.get("stats").and_then(|x| x.as_bool()) { cfg.stats = b; } - 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(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; } + let v: serde_json::Value = + serde_json::from_str(s).map_err(|e| RuntimeError::InvalidOperation { + message: format!("Invalid JSON: {}", e), + })?; + if let Some(b) = v.get("exec").and_then(|x| x.as_bool()) { + cfg.exec = b; + } + if let Some(b) = v.get("stats").and_then(|x| x.as_bool()) { + cfg.stats = b; + } + 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(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())) } pub fn apply(&self) -> Box { @@ -124,20 +173,41 @@ impl JitConfigBox { } impl BoxCore for JitConfigBox { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { 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 } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + 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 { - fn to_string_box(&self) -> StringBox { StringBox::new(self.summary().to_string_box().value) } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::()) } - fn type_name(&self) -> &'static str { "JitConfigBox" } + fn to_string_box(&self) -> StringBox { + StringBox::new(self.summary().to_string_box().value) + } + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + BoolBox::new(other.as_any().is::()) + } + fn type_name(&self) -> &'static str { + "JitConfigBox" + } fn clone_box(&self) -> Box { 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 { + self.clone_box() } - fn share_box(&self) -> Box { self.clone_box() } } diff --git a/src/boxes/jit_events_box.rs b/src/boxes/jit_events_box.rs index 08c06d9a..2d396055 100644 --- a/src/boxes/jit_events_box.rs +++ b/src/boxes/jit_events_box.rs @@ -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; #[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 { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { 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 } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + 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 { - fn to_string_box(&self) -> StringBox { StringBox::new("JitEventsBox") } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::()) } - fn type_name(&self) -> &'static str { "JitEventsBox" } - fn clone_box(&self) -> Box { Box::new(Self { base: self.base.clone() }) } - fn share_box(&self) -> Box { self.clone_box() } + fn to_string_box(&self) -> StringBox { + StringBox::new("JitEventsBox") + } + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + BoolBox::new(other.as_any().is::()) + } + fn type_name(&self) -> &'static str { + "JitEventsBox" + } + fn clone_box(&self) -> Box { + Box::new(Self { + base: self.base.clone(), + }) + } + fn share_box(&self) -> Box { + self.clone_box() + } } impl JitEventsBox { @@ -28,14 +58,17 @@ impl JitEventsBox { Box::new(VoidBox::new()) } pub fn enable(&self, on: bool) -> Box { - if on { std::env::set_var("NYASH_JIT_EVENTS", "1"); } - else { std::env::remove_var("NYASH_JIT_EVENTS"); } + if on { + std::env::set_var("NYASH_JIT_EVENTS", "1"); + } else { + std::env::remove_var("NYASH_JIT_EVENTS"); + } Box::new(VoidBox::new()) } pub fn log(&self, kind: &str, function: &str, note_json: &str) -> Box { - let extra = serde_json::from_str::(note_json).unwrap_or_else(|_| serde_json::json!({"note": note_json})); + let extra = serde_json::from_str::(note_json) + .unwrap_or_else(|_| serde_json::json!({"note": note_json})); crate::jit::events::emit(kind, function, None, None, extra); Box::new(VoidBox::new()) } } - diff --git a/src/boxes/jit_hostcall_registry_box.rs b/src/boxes/jit_hostcall_registry_box.rs index 7fe3eb6f..65c96021 100644 --- a/src/boxes/jit_hostcall_registry_box.rs +++ b/src/boxes/jit_hostcall_registry_box.rs @@ -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; #[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 { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { 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 } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + 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 { @@ -20,18 +38,41 @@ impl NyashBox for JitHostcallRegistryBox { let payload = serde_json::json!({ "readonly": ro, "mutating": mu }); StringBox::new(payload.to_string()) } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::()) } - fn type_name(&self) -> &'static str { "JitHostcallRegistryBox" } - fn clone_box(&self) -> Box { Box::new(Self { base: self.base.clone() }) } - fn share_box(&self) -> Box { self.clone_box() } + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + BoolBox::new(other.as_any().is::()) + } + fn type_name(&self) -> &'static str { + "JitHostcallRegistryBox" + } + fn clone_box(&self) -> Box { + Box::new(Self { + base: self.base.clone(), + }) + } + fn share_box(&self) -> Box { + self.clone_box() + } } impl JitHostcallRegistryBox { - pub fn add_readonly(&self, sym: &str) -> Box { crate::jit::hostcall_registry::add_readonly(sym); Box::new(VoidBox::new()) } - pub fn add_mutating(&self, sym: &str) -> Box { crate::jit::hostcall_registry::add_mutating(sym); Box::new(VoidBox::new()) } - pub fn set_from_csv(&self, ro_csv: &str, mu_csv: &str) -> Box { crate::jit::hostcall_registry::set_from_csv(ro_csv, mu_csv); Box::new(VoidBox::new()) } + pub fn add_readonly(&self, sym: &str) -> Box { + crate::jit::hostcall_registry::add_readonly(sym); + Box::new(VoidBox::new()) + } + pub fn add_mutating(&self, sym: &str) -> Box { + crate::jit::hostcall_registry::add_mutating(sym); + Box::new(VoidBox::new()) + } + pub fn set_from_csv(&self, ro_csv: &str, mu_csv: &str) -> Box { + 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 { 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")) + } } } diff --git a/src/boxes/jit_policy_box.rs b/src/boxes/jit_policy_box.rs index c8fafc5b..ed6f0855 100644 --- a/src/boxes/jit_policy_box.rs +++ b/src/boxes/jit_policy_box.rs @@ -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; #[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 { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { 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 } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + 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 { fn to_string_box(&self) -> StringBox { 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) } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::()) } - fn type_name(&self) -> &'static str { "JitPolicyBox" } - fn clone_box(&self) -> Box { Box::new(Self { base: self.base.clone() }) } - fn share_box(&self) -> Box { self.clone_box() } + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + BoolBox::new(other.as_any().is::()) + } + fn type_name(&self) -> &'static str { + "JitPolicyBox" + } + fn clone_box(&self) -> Box { + Box::new(Self { + base: self.base.clone(), + }) + } + fn share_box(&self) -> Box { + self.clone_box() + } } // Methods (exposed via VM dispatch): @@ -32,19 +64,26 @@ impl JitPolicyBox { let mut cur = crate::jit::policy::current(); match name { "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); Box::new(VoidBox::new()) } pub fn get_flag(&self, name: &str) -> Box { 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)) } pub fn set_whitelist_csv(&self, csv: &str) -> Box { 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); 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_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); Box::new(VoidBox::new()) diff --git a/src/boxes/jit_stats_box.rs b/src/boxes/jit_stats_box.rs index 3f70643b..a1ea1757 100644 --- a/src/boxes/jit_stats_box.rs +++ b/src/boxes/jit_stats_box.rs @@ -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; #[derive(Debug, Clone)] -pub struct JitStatsBox { base: BoxBase } +pub struct JitStatsBox { + base: BoxBase, +} impl JitStatsBox { - pub fn new() -> Self { Self { base: BoxBase::new() } } + pub fn new() -> Self { + Self { + base: BoxBase::new(), + } + } pub fn to_json(&self) -> Box { let cfg = crate::jit::config::current(); 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!({ "version": 1, "abi_mode": mode, @@ -25,17 +35,37 @@ impl JitStatsBox { } impl BoxCore for JitStatsBox { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { 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 } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + 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 { - fn to_string_box(&self) -> StringBox { StringBox::new(self.to_json().to_string_box().value) } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::()) } - fn type_name(&self) -> &'static str { "JitStatsBox" } - fn clone_box(&self) -> Box { Box::new(self.clone()) } - fn share_box(&self) -> Box { self.clone_box() } + fn to_string_box(&self) -> StringBox { + StringBox::new(self.to_json().to_string_box().value) + } + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + BoolBox::new(other.as_any().is::()) + } + fn type_name(&self) -> &'static str { + "JitStatsBox" + } + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + fn share_box(&self) -> Box { + self.clone_box() + } } diff --git a/src/boxes/jit_strict_box.rs b/src/boxes/jit_strict_box.rs index c59fd492..c0f5e14e 100644 --- a/src/boxes/jit_strict_box.rs +++ b/src/boxes/jit_strict_box.rs @@ -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; #[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 { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { 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 } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + 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 { - fn to_string_box(&self) -> StringBox { StringBox::new("JitStrictBox".to_string()) } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { BoolBox::new(other.as_any().is::()) } - fn type_name(&self) -> &'static str { "JitStrictBox" } - fn clone_box(&self) -> Box { Box::new(Self { base: self.base.clone() }) } - fn share_box(&self) -> Box { self.clone_box() } + fn to_string_box(&self) -> StringBox { + StringBox::new("JitStrictBox".to_string()) + } + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + BoolBox::new(other.as_any().is::()) + } + fn type_name(&self) -> &'static str { + "JitStrictBox" + } + fn clone_box(&self) -> Box { + Box::new(Self { + base: self.base.clone(), + }) + } + fn share_box(&self) -> Box { + self.clone_box() + } } impl JitStrictBox { @@ -27,8 +57,12 @@ impl JitStrictBox { pub fn enable(&self, on: bool) -> Box { if on { 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_ARGS_HANDLE_ONLY").ok().is_none() { std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "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_ARGS_HANDLE_ONLY").ok().is_none() { + std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1"); + } } else { std::env::remove_var("NYASH_JIT_STRICT"); } @@ -36,11 +70,19 @@ impl JitStrictBox { } pub fn set_only(&self, on: bool) -> Box { - 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()) } pub fn set_handle_only(&self, on: bool) -> Box { - 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()) } @@ -60,4 +102,3 @@ impl JitStrictBox { Box::new(VoidBox::new()) } } - diff --git a/src/boxes/json/mod.rs b/src/boxes/json/mod.rs index 1484f91f..a169ad95 100644 --- a/src/boxes/json/mod.rs +++ b/src/boxes/json/mod.rs @@ -2,12 +2,12 @@ // Nyashの箱システムによるJSON解析・生成を提供します。 // 参考: 既存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::map_box::MapBox; +use serde_json::{Error, Value}; use std::any::Any; use std::sync::RwLock; -use serde_json::{Value, Error}; #[derive(Debug)] pub struct JSONBox { @@ -18,7 +18,7 @@ pub struct JSONBox { impl Clone for JSONBox { fn clone(&self) -> Self { let value_clone = self.value.read().unwrap().clone(); - + Self { value: RwLock::new(value_clone), base: BoxBase::new(), // New unique ID for clone @@ -29,24 +29,24 @@ impl Clone for JSONBox { impl JSONBox { pub fn from_str(s: &str) -> Result { let value = serde_json::from_str(s)?; - Ok(JSONBox { - value: RwLock::new(value), - base: BoxBase::new() + Ok(JSONBox { + value: RwLock::new(value), + base: BoxBase::new(), }) } - + pub fn new(value: Value) -> Self { - JSONBox { - value: RwLock::new(value), - base: BoxBase::new() + JSONBox { + value: RwLock::new(value), + base: BoxBase::new(), } } - + pub fn to_string(&self) -> String { let value = self.value.read().unwrap(); value.to_string() } - + /// JSONパース pub fn parse(data: Box) -> Box { let json_str = data.to_string_box().value; @@ -55,17 +55,17 @@ impl JSONBox { Err(e) => Box::new(StringBox::new(&format!("Error parsing JSON: {}", e))), } } - + /// JSON文字列化 pub fn stringify(&self) -> Box { Box::new(StringBox::new(&self.to_string())) } - + /// 値取得 pub fn get(&self, key: Box) -> Box { let key_str = key.to_string_box().value; let value = self.value.read().unwrap(); - + if let Some(obj) = value.as_object() { if let Some(val) = obj.get(&key_str) { json_value_to_nyash_box(val) @@ -86,14 +86,14 @@ impl JSONBox { Box::new(crate::boxes::null_box::NullBox::new()) } } - + /// 値設定 pub fn set(&self, key: Box, new_value: Box) -> Box { let key_str = key.to_string_box().value; let mut value = self.value.write().unwrap(); - + let json_value = nyash_box_to_json_value(new_value); - + if let Some(obj) = value.as_object_mut() { obj.insert(key_str, json_value); Box::new(StringBox::new("ok")) @@ -101,31 +101,31 @@ impl JSONBox { Box::new(StringBox::new("Error: JSONBox is not an object")) } } - + /// キー存在チェック pub fn has(&self, key: Box) -> Box { let key_str = key.to_string_box().value; let value = self.value.read().unwrap(); - + if let Some(obj) = value.as_object() { Box::new(BoolBox::new(obj.contains_key(&key_str))) } else { Box::new(BoolBox::new(false)) } } - + /// すべてのキーを取得 pub fn keys(&self) -> Box { let value = self.value.read().unwrap(); let array = ArrayBox::new(); - + if let Some(obj) = value.as_object() { for key in obj.keys() { // ArrayBoxのpushメソッドは&selfなので、直接呼び出し可能 let _ = array.push(Box::new(StringBox::new(key))); } } - + Box::new(array) } } @@ -134,11 +134,11 @@ impl BoxCore for JSONBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let value = self.value.read().unwrap(); let json_type = match *value { @@ -148,18 +148,18 @@ impl BoxCore for JSONBox { Value::String(_) => "string", Value::Array(ref arr) => { return write!(f, "JSONBox[array:{}]", arr.len()); - }, + } Value::Object(ref obj) => { return write!(f, "JSONBox[object:{}]", obj.len()); - }, + } }; write!(f, "JSONBox[{}]", json_type) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -175,7 +175,7 @@ impl NyashBox for JSONBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -186,12 +186,10 @@ impl NyashBox for JSONBox { StringBox::new(value.to_string()) } - fn type_name(&self) -> &'static str { "JSONBox" } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_json) = other.as_any().downcast_ref::() { let self_value = self.value.read().unwrap(); @@ -230,10 +228,7 @@ fn json_value_to_nyash_box(value: &Value) -> Box { Value::Object(obj) => { let map_box = MapBox::new(); for (key, val) in obj { - map_box.set( - Box::new(StringBox::new(key)), - json_value_to_nyash_box(val) - ); + map_box.set(Box::new(StringBox::new(key)), json_value_to_nyash_box(val)); } Box::new(map_box) } @@ -242,7 +237,11 @@ fn json_value_to_nyash_box(value: &Value) -> Box { /// NyashBox を JSON Value に変換 fn nyash_box_to_json_value(value: Box) -> Value { - if value.as_any().downcast_ref::().is_some() { + if value + .as_any() + .downcast_ref::() + .is_some() + { Value::Null } else if let Some(bool_box) = value.as_any().downcast_ref::() { Value::Bool(bool_box.value) @@ -259,7 +258,8 @@ fn nyash_box_to_json_value(value: Box) -> Value { Value::String(string_box.value.clone()) } else if let Some(array_box) = value.as_any().downcast_ref::() { let items = array_box.items.read().unwrap(); - let arr: Vec = items.iter() + let arr: Vec = items + .iter() .map(|item| nyash_box_to_json_value(item.clone_box())) .collect(); Value::Array(arr) diff --git a/src/boxes/map_box.rs b/src/boxes/map_box.rs index 735adfcc..701b6ba4 100644 --- a/src/boxes/map_box.rs +++ b/src/boxes/map_box.rs @@ -1,10 +1,10 @@ /*! 🗄️ MapBox - キー値ストレージBox - * + * * ## 📝 概要 * 高性能キー値ストレージを提供するBox。 * JavaScript Map、Python dict、C# Dictionaryと同等機能。 * 動的データ管理やキャッシュ実装に最適。 - * + * * ## 🛠️ 利用可能メソッド * - `set(key, value)` - キー値ペア設定 * - `get(key)` - 値取得 @@ -15,21 +15,21 @@ * - `values()` - 全値取得 * - `size()` - データ数取得 * - `isEmpty()` - 空判定 - * + * * ## 💡 使用例 * ```nyash * local map, result * map = new MapBox() - * + * * // データ設定 * map.set("name", "Alice") * map.set("age", 25) * map.set("active", true) - * + * * // データ取得 * result = map.get("name") // "Alice" * print("User: " + result) - * + * * // 存在確認 * if (map.has("email")) { * print("Email: " + map.get("email")) @@ -37,7 +37,7 @@ * print("No email registered") * } * ``` - * + * * ## 🎮 実用例 - ゲーム設定管理 * ```nyash * static box GameConfig { @@ -68,7 +68,7 @@ * } * } * ``` - * + * * ## 🔍 キャッシュ実装例 * ```nyash * static box APICache { @@ -95,7 +95,7 @@ * } * } * ``` - * + * * ## ⚠️ 注意 * - キーは自動的に文字列変換される * - スレッドセーフ (Arc使用) @@ -103,41 +103,45 @@ * - 存在しないキーの取得は "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 std::fmt::{Debug, Display}; use std::any::Any; use std::collections::HashMap; -use std::sync::{Arc, RwLock}; // Arc追加 +use std::fmt::{Debug, Display}; +use std::sync::{Arc, RwLock}; // Arc追加 /// キーバリューストアを表すBox pub struct MapBox { - data: Arc>>>, // Arc追加 + data: Arc>>>, // Arc追加 base: BoxBase, } impl MapBox { pub fn new() -> Self { Self { - data: Arc::new(RwLock::new(HashMap::new())), // Arc::new追加 + data: Arc::new(RwLock::new(HashMap::new())), // Arc::new追加 base: BoxBase::new(), } } - + /// 値を設定 pub fn set(&self, key: Box, value: Box) -> Box { let key_str = key.to_string_box().value; self.data.write().unwrap().insert(key_str.clone(), value); Box::new(StringBox::new(&format!("Set key: {}", key_str))) } - + /// 値を取得 pub fn get(&self, key: Box) -> Box { let key_str = key.to_string_box().value; match self.data.read().unwrap().get(&key_str) { Some(value) => { #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] - if value.as_any().downcast_ref::().is_some() { + if value + .as_any() + .downcast_ref::() + .is_some() + { return value.share_box(); } value.clone_box() @@ -145,13 +149,15 @@ impl MapBox { None => Box::new(StringBox::new(&format!("Key not found: {}", key_str))), } } - + /// キーが存在するかチェック pub fn has(&self, key: Box) -> Box { 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), + )) } - + /// キーを削除 pub fn delete(&self, key: Box) -> Box { let key_str = key.to_string_box().value; @@ -160,7 +166,7 @@ impl MapBox { None => Box::new(StringBox::new(&format!("Key not found: {}", key_str))), } } - + /// 全てのキーを取得 pub fn keys(&self) -> Box { let keys: Vec = self.data.read().unwrap().keys().cloned().collect(); @@ -170,10 +176,13 @@ impl MapBox { } Box::new(array) } - + /// 全ての値を取得 pub fn values(&self) -> Box { - let values: Vec> = self.data.read().unwrap() + let values: Vec> = self + .data + .read() + .unwrap() .values() .map(|v| v.clone_box()) .collect(); @@ -183,45 +192,46 @@ impl MapBox { } Box::new(array) } - + /// サイズを取得 pub fn size(&self) -> Box { Box::new(IntegerBox::new(self.data.read().unwrap().len() as i64)) } - + /// 全てクリア pub fn clear(&self) -> Box { self.data.write().unwrap().clear(); Box::new(StringBox::new("Map cleared")) } - + /// 各要素に対して関数を実行 pub fn forEach(&self, _callback: Box) -> Box { // 簡易実装:callbackの実行はスキップ let count = self.data.read().unwrap().len(); Box::new(StringBox::new(&format!("Iterated over {} items", count))) } - + /// JSON文字列に変換 pub fn toJSON(&self) -> Box { let data = self.data.read().unwrap(); let mut json_parts = Vec::new(); - + for (key, value) in data.iter() { let value_str = value.to_string_box().value; // 値が数値の場合はそのまま、文字列の場合は引用符で囲む - let formatted_value = if value.as_any().downcast_ref::().is_some() - || value.as_any().downcast_ref::().is_some() { + let formatted_value = if value.as_any().downcast_ref::().is_some() + || value.as_any().downcast_ref::().is_some() + { value_str } else { format!("\"{}\"", value_str.replace("\"", "\\\"")) }; json_parts.push(format!("\"{}\":{}", key, formatted_value)); } - + Box::new(StringBox::new(&format!("{{{}}}", json_parts.join(",")))) } - + /// 内部データへのアクセス(JSONBox用) pub fn get_data(&self) -> &RwLock>> { &self.data @@ -233,11 +243,12 @@ impl Clone for MapBox { fn clone(&self) -> Self { // ディープコピー(独立インスタンス) let data_guard = self.data.read().unwrap(); - let cloned_data: HashMap> = data_guard.iter() - .map(|(k, v)| (k.clone(), v.clone_box())) // 要素もディープコピー + let cloned_data: HashMap> = data_guard + .iter() + .map(|(k, v)| (k.clone(), v.clone_box())) // 要素もディープコピー .collect(); MapBox { - data: Arc::new(RwLock::new(cloned_data)), // 新しいArc + data: Arc::new(RwLock::new(cloned_data)), // 新しいArc base: BoxBase::new(), } } @@ -247,50 +258,51 @@ impl BoxCore for MapBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let size = self.data.read().unwrap().len(); write!(f, "MapBox(size={})", size) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } } impl NyashBox for MapBox { - fn is_identity(&self) -> bool { true } + fn is_identity(&self) -> bool { + true + } fn type_name(&self) -> &'static str { "MapBox" } - + fn to_string_box(&self) -> StringBox { let size = self.data.read().unwrap().len(); StringBox::new(&format!("MapBox(size={})", size)) } - - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 🎯 状態共有の核心実装 fn share_box(&self) -> Box { let new_instance = MapBox { - data: Arc::clone(&self.data), // Arcクローンで状態共有 - base: BoxBase::new(), // 新しいID + data: Arc::clone(&self.data), // Arcクローンで状態共有 + base: BoxBase::new(), // 新しいID }; Box::new(new_instance) } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_map) = other.as_any().downcast_ref::() { // 同じインスタンスかチェック(データの共有を考慮) @@ -299,7 +311,6 @@ impl NyashBox for MapBox { BoolBox::new(false) } } - } impl Display for MapBox { diff --git a/src/boxes/math_box.rs b/src/boxes/math_box.rs index 86ce0283..287e15b8 100644 --- a/src/boxes/math_box.rs +++ b/src/boxes/math_box.rs @@ -1,54 +1,54 @@ /*! 🧮 MathBox - 数学計算Box - * + * * ## 📝 概要 * 高度な数学演算を提供するBox。Python mathモジュールや * JavaScript Math オブジェクトと同様の機能を提供。 - * + * * ## 🛠️ 利用可能メソッド - * + * * ### 🔢 基本計算 * - `abs(value)` - 絶対値 * - `max(a, b)` - 最大値 * - `min(a, b)` - 最小値 * - `pow(base, exp)` - 累乗 (base^exp) * - `sqrt(value)` - 平方根 - * + * * ### 📐 三角関数 * - `sin(radians)` - 正弦 * - `cos(radians)` - 余弦 * - `tan(radians)` - 正接 - * + * * ### 📊 対数・指数関数 * - `log(value)` - 自然対数 (ln) * - `log10(value)` - 常用対数 * - `exp(value)` - 指数関数 (e^x) - * + * * ### 🔄 丸め関数 * - `floor(value)` - 切り下げ * - `ceil(value)` - 切り上げ * - `round(value)` - 四捨五入 - * + * * ### 📏 定数取得 * - `getPi()` - 円周率π (3.14159...) * - `getE()` - 自然対数の底e (2.71828...) - * + * * ## 💡 使用例 * ```nyash * local math, result * math = new MathBox() - * + * * result = math.abs(-42) // 42 * result = math.max(10, 25) // 25 * result = math.sqrt(16) // 4.0 * result = math.pow(2, 3) // 8.0 * result = math.sin(math.getPi() / 2) // 1.0 - * + * * // 計算例 * local pi, area * pi = math.getPi() * area = pi * math.pow(5, 2) // 半径5の円の面積 * ``` - * + * * ## ⚠️ 注意 * - 三角関数の引数はラジアン * - 負数の平方根・対数はエラー @@ -56,9 +56,9 @@ * - 整数演算は自動でFloatBoxに変換される場合あり */ -use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase}; -use std::fmt::{Debug, Display}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox}; use std::any::Any; +use std::fmt::{Debug, Display}; /// 数学演算を提供するBox #[derive(Debug, Clone)] @@ -68,11 +68,11 @@ pub struct MathBox { impl MathBox { pub fn new() -> Self { - Self { - base: BoxBase::new() + Self { + base: BoxBase::new(), } } - + /// 絶対値を計算 pub fn abs(&self, value: Box) -> Box { if let Some(int_box) = value.as_any().downcast_ref::() { @@ -83,46 +83,46 @@ impl MathBox { Box::new(StringBox::new("Error: abs() requires numeric input")) } } - + /// 最大値を返す pub fn max(&self, a: Box, b: Box) -> Box { if let (Some(a_int), Some(b_int)) = ( a.as_any().downcast_ref::(), - b.as_any().downcast_ref::() + b.as_any().downcast_ref::(), ) { Box::new(IntegerBox::new(a_int.value.max(b_int.value))) } else if let (Some(a_float), Some(b_float)) = ( a.as_any().downcast_ref::(), - b.as_any().downcast_ref::() + b.as_any().downcast_ref::(), ) { Box::new(FloatBox::new(a_float.value.max(b_float.value))) } else { Box::new(StringBox::new("Error: max() requires numeric inputs")) } } - + /// 最小値を返す pub fn min(&self, a: Box, b: Box) -> Box { if let (Some(a_int), Some(b_int)) = ( a.as_any().downcast_ref::(), - b.as_any().downcast_ref::() + b.as_any().downcast_ref::(), ) { Box::new(IntegerBox::new(a_int.value.min(b_int.value))) } else if let (Some(a_float), Some(b_float)) = ( a.as_any().downcast_ref::(), - b.as_any().downcast_ref::() + b.as_any().downcast_ref::(), ) { Box::new(FloatBox::new(a_float.value.min(b_float.value))) } else { Box::new(StringBox::new("Error: min() requires numeric inputs")) } } - + /// 累乗を計算 pub fn pow(&self, base: Box, exp: Box) -> Box { if let (Some(base_int), Some(exp_int)) = ( base.as_any().downcast_ref::(), - exp.as_any().downcast_ref::() + exp.as_any().downcast_ref::(), ) { if exp_int.value >= 0 { let result = (base_int.value as f64).powi(exp_int.value as i32); @@ -134,7 +134,7 @@ impl MathBox { Box::new(StringBox::new("Error: pow() requires numeric inputs")) } } - + /// 平方根を計算 pub fn sqrt(&self, value: Box) -> Box { if let Some(int_box) = value.as_any().downcast_ref::() { @@ -153,19 +153,19 @@ impl MathBox { Box::new(StringBox::new("Error: sqrt() requires numeric input")) } } - + /// 円周率πを返す #[allow(non_snake_case)] pub fn getPi(&self) -> Box { Box::new(FloatBox::new(std::f64::consts::PI)) } - + /// 自然対数の底eを返す #[allow(non_snake_case)] pub fn getE(&self) -> Box { Box::new(FloatBox::new(std::f64::consts::E)) } - + /// サイン(正弦) pub fn sin(&self, value: Box) -> Box { if let Some(int_box) = value.as_any().downcast_ref::() { @@ -176,7 +176,7 @@ impl MathBox { Box::new(StringBox::new("Error: sin() requires numeric input")) } } - + /// コサイン(余弦) pub fn cos(&self, value: Box) -> Box { if let Some(int_box) = value.as_any().downcast_ref::() { @@ -187,7 +187,7 @@ impl MathBox { Box::new(StringBox::new("Error: cos() requires numeric input")) } } - + /// タンジェント(正接) pub fn tan(&self, value: Box) -> Box { if let Some(int_box) = value.as_any().downcast_ref::() { @@ -198,7 +198,7 @@ impl MathBox { Box::new(StringBox::new("Error: tan() requires numeric input")) } } - + /// 自然対数 pub fn log(&self, value: Box) -> Box { if let Some(int_box) = value.as_any().downcast_ref::() { @@ -217,7 +217,7 @@ impl MathBox { Box::new(StringBox::new("Error: log() requires numeric input")) } } - + /// 常用対数(底10) pub fn log10(&self, value: Box) -> Box { if let Some(int_box) = value.as_any().downcast_ref::() { @@ -236,7 +236,7 @@ impl MathBox { Box::new(StringBox::new("Error: log10() requires numeric input")) } } - + /// 指数関数(e^x) pub fn exp(&self, value: Box) -> Box { if let Some(int_box) = value.as_any().downcast_ref::() { @@ -247,33 +247,33 @@ impl MathBox { Box::new(StringBox::new("Error: exp() requires numeric input")) } } - + /// 床関数(切り下げ) pub fn floor(&self, value: Box) -> Box { if let Some(int_box) = value.as_any().downcast_ref::() { - Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま + Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま } else if let Some(float_box) = value.as_any().downcast_ref::() { Box::new(IntegerBox::new(float_box.value.floor() as i64)) } else { Box::new(StringBox::new("Error: floor() requires numeric input")) } } - + /// 天井関数(切り上げ) pub fn ceil(&self, value: Box) -> Box { if let Some(int_box) = value.as_any().downcast_ref::() { - Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま + Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま } else if let Some(float_box) = value.as_any().downcast_ref::() { Box::new(IntegerBox::new(float_box.value.ceil() as i64)) } else { Box::new(StringBox::new("Error: ceil() requires numeric input")) } } - + /// 四捨五入 pub fn round(&self, value: Box) -> Box { if let Some(int_box) = value.as_any().downcast_ref::() { - Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま + Box::new(IntegerBox::new(int_box.value)) // 整数はそのまま } else if let Some(float_box) = value.as_any().downcast_ref::() { Box::new(IntegerBox::new(float_box.value.round() as i64)) } else { @@ -286,19 +286,19 @@ impl BoxCore for MathBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "MathBox()") } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -308,20 +308,20 @@ impl NyashBox for MathBox { fn type_name(&self) -> &'static str { "MathBox" } - + fn to_string_box(&self) -> StringBox { StringBox::new("MathBox()") } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_math) = other.as_any().downcast_ref::() { BoolBox::new(self.box_id() == other_math.box_id()) @@ -329,7 +329,6 @@ impl NyashBox for MathBox { BoolBox::new(false) } } - } impl Display for MathBox { @@ -347,9 +346,9 @@ pub struct FloatBox { impl FloatBox { pub fn new(value: f64) -> Self { - Self { - value, - base: BoxBase::new() + Self { + value, + base: BoxBase::new(), } } } @@ -358,19 +357,19 @@ impl BoxCore for FloatBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.value) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -380,20 +379,20 @@ impl NyashBox for FloatBox { fn type_name(&self) -> &'static str { "FloatBox" } - + fn to_string_box(&self) -> StringBox { StringBox::new(&self.value.to_string()) } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_float) = other.as_any().downcast_ref::() { BoolBox::new((self.value - other_float.value).abs() < f64::EPSILON) @@ -403,7 +402,6 @@ impl NyashBox for FloatBox { BoolBox::new(false) } } - } impl Display for FloatBox { @@ -423,19 +421,19 @@ pub struct RangeBox { impl RangeBox { pub fn new(start: i64, end: i64, step: i64) -> Self { - Self { - start, - end, - step, - base: BoxBase::new() + Self { + start, + end, + step, + base: BoxBase::new(), } } - + /// イテレータとして値を生成 pub fn iter(&self) -> Vec { let mut result = Vec::new(); let mut current = self.start; - + if self.step > 0 { while current < self.end { result.push(current); @@ -447,7 +445,7 @@ impl RangeBox { current += self.step; } } - + result } } @@ -456,19 +454,19 @@ impl BoxCore for RangeBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "Range({}, {}, {})", self.start, self.end, self.step) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -478,36 +476,38 @@ impl NyashBox for RangeBox { fn type_name(&self) -> &'static str { "RangeBox" } - + 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 { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_range) = other.as_any().downcast_ref::() { BoolBox::new( - self.start == other_range.start && - self.end == other_range.end && - self.step == other_range.step + self.start == other_range.start + && self.end == other_range.end + && self.step == other_range.step, ) } else { BoolBox::new(false) } } - } impl Display for RangeBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/mod.rs b/src/boxes/mod.rs index 7a563736..c6688947 100644 --- a/src/boxes/mod.rs +++ b/src/boxes/mod.rs @@ -1,38 +1,38 @@ /*! 🎯 Nyash Box実装モジュール * Everything is Box哲学に基づく各Box型の実装 - * + * * ## 📦 利用可能なBox一覧 - * + * * ### 🔤 基本データ型Box * - **StringBox**: 文字列操作 - `"Hello".length()`, `str.split(",")` * - **IntegerBox**: 整数計算 - `42.add(8)`, `num.toString()` * - **BoolBox**: 真偽値 - `true.not()`, `flag.toString()` - * + * * ### 🧮 計算・ユーティリティBox * - **MathBox**: 数学関数 - `Math.sin(x)`, `Math.random()` * - **TimeBox**: 時間操作 - `Time.now()`, `time.format()` * - **RandomBox**: 乱数生成 - `Random.int(10)`, `Random.choice(array)` - * + * * ### 🖥️ システム・IO Box * - **ConsoleBox**: コンソール出力 - `console.log()`, `console.error()` * - **DebugBox**: デバッグ支援 - `debug.trace()`, `debug.memory()` * - **SoundBox**: 音声再生 - `sound.beep()`, `sound.play(file)` - * + * * ### 🗄️ コレクション・データBox * - **MapBox**: キー値ストレージ - `map.set(key, val)`, `map.get(key)` * - **NullBox**: NULL値表現 - `null.toString()` → "void" - * + * * ### 🖼️ GUI・グラフィックBox * - **EguiBox**: デスクトップGUI - `gui.setTitle()`, `gui.run()` - * + * * ### 🌐 Web専用Box (WASM環境) * - **WebDisplayBox**: HTML表示 - `display.show(html)` * - **WebConsoleBox**: ブラウザコンソール - `webConsole.log()` * - **WebCanvasBox**: Canvas描画 - `canvas.drawRect()` - * + * * ### 🔗 通信・ネットワークBox * - **SimpleIntentBox**: P2P通信 - `intent.send()`, `intent.on()` - * + * * ## 💡 使用例 * ```nyash * // 基本的な使い方 @@ -40,7 +40,7 @@ * str = "Nyash" * num = 42 * result = str.concat(" v") + num.toString() - * + * * // GUIアプリ作成 * local app * app = new EguiBox() @@ -53,41 +53,41 @@ #![allow(non_snake_case)] // 各Boxモジュールを宣言 -pub mod string_box; -pub mod integer_box; pub mod bool_box; -pub mod math_box; -pub mod time_box; pub mod debug_box; +pub mod integer_box; +pub mod math_box; pub mod random_box; +pub mod string_box; +pub mod time_box; // These boxes use web APIs that require special handling in WASM +pub mod aot_compiler_box; +pub mod aot_config_box; #[cfg(not(target_arch = "wasm32"))] -pub mod timer_box; +pub mod audio_box; #[cfg(not(target_arch = "wasm32"))] pub mod canvas_event_box; #[cfg(not(target_arch = "wasm32"))] pub mod canvas_loop_box; -#[cfg(not(target_arch = "wasm32"))] -pub mod audio_box; +pub mod console_box; +pub mod debug_config_box; +pub mod function_box; +pub mod gc_config_box; +pub mod jit_config_box; +pub mod jit_events_box; +pub mod jit_hostcall_registry_box; +pub mod jit_policy_box; +pub mod jit_stats_box; +pub mod jit_strict_box; +pub mod map_box; #[cfg(not(target_arch = "wasm32"))] pub mod qr_box; -pub mod sound_box; -pub mod map_box; -pub mod console_box; -pub mod jit_config_box; -pub mod jit_stats_box; -pub mod jit_policy_box; -pub mod jit_events_box; -pub mod jit_strict_box; -pub mod jit_hostcall_registry_box; -pub mod debug_config_box; -pub mod gc_config_box; -pub mod aot_config_box; -pub mod aot_compiler_box; -pub mod task_group_box; -pub mod token_box; -pub mod function_box; pub mod ref_cell_box; +pub mod sound_box; +pub mod task_group_box; +#[cfg(not(target_arch = "wasm32"))] +pub mod timer_box; +pub mod token_box; // Web専用Box群(ブラウザ環境でのみ利用可能) #[cfg(target_arch = "wasm32")] @@ -99,34 +99,34 @@ pub mod egui_box; // 共通で使う型とトレイトを再エクスポート // pub use string_box::StringBox; // レガシー実装、box_trait::StringBoxを使用すること -// pub use integer_box::IntegerBox; // レガシー実装、box_trait::IntegerBoxを使用すること +// pub use integer_box::IntegerBox; // レガシー実装、box_trait::IntegerBoxを使用すること // pub use bool_box::BoolBox; // レガシー実装、box_trait::BoolBoxを使用すること -pub use math_box::{MathBox, FloatBox}; -pub use time_box::{TimeBox, DateTimeBox}; -pub use debug_box::DebugBox; -pub use random_box::RandomBox; +pub use aot_compiler_box::AotCompilerBox; +pub use aot_config_box::AotConfigBox; #[cfg(not(target_arch = "wasm32"))] -pub use timer_box::TimerBox; +pub use audio_box::AudioBox; #[cfg(not(target_arch = "wasm32"))] pub use canvas_event_box::CanvasEventBox; #[cfg(not(target_arch = "wasm32"))] pub use canvas_loop_box::CanvasLoopBox; -#[cfg(not(target_arch = "wasm32"))] -pub use audio_box::AudioBox; +pub use console_box::ConsoleBox; +pub use debug_box::DebugBox; +pub use jit_config_box::JitConfigBox; +pub use jit_events_box::JitEventsBox; +pub use jit_hostcall_registry_box::JitHostcallRegistryBox; +pub use jit_policy_box::JitPolicyBox; +pub use jit_stats_box::JitStatsBox; +pub use jit_strict_box::JitStrictBox; +pub use map_box::MapBox; +pub use math_box::{FloatBox, MathBox}; #[cfg(not(target_arch = "wasm32"))] pub use qr_box::QRBox; +pub use random_box::RandomBox; pub use sound_box::SoundBox; -pub use map_box::MapBox; -pub use console_box::ConsoleBox; -pub use jit_config_box::JitConfigBox; -pub use jit_stats_box::JitStatsBox; -pub use jit_policy_box::JitPolicyBox; -pub use jit_events_box::JitEventsBox; -pub use jit_strict_box::JitStrictBox; -pub use jit_hostcall_registry_box::JitHostcallRegistryBox; -pub use aot_config_box::AotConfigBox; -pub use aot_compiler_box::AotCompilerBox; pub use task_group_box::TaskGroupBox; +pub use time_box::{DateTimeBox, TimeBox}; +#[cfg(not(target_arch = "wasm32"))] +pub use timer_box::TimerBox; pub use token_box::TokenBox; // EguiBoxの再エクスポート(非WASM環境のみ) @@ -135,7 +135,7 @@ pub use egui_box::EguiBox; // Web Box群の再エクスポート(WASM環境のみ) #[cfg(target_arch = "wasm32")] -pub use web::{WebDisplayBox, WebConsoleBox, WebCanvasBox}; +pub use web::{WebCanvasBox, WebConsoleBox, WebDisplayBox}; pub mod null_box; @@ -144,35 +144,35 @@ pub mod array; pub mod buffer; pub mod file; pub mod future; -pub mod json; -pub mod result; pub mod http; -pub mod stream; -pub mod regex; -pub mod socket_box; pub mod http_message_box; pub mod http_server_box; +pub mod json; +pub mod regex; +pub mod result; +pub mod socket_box; +pub mod stream; // P2P通信Box群 (NEW! - Completely rewritten) pub mod intent_box; pub mod p2p_box; // null関数も再エクスポート -pub use null_box::{NullBox, null}; +pub use null_box::{null, NullBox}; // High-priority Box types re-export pub use array::ArrayBox; pub use buffer::BufferBox; pub use file::FileBox; -pub use future::{NyashFutureBox, FutureBox, FutureWeak}; -pub use json::JSONBox; -pub use result::{NyashResultBox, ResultBox}; +pub use future::{FutureBox, FutureWeak, NyashFutureBox}; pub use http::HttpClientBox; -pub use stream::{NyashStreamBox, StreamBox}; -pub use regex::RegexBox; -pub use socket_box::SocketBox; pub use http_message_box::{HTTPRequestBox, HTTPResponseBox}; pub use http_server_box::HTTPServerBox; +pub use json::JSONBox; +pub use regex::RegexBox; +pub use result::{NyashResultBox, ResultBox}; +pub use socket_box::SocketBox; +pub use stream::{NyashStreamBox, StreamBox}; // P2P通信Boxの再エクスポート pub use intent_box::IntentBox; diff --git a/src/boxes/null_box.rs b/src/boxes/null_box.rs index 51de0e84..ef761d61 100644 --- a/src/boxes/null_box.rs +++ b/src/boxes/null_box.rs @@ -1,37 +1,37 @@ /*! 🚫 NullBox - NULL値表現Box - * + * * ## 📝 概要 * null/void値を表現する特別なBox。 * JavaScript null、Python None、C# nullと同等の機能を提供。 * NULL安全プログラミングをサポート。 - * + * * ## 🛠️ 利用可能メソッド * - `isNull()` - null判定 (常にtrue) * - `isNotNull()` - 非null判定 (常にfalse) * - `toString()` - 文字列変換 ("null") * - `equals(other)` - 等価比較 (他のnullとのみtrue) - * + * * ## 🛡️ 静的メソッド (null安全機能) * - `NullBox.checkNull(value)` - 値のnull判定 * - `NullBox.checkNotNull(value)` - 値の非null判定 * - `NullBox.getOrDefault(value, default)` - null時デフォルト値取得 - * + * * ## 💡 使用例 * ```nyash * local user, name, default_name - * + * * // null値の作成と判定 * user = null * if (user == null) { * print("User is null") * } - * + * * // null安全な値取得 * name = getUsername() // null の可能性 * default_name = NullBox.getOrDefault(name, "Anonymous") * print("Hello, " + default_name) * ``` - * + * * ## 🎮 実用例 - null安全プログラミング * ```nyash * static box UserManager { @@ -62,12 +62,12 @@ * } * } * ``` - * + * * ## 🔍 デバッグ活用 * ```nyash * local data, result * data = fetchDataFromAPI() // null になる可能性 - * + * * // null チェック付きデバッグ * if (NullBox.checkNull(data)) { * print("Warning: API returned null data") @@ -76,7 +76,7 @@ * result = data.process() * } * ``` - * + * * ## ⚠️ 重要な特徴 * - `null == null` は常にtrue * - `null.toString()` は "null" @@ -84,9 +84,9 @@ * - メソッド呼び出し時のnullチェックでNullPointerException防止 */ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; -use std::fmt::{Debug, Display}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use std::any::Any; +use std::fmt::{Debug, Display}; /// null値を表現するBox #[derive(Debug, Clone)] @@ -96,36 +96,33 @@ pub struct NullBox { impl NullBox { pub fn new() -> Self { - Self { - base: BoxBase::new() + Self { + base: BoxBase::new(), } } - + /// null値かどうかを判定 pub fn is_null(&self) -> bool { - true // NullBoxは常にnull + true // NullBoxは常にnull } - + /// 値がnullでないかを判定 pub fn is_not_null(&self) -> bool { - false // NullBoxは常にnull + false // NullBoxは常にnull } - + /// 他の値がnullかどうかを判定 pub fn check_null(value: &dyn NyashBox) -> bool { value.as_any().downcast_ref::().is_some() } - + /// 他の値がnullでないかを判定 pub fn check_not_null(value: &dyn NyashBox) -> bool { !Self::check_null(value) } - + /// null安全な値の取得 - pub fn get_or_default( - value: &dyn NyashBox, - default: Box - ) -> Box { + pub fn get_or_default(value: &dyn NyashBox, default: Box) -> Box { if Self::check_null(value) { default } else { @@ -138,19 +135,19 @@ impl BoxCore for NullBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "null") } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -160,25 +157,24 @@ impl NyashBox for NullBox { fn type_name(&self) -> &'static str { "NullBox" } - + fn to_string_box(&self) -> StringBox { StringBox::new("null") } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { // すべてのNullBoxは等しい BoolBox::new(other.as_any().downcast_ref::().is_some()) } - } impl Display for NullBox { @@ -196,7 +192,7 @@ pub fn null() -> Box { mod tests { use super::*; use crate::box_trait::IntegerBox; - + #[test] fn test_null_creation() { let null_box = NullBox::new(); @@ -204,41 +200,41 @@ mod tests { assert!(!null_box.is_not_null()); assert_eq!(null_box.to_string_box().value, "null"); } - + #[test] fn test_null_check() { let null_box = null(); let int_box = Box::new(IntegerBox::new(42)); - + assert!(NullBox::check_null(null_box.as_ref())); assert!(!NullBox::check_null(int_box.as_ref())); - + assert!(!NullBox::check_not_null(null_box.as_ref())); assert!(NullBox::check_not_null(int_box.as_ref())); } - + #[test] fn test_null_equality() { let null1 = NullBox::new(); let null2 = NullBox::new(); let int_box = IntegerBox::new(42); - + assert!(null1.equals(&null2).value); assert!(!null1.equals(&int_box).value); } - + #[test] fn test_get_or_default() { let null_box = null(); let default_value = Box::new(IntegerBox::new(100)); let actual_value = Box::new(IntegerBox::new(42)); - + // nullの場合はデフォルト値を返す let result1 = NullBox::get_or_default(null_box.as_ref(), default_value.clone()); assert_eq!(result1.to_string_box().value, "100"); - + // null以外の場合は元の値を返す let result2 = NullBox::get_or_default(actual_value.as_ref(), default_value); assert_eq!(result2.to_string_box().value, "42"); } -} \ No newline at end of file +} diff --git a/src/boxes/p2p_box.rs b/src/boxes/p2p_box.rs index f5b59ede..c4634d4d 100644 --- a/src/boxes/p2p_box.rs +++ b/src/boxes/p2p_box.rs @@ -1,49 +1,49 @@ /*! 📡 P2PBox - Modern P2P Communication Node - * + * * ## 📝 概要 * P2PBoxは現代的なP2P通信ノードを表現するBoxです。 * 新しいアーキテクチャ(IntentBox + MessageBus + Transport)を使用し、 * 構造化メッセージによる安全で明示的な通信を実現します。 - * + * * ## 🎯 AI大会議決定事項準拠 * - **個別送信のみ**: `send(to, message)` 固定API * - **ブロードキャスト除外**: 安全性のため完全除外 * - **明示的API**: 関数オーバーロード不採用 * - **構造化メッセージ**: IntentBox (name + payload) 使用 - * + * * ## 🛠️ 利用可能メソッド * - `new(node_id, transport)` - ノードを作成 * - `send(to, intent)` - 特定ノードにメッセージ送信 * - `on(intent_name, handler)` - イベントリスナー登録 * - `getNodeId()` - ノードID取得 * - `isReachable(node_id)` - ノード到達可能性確認 - * + * * ## 💡 使用例 * ```nyash * // ノード作成 * local alice = new P2PBox("alice", "inprocess") * local bob = new P2PBox("bob", "inprocess") - * + * * // 受信ハンドラ登録 * bob.on("chat.message", function(intent, from) { * print("From " + from + ": " + intent.payload.text) * }) - * + * * // メッセージ送信 * local msg = new IntentBox("chat.message", { text: "Hello P2P!" }) * alice.send("bob", msg) * ``` */ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; +use crate::boxes::result::ResultBox; use crate::boxes::IntentBox; use crate::method_box::MethodBox; -use crate::boxes::result::ResultBox; -use crate::transport::{Transport, InProcessTransport}; +use crate::transport::{InProcessTransport, Transport}; use std::any::Any; -use std::sync::{RwLock, Arc}; -use std::sync::atomic::{AtomicBool, Ordering}; use std::collections::HashMap; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, RwLock}; /// P2PBox - P2P通信ノード (RwLock pattern) #[derive(Debug)] @@ -72,7 +72,7 @@ impl Clone for P2PBox { let handlers_val = HashMap::new(); // Start fresh for cloned instance let last_from_val = self.last_from.read().unwrap().clone(); let last_intent_val = self.last_intent_name.read().unwrap().clone(); - + Self { base: BoxBase::new(), // New unique ID for clone node_id: RwLock::new(node_id_val), @@ -93,7 +93,7 @@ pub enum TransportKind { impl std::str::FromStr for TransportKind { type Err = String; - + fn from_str(s: &str) -> Result { match s.to_lowercase().as_str() { "inprocess" => Ok(TransportKind::InProcess), @@ -136,28 +136,38 @@ impl P2PBox { { if let Ok(mut t) = transport_arc_outer.write() { let transport_arc_for_cb = Arc::clone(&transport_arc_outer); - t.register_intent_handler("sys.ping", Box::new(move |env| { - if let Ok(mut lf) = last_from.write() { *lf = Some(env.from.clone()); } - if let Ok(mut li) = last_intent.write() { *li = Some(env.intent.get_name().to_string_box().value); } - // Reply asynchronously to avoid deep call stacks - let to = env.from.clone(); - let reply = crate::boxes::IntentBox::new("sys.pong".to_string(), serde_json::json!({})); - let transport_arc = Arc::clone(&transport_arc_for_cb); - std::thread::spawn(move || { - // slight delay to avoid lock contention and ordering races - std::thread::sleep(std::time::Duration::from_millis(3)); - if let Ok(transport) = transport_arc.read() { - let _ = transport.send(&to, reply, Default::default()); + t.register_intent_handler( + "sys.ping", + Box::new(move |env| { + if let Ok(mut lf) = last_from.write() { + *lf = Some(env.from.clone()); } - }); - })); + if let Ok(mut li) = last_intent.write() { + *li = Some(env.intent.get_name().to_string_box().value); + } + // Reply asynchronously to avoid deep call stacks + let to = env.from.clone(); + let reply = crate::boxes::IntentBox::new( + "sys.pong".to_string(), + serde_json::json!({}), + ); + let transport_arc = Arc::clone(&transport_arc_for_cb); + std::thread::spawn(move || { + // slight delay to avoid lock contention and ordering races + std::thread::sleep(std::time::Duration::from_millis(3)); + if let Ok(transport) = transport_arc.read() { + let _ = transport.send(&to, reply, Default::default()); + } + }); + }), + ); }; } } p2p } - + /// ノードIDを取得 pub fn get_node_id(&self) -> Box { let node_id = self.node_id.read().unwrap().clone(); @@ -177,14 +187,17 @@ impl P2PBox { // Register temporary transport-level handler for sys.pong if let Ok(mut t) = self.transport.write() { - t.register_intent_handler("sys.pong", Box::new(move |env| { - if active_cb.load(Ordering::SeqCst) { - // record last receive for visibility - // Note: we cannot access self here safely; rely on tx notify only - let _ = env; // suppress unused - let _ = tx.send(()); - } - })); + t.register_intent_handler( + "sys.pong", + Box::new(move |env| { + if active_cb.load(Ordering::SeqCst) { + // record last receive for visibility + // Note: we cannot access self here safely; rely on tx notify only + let _ = env; // suppress unused + let _ = tx.send(()); + } + }), + ); // Send sys.ping let ping = IntentBox::new("sys.ping".to_string(), serde_json::json!({})); @@ -199,7 +212,9 @@ impl P2PBox { } // Wait for pong with timeout - let ok = rx.recv_timeout(std::time::Duration::from_millis(timeout_ms)).is_ok(); + let ok = rx + .recv_timeout(std::time::Duration::from_millis(timeout_ms)) + .is_ok(); active.store(false, Ordering::SeqCst); Box::new(BoolBox::new(ok)) } @@ -208,11 +223,11 @@ impl P2PBox { pub fn ping(&self, to: Box) -> Box { self.ping_with_timeout(to, 300) } - + /// 特定ノードにメッセージを送信 pub fn send(&self, to: Box, intent: Box) -> Box { let to_str = to.to_string_box().value; - + // Extract IntentBox from the generic Box if let Some(intent_box) = intent.as_any().downcast_ref::() { let transport = self.transport.read().unwrap(); @@ -221,20 +236,34 @@ impl P2PBox { // Minimal loopback trace without relying on transport callbacks let self_id = self.node_id.read().unwrap().clone(); if to_str == self_id { - if let Ok(mut lf) = self.last_from.write() { *lf = Some(self_id.clone()); } - if let Ok(mut li) = self.last_intent_name.write() { *li = Some(intent_box.get_name().to_string_box().value); } + if let Ok(mut lf) = self.last_from.write() { + *lf = Some(self_id.clone()); + } + if let Ok(mut li) = self.last_intent_name.write() { + *li = Some(intent_box.get_name().to_string_box().value); + } } Box::new(ResultBox::new_ok(Box::new(BoolBox::new(true)))) - }, - Err(e) => Box::new(ResultBox::new_err(Box::new(StringBox::new(format!("{:?}", e))))), + } + Err(e) => Box::new(ResultBox::new_err(Box::new(StringBox::new(format!( + "{:?}", + e + ))))), } } else { - Box::new(ResultBox::new_err(Box::new(StringBox::new("Second argument must be IntentBox")))) + Box::new(ResultBox::new_err(Box::new(StringBox::new( + "Second argument must be IntentBox", + )))) } } - + /// イベントハンドラーを登録 - fn register_handler_internal(&self, intent_str: &str, handler: &Box, once: bool) -> Box { + fn register_handler_internal( + &self, + intent_str: &str, + handler: &Box, + once: bool, + ) -> Box { // 保存 { let mut handlers = self.handlers.write().unwrap(); @@ -245,7 +274,10 @@ impl P2PBox { let flag = Arc::new(AtomicBool::new(true)); { let mut flags = self.handler_flags.write().unwrap(); - flags.entry(intent_str.to_string()).or_default().push(flag.clone()); + flags + .entry(intent_str.to_string()) + .or_default() + .push(flag.clone()); } // once情報を記録 { @@ -265,82 +297,111 @@ impl P2PBox { // capture flags map to allow removal on once let flags_arc = Arc::clone(&self.handler_flags); let intent_name_closure = intent_name.clone(); - t.register_intent_handler(&intent_name, Box::new(move |env| { - if flag.load(Ordering::SeqCst) { - if let Ok(mut lf) = last_from.write() { *lf = Some(env.from.clone()); } - if let Ok(mut li) = last_intent.write() { *li = Some(env.intent.get_name().to_string_box().value); } - let _ = method_clone.invoke(vec![ - Box::new(env.intent.clone()), - Box::new(StringBox::new(env.from.clone())), - ]); - if once { - flag.store(false, Ordering::SeqCst); - if let Ok(mut flags) = flags_arc.write() { - if let Some(v) = flags.get_mut(&intent_name_closure) { v.clear(); } + t.register_intent_handler( + &intent_name, + Box::new(move |env| { + if flag.load(Ordering::SeqCst) { + if let Ok(mut lf) = last_from.write() { + *lf = Some(env.from.clone()); + } + if let Ok(mut li) = last_intent.write() { + *li = Some(env.intent.get_name().to_string_box().value); + } + let _ = method_clone.invoke(vec![ + Box::new(env.intent.clone()), + Box::new(StringBox::new(env.from.clone())), + ]); + if once { + flag.store(false, Ordering::SeqCst); + if let Ok(mut flags) = flags_arc.write() { + if let Some(v) = flags.get_mut(&intent_name_closure) { + v.clear(); + } + } } } - } - })); + }), + ); // FunctionBox ハンドラー(関数値) - } else if let Some(func_box) = handler.as_any().downcast_ref::() { + } else if let Some(func_box) = handler + .as_any() + .downcast_ref::() + { let func_clone = func_box.clone(); let intent_name = intent_str.to_string(); let last_from = Arc::clone(&self.last_from); let last_intent = Arc::clone(&self.last_intent_name); let flags_arc = Arc::clone(&self.handler_flags); let intent_name_closure = intent_name.clone(); - t.register_intent_handler(&intent_name, Box::new(move |env| { - if flag.load(Ordering::SeqCst) { - if let Ok(mut lf) = last_from.write() { *lf = Some(env.from.clone()); } - if let Ok(mut li) = last_intent.write() { *li = Some(env.intent.get_name().to_string_box().value); } - // 最小インタープリタで FunctionBox を実行 - let mut interp = crate::interpreter::NyashInterpreter::new(); - // キャプチャ注入 - for (k, v) in func_clone.env.captures.iter() { - interp.declare_local_variable(k, v.clone_or_share()); - } - if let Some(me_w) = &func_clone.env.me_value { - if let Some(me_arc) = me_w.upgrade() { - interp.declare_local_variable("me", (*me_arc).clone_or_share()); + t.register_intent_handler( + &intent_name, + Box::new(move |env| { + if flag.load(Ordering::SeqCst) { + if let Ok(mut lf) = last_from.write() { + *lf = Some(env.from.clone()); + } + if let Ok(mut li) = last_intent.write() { + *li = Some(env.intent.get_name().to_string_box().value); + } + // 最小インタープリタで FunctionBox を実行 + let mut interp = crate::interpreter::NyashInterpreter::new(); + // キャプチャ注入 + for (k, v) in func_clone.env.captures.iter() { + interp.declare_local_variable(k, v.clone_or_share()); + } + if let Some(me_w) = &func_clone.env.me_value { + if let Some(me_arc) = me_w.upgrade() { + interp.declare_local_variable("me", (*me_arc).clone_or_share()); + } + } + // 引数束縛: intent, from(必要数だけ) + let args: Vec> = vec![ + Box::new(env.intent.clone()), + Box::new(StringBox::new(env.from.clone())), + ]; + for (i, p) in func_clone.params.iter().enumerate() { + if let Some(av) = args.get(i) { + interp.declare_local_variable(p, av.clone_or_share()); + } + } + // 本体実行 + crate::runtime::global_hooks::push_task_scope(); + for st in &func_clone.body { + let _ = interp.execute_statement(st); + } + crate::runtime::global_hooks::pop_task_scope(); + if once { + flag.store(false, Ordering::SeqCst); + if let Ok(mut flags) = flags_arc.write() { + if let Some(v) = flags.get_mut(&intent_name_closure) { + v.clear(); + } + } } } - // 引数束縛: intent, from(必要数だけ) - let args: Vec> = vec![ - Box::new(env.intent.clone()), - Box::new(StringBox::new(env.from.clone())), - ]; - for (i, p) in func_clone.params.iter().enumerate() { - if let Some(av) = args.get(i) { - interp.declare_local_variable(p, av.clone_or_share()); - } - } - // 本体実行 - crate::runtime::global_hooks::push_task_scope(); - for st in &func_clone.body { - let _ = interp.execute_statement(st); - } - crate::runtime::global_hooks::pop_task_scope(); - if once { - flag.store(false, Ordering::SeqCst); - if let Ok(mut flags) = flags_arc.write() { - if let Some(v) = flags.get_mut(&intent_name_closure) { v.clear(); } - } - } - } - })); + }), + ); } } Box::new(ResultBox::new_ok(Box::new(BoolBox::new(true)))) } /// イベントハンドラーを登録 - pub fn on(&self, intent_name: Box, handler: Box) -> Box { + pub fn on( + &self, + intent_name: Box, + handler: Box, + ) -> Box { let intent_str = intent_name.to_string_box().value; self.register_handler_internal(&intent_str, &handler, false) } /// 一度だけのハンドラー登録 - pub fn on_once(&self, intent_name: Box, handler: Box) -> Box { + pub fn on_once( + &self, + intent_name: Box, + handler: Box, + ) -> Box { let intent_str = intent_name.to_string_box().value; self.register_handler_internal(&intent_str, &handler, true) } @@ -350,7 +411,9 @@ impl P2PBox { let intent_str = intent_name.to_string_box().value; if let Ok(mut flags) = self.handler_flags.write() { if let Some(v) = flags.get_mut(&intent_str) { - for f in v.iter() { f.store(false, Ordering::SeqCst); } + for f in v.iter() { + f.store(false, Ordering::SeqCst); + } v.clear(); } } @@ -364,7 +427,7 @@ impl P2PBox { let transport = self.transport.read().unwrap(); Box::new(BoolBox::new(transport.is_reachable(&node_str))) } - + /// トランスポート種類を取得 pub fn get_transport_type(&self) -> Box { let transport = self.transport.read().unwrap(); @@ -396,11 +459,16 @@ impl P2PBox { // once登録かつ直近受信が同名なら 0 を返す(自己送信の安定化用) if let (Ok(once_map), Ok(last)) = (self.handler_once.read(), self.last_intent_name.read()) { if let Some(true) = once_map.get(&name).copied() { - if let Some(li) = &*last { if li == &name { return Box::new(crate::box_trait::IntegerBox::new(0)); } } + if let Some(li) = &*last { + if li == &name { + return Box::new(crate::box_trait::IntegerBox::new(0)); + } + } } } let flags = self.handler_flags.read().unwrap(); - let cnt = flags.get(&name) + let cnt = flags + .get(&name) .map(|v| v.iter().filter(|f| f.load(Ordering::SeqCst)).count()) .unwrap_or(0); Box::new(crate::box_trait::IntegerBox::new(cnt as i64)) @@ -414,18 +482,21 @@ impl P2PBox { /// 最後に受信したIntent名を取得(ループバック検証用) pub fn get_last_intent_name(&self) -> Box { - let v = self.last_intent_name.read().unwrap().clone().unwrap_or_default(); + let v = self + .last_intent_name + .read() + .unwrap() + .clone() + .unwrap_or_default(); Box::new(StringBox::new(v)) } } - - impl NyashBox for P2PBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + fn share_box(&self) -> Box { // Share underlying transport and state via Arc clones let node_id_val = self.node_id.read().unwrap().clone(); @@ -446,7 +517,7 @@ impl NyashBox for P2PBox { let transport_type = self.transport.read().unwrap().transport_type().to_string(); StringBox::new(format!("P2PBox[{}:{}]", node_id, transport_type)) } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_p2p) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_p2p.base.id) @@ -464,7 +535,7 @@ impl BoxCore for P2PBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } @@ -474,11 +545,11 @@ impl BoxCore for P2PBox { let transport_type = self.transport.read().unwrap().transport_type().to_string(); write!(f, "P2PBox[{}:{}]", node_id, transport_type) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -498,7 +569,10 @@ mod tests { fn self_ping_sets_last_fields() { let p = P2PBox::new("alice".to_string(), TransportKind::InProcess); let intent = IntentBox::new("ping".to_string(), serde_json::json!({})); - let res = p.send(Box::new(StringBox::new("alice".to_string())), Box::new(intent)); + let res = p.send( + Box::new(StringBox::new("alice".to_string())), + Box::new(intent), + ); // Ensure Ok if let Some(r) = res.as_any().downcast_ref::() { assert!(matches!(r, ResultBox::Ok(_))); @@ -506,7 +580,10 @@ mod tests { panic!("send did not return ResultBox"); } assert_eq!(p.get_last_from().to_string_box().value, "alice".to_string()); - assert_eq!(p.get_last_intent_name().to_string_box().value, "ping".to_string()); + assert_eq!( + p.get_last_intent_name().to_string_box().value, + "ping".to_string() + ); } /// Internal helper for tests: register raw Rust handler with optional async reply @@ -521,22 +598,29 @@ mod tests { // Avoid deep clone (which re-registers transport). Use transport directly for reply. let transport_arc = Arc::clone(&self.transport); let reply_name = reply_intent.map(|s| s.to_string()); - t.register_intent_handler(&intent_name, Box::new(move |env| { - if let Ok(mut lf) = last_from.write() { *lf = Some(env.from.clone()); } - if let Ok(mut li) = last_intent.write() { *li = Some(env.intent.get_name().to_string_box().value); } - if let Some(rn) = reply_name.clone() { - let to = env.from.clone(); - let transport_arc = Arc::clone(&transport_arc); - std::thread::spawn(move || { - // slight delay to avoid lock contention - std::thread::sleep(std::time::Duration::from_millis(5)); - let intent = IntentBox::new(rn, serde_json::json!({})); - if let Ok(transport) = transport_arc.read() { - let _ = transport.send(&to, intent, Default::default()); - } - }); - } - })); + t.register_intent_handler( + &intent_name, + Box::new(move |env| { + if let Ok(mut lf) = last_from.write() { + *lf = Some(env.from.clone()); + } + if let Ok(mut li) = last_intent.write() { + *li = Some(env.intent.get_name().to_string_box().value); + } + if let Some(rn) = reply_name.clone() { + let to = env.from.clone(); + let transport_arc = Arc::clone(&transport_arc); + std::thread::spawn(move || { + // slight delay to avoid lock contention + std::thread::sleep(std::time::Duration::from_millis(5)); + let intent = IntentBox::new(rn, serde_json::json!({})); + if let Ok(transport) = transport_arc.read() { + let _ = transport.send(&to, intent, Default::default()); + } + }); + } + }), + ); } } } diff --git a/src/boxes/qr_box.rs b/src/boxes/qr_box.rs index fe67fc11..9d23bd64 100644 --- a/src/boxes/qr_box.rs +++ b/src/boxes/qr_box.rs @@ -1,64 +1,62 @@ /*! * QRBox - QRコード生成・読み取りBox - * + * * ## 📝 概要 * QRコードの生成、読み取り、カスタマイズを統一的に管理するBox。 * アプリ間連携、データ共有、認証システムに最適。 - * + * * ## 🛠️ 利用可能メソッド - * + * * ### 📱 QRコード生成 * - `generate(text)` - テキストからQRコード生成 * - `generateURL(url)` - URL用QRコード生成 * - `generateWiFi(ssid, password, security)` - WiFi設定QR * - `generateContact(name, phone, email)` - 連絡先QR - * + * * ### 🎨 カスタマイズ * - `setSize(width, height)` - QRコードサイズ設定 * - `setColors(fg, bg)` - 前景色・背景色設定 * - `setLogo(image)` - ロゴ埋め込み * - `setErrorCorrection(level)` - エラー訂正レベル - * + * * ### 📷 読み取り * - `scanFromImage(imageData)` - 画像からQR読み取り * - `scanFromCanvas(canvas)` - Canvasから読み取り * - `startCamera()` - カメラ読み取り開始 - * + * * ### 📊 情報取得 * - `getDataURL()` - QRコードのData URL取得 * - `getImageData()` - ImageData形式で取得 * - `getInfo()` - QRコード情報取得 - * + * * ## 💡 使用例 * ```nyash * local qr, canvas * qr = new QRBox() * canvas = new WebCanvasBox("qr-canvas", 300, 300) - * + * * // 基本的なQRコード生成 * qr.generate("https://nyash-lang.org") * qr.setSize(200, 200) * qr.setColors("#000000", "#ffffff") - * + * * // Canvasに描画 * local imageData = qr.getImageData() * canvas.putImageData(imageData, 50, 50) - * + * * // WiFi設定QR * qr.generateWiFi("MyWiFi", "password123", "WPA2") * ``` */ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use std::any::Any; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; #[cfg(target_arch = "wasm32")] -use web_sys::{ - HtmlCanvasElement, CanvasRenderingContext2d, ImageData -}; +use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, ImageData}; /// QRコード管理Box #[derive(Debug, Clone)] @@ -146,7 +144,11 @@ impl QRBox { pub fn get_info(&self) -> String { format!( "Type: {}, Size: {}x{}, Error Correction: {}, Data Length: {}", - self.qr_type, self.size.0, self.size.1, self.error_correction, self.data.len() + self.qr_type, + self.size.0, + self.size.1, + self.error_correction, + self.data.len() ) } @@ -170,7 +172,8 @@ impl QRBox { if let Some(canvas_element) = document.get_element_by_id(canvas_id) { if let Ok(canvas) = canvas_element.dyn_into::() { if let Ok(context) = canvas.get_context("2d") { - if let Ok(ctx) = context.unwrap().dyn_into::() { + if let Ok(ctx) = context.unwrap().dyn_into::() + { return self.draw_simple_qr(&ctx); } } @@ -186,28 +189,29 @@ impl QRBox { fn draw_simple_qr(&self, ctx: &CanvasRenderingContext2d) -> bool { let module_size = 8; let modules = 25; // 25x25のQRコード - + // 背景を描画 ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(&self.background_color)); ctx.fill_rect(0.0, 0.0, self.size.0 as f64, self.size.1 as f64); - + // QRコードパターンを生成(簡易版) ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(&self.foreground_color)); - + // データベースの簡単なハッシュを作成 let hash = self.simple_hash(&self.data); - + for y in 0..modules { for x in 0..modules { // ファインダーパターンの描画 if (x < 7 && y < 7) || (x >= modules - 7 && y < 7) || (x < 7 && y >= modules - 7) { - if (x == 0 || x == 6 || y == 0 || y == 6) || - (x >= 2 && x <= 4 && y >= 2 && y <= 4) { + if (x == 0 || x == 6 || y == 0 || y == 6) + || (x >= 2 && x <= 4 && y >= 2 && y <= 4) + { ctx.fill_rect( (x * module_size) as f64, (y * module_size) as f64, module_size as f64, - module_size as f64 + module_size as f64, ); } } else { @@ -218,13 +222,13 @@ impl QRBox { (x * module_size) as f64, (y * module_size) as f64, module_size as f64, - module_size as f64 + module_size as f64, ); } } } } - + true } @@ -241,10 +245,16 @@ impl QRBox { #[cfg(not(target_arch = "wasm32"))] /// Non-WASM環境用のダミー実装 pub fn draw_to_canvas(&self, canvas_id: &str) -> bool { - println!("QRBox: Drawing QR code to canvas '{}' (simulated)", canvas_id); + println!( + "QRBox: Drawing QR code to canvas '{}' (simulated)", + canvas_id + ); println!(" Data: {}", self.data); println!(" Size: {}x{}", self.size.0, self.size.1); - println!(" Colors: {} on {}", self.foreground_color, self.background_color); + println!( + " Colors: {} on {}", + self.foreground_color, self.background_color + ); true } @@ -264,7 +274,8 @@ impl QRBox { /// バッチ生成機能 pub fn generate_batch(&self, data_list: &[String]) -> Vec { - data_list.iter() + data_list + .iter() .map(|data| format!("QR for: {}", data)) .collect() } @@ -279,7 +290,7 @@ impl QRBox { "H" => 4, _ => 2, }; - + data_len * base_complexity } } @@ -288,19 +299,23 @@ impl BoxCore for QRBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "QRBox(type={}, size={}x{})", self.qr_type, self.size.0, self.size.1) + write!( + f, + "QRBox(type={}, size={}x{})", + self.qr_type, self.size.0, self.size.1 + ) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -310,20 +325,23 @@ impl NyashBox for QRBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } fn to_string_box(&self) -> StringBox { - StringBox::new(format!("QRBox(type={}, size={}x{})", self.qr_type, self.size.0, self.size.1)) + StringBox::new(format!( + "QRBox(type={}, size={}x{})", + self.qr_type, self.size.0, self.size.1 + )) } fn type_name(&self) -> &'static str { "QRBox" } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_qr) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_qr.base.id) @@ -337,4 +355,4 @@ impl std::fmt::Display for QRBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/random_box.rs b/src/boxes/random_box.rs index 86b06ba9..32452780 100644 --- a/src/boxes/random_box.rs +++ b/src/boxes/random_box.rs @@ -1,46 +1,46 @@ /*! 🎲 RandomBox - 乱数生成Box - * + * * ## 📝 概要 * 高品質な乱数生成機能を提供するBox。 * ゲーム開発、統計処理、テストデータ生成に最適。 - * + * * ## 🛠️ 利用可能メソッド - * + * * ### 🔢 基本乱数 * - `random()` - 0.0~1.0の浮動小数点乱数 * - `randInt(min, max)` - 指定範囲の整数乱数 * - `randBool()` - true/falseのランダム選択 * - `seed(value)` - 乱数種を設定(再現可能な乱数) - * + * * ### 🎯 選択・配列操作 * - `choice(array)` - 配列からランダム選択 * - `shuffle(array)` - 配列をシャッフル - * + * * ### 🎨 生成 * - `randString(length)` - ランダム文字列生成 * - `probability(prob)` - 指定確率でtrue - * + * * ## 💡 使用例 * ```nyash * local random, result, dice, array * random = new RandomBox() - * + * * // 基本的な乱数 * result = random.random() // 0.0~1.0 * dice = random.randInt(1, 6) // サイコロ(1-6) * result = random.randBool() // true or false - * + * * // 配列関連 * array = ["apple", "banana", "cherry"] * result = random.choice(array) // ランダム選択 * array = random.shuffle(array) // シャッフル - * + * * // ゲーム用途 * local password, critical_hit * password = random.randString(8) // 8文字のランダム文字列 * critical_hit = random.probability(0.1) // 10%でクリティカル * ``` - * + * * ## 🎮 実用例 * ```nyash * // RPGダメージ計算 @@ -50,7 +50,7 @@ * if (is_critical) { * damage = damage * 2 * } - * + * * // テストデータ生成 * local users, user_id, user_name * users = [] @@ -60,17 +60,17 @@ * users.push(user_name + ":" + user_id) * } * ``` - * + * * ## ⚠️ 注意 * - 暗号学的に安全な乱数ではない(セキュリティ用途非推奨) * - seed()で同じ値を設定すると同じ乱数列を生成(テスト用) * - 大きな配列のshuffleは処理時間が長い場合あり */ -use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox}; use crate::boxes::{ArrayBox, FloatBox}; -use std::fmt::{Debug, Display}; use std::any::Any; +use std::fmt::{Debug, Display}; use std::sync::RwLock; /// 乱数生成を提供するBox @@ -84,7 +84,7 @@ pub struct RandomBox { impl Clone for RandomBox { fn clone(&self) -> Self { let seed_val = *self.seed.read().unwrap(); - + Self { seed: RwLock::new(seed_val), base: BoxBase::new(), // New unique ID for clone @@ -99,13 +99,13 @@ impl RandomBox { .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_nanos() as u64; - + Self { seed: RwLock::new(seed), base: BoxBase::new(), } } - + /// 種を設定 pub fn seed(&self, new_seed: Box) -> Box { if let Some(int_box) = new_seed.as_any().downcast_ref::() { @@ -115,7 +115,7 @@ impl RandomBox { Box::new(StringBox::new("Error: seed() requires integer input")) } } - + /// 次の乱数を生成(線形合同法) fn next_random(&self) -> u64 { let mut seed = self.seed.write().unwrap(); @@ -123,46 +123,53 @@ impl RandomBox { *seed = seed.wrapping_mul(1664525).wrapping_add(1013904223); *seed } - + /// 0.0-1.0の浮動小数点乱数 pub fn random(&self) -> Box { let r = self.next_random(); let normalized = (r as f64) / (u64::MAX as f64); Box::new(FloatBox::new(normalized)) } - + /// 指定範囲の整数乱数 pub fn randInt(&self, min: Box, max: Box) -> Box { if let (Some(min_int), Some(max_int)) = ( min.as_any().downcast_ref::(), - max.as_any().downcast_ref::() + max.as_any().downcast_ref::(), ) { if min_int.value > max_int.value { return Box::new(StringBox::new("Error: min must be <= max")); } - + let range = (max_int.value - min_int.value + 1) as u64; let r = self.next_random() % range; Box::new(IntegerBox::new(min_int.value + r as i64)) } else { - Box::new(StringBox::new("Error: randInt() requires two integer inputs")) + Box::new(StringBox::new( + "Error: randInt() requires two integer inputs", + )) } } - + /// true/falseのランダム選択 pub fn randBool(&self) -> Box { let r = self.next_random(); Box::new(BoolBox::new(r % 2 == 0)) } - + /// 配列からランダム選択 pub fn choice(&self, array: Box) -> Box { if let Some(array_box) = array.as_any().downcast_ref::() { - let length = array_box.length().to_string_box().value.parse::().unwrap_or(0); + let length = array_box + .length() + .to_string_box() + .value + .parse::() + .unwrap_or(0); if length == 0 { return Box::new(StringBox::new("Error: cannot choose from empty array")); } - + let index = self.next_random() % (length as u64); // 新しいArrayBox.get()は既にBoxを返すので、直接使用 array_box.get(Box::new(IntegerBox::new(index as i64))) @@ -170,18 +177,23 @@ impl RandomBox { Box::new(StringBox::new("Error: choice() requires array input")) } } - + /// 配列をシャッフル pub fn shuffle(&self, array: Box) -> Box { if let Some(array_box) = array.as_any().downcast_ref::() { - let length = array_box.length().to_string_box().value.parse::().unwrap_or(0); + let length = array_box + .length() + .to_string_box() + .value + .parse::() + .unwrap_or(0); if length <= 1 { return array; } - + // 新しい配列を作成 let shuffled = ArrayBox::new(); - + // 元の配列の要素を全て新しい配列にコピー for i in 0..length { let element = array_box.get(Box::new(IntegerBox::new(i as i64))); @@ -190,12 +202,12 @@ impl RandomBox { shuffled.push(element); } } - + // 簡易シャッフル実装(完全なFisher-Yatesは複雑なので) // 代わりに、元の配列からランダムに選んで新しい配列を作る let result = ArrayBox::new(); let mut remaining_indices: Vec = (0..length as usize).collect(); - + while !remaining_indices.is_empty() { let random_idx = (self.next_random() % remaining_indices.len() as u64) as usize; let actual_idx = remaining_indices.remove(random_idx); @@ -205,42 +217,44 @@ impl RandomBox { result.push(element); } } - + Box::new(result) } else { Box::new(StringBox::new("Error: shuffle() requires array input")) } } - + /// ランダムな文字列生成 pub fn randString(&self, length: Box) -> Box { if let Some(len_int) = length.as_any().downcast_ref::() { if len_int.value < 0 { return Box::new(StringBox::new("Error: length must be positive")); } - + let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; let char_vec: Vec = chars.chars().collect(); let mut result = String::new(); - + for _ in 0..len_int.value { let index = self.next_random() % (char_vec.len() as u64); result.push(char_vec[index as usize]); } - + Box::new(StringBox::new(&result)) } else { - Box::new(StringBox::new("Error: randString() requires integer length")) + Box::new(StringBox::new( + "Error: randString() requires integer length", + )) } } - + /// 指定確率でtrue pub fn probability(&self, prob: Box) -> Box { if let Some(float_box) = prob.as_any().downcast_ref::() { if float_box.value < 0.0 || float_box.value > 1.0 { return Box::new(StringBox::new("Error: probability must be 0.0-1.0")); } - + let r = self.next_random() as f64 / u64::MAX as f64; Box::new(BoolBox::new(r < float_box.value)) } else if let Some(int_box) = prob.as_any().downcast_ref::() { @@ -248,11 +262,13 @@ impl RandomBox { if prob_val < 0.0 || prob_val > 1.0 { return Box::new(StringBox::new("Error: probability must be 0.0-1.0")); } - + let r = self.next_random() as f64 / u64::MAX as f64; Box::new(BoolBox::new(r < prob_val)) } else { - Box::new(StringBox::new("Error: probability() requires numeric input")) + Box::new(StringBox::new( + "Error: probability() requires numeric input", + )) } } } @@ -261,20 +277,20 @@ impl NyashBox for RandomBox { fn type_name(&self) -> &'static str { "RandomBox" } - + fn to_string_box(&self) -> StringBox { StringBox::new("RandomBox()") } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_random) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_random.base.id) @@ -282,27 +298,25 @@ impl NyashBox for RandomBox { BoolBox::new(false) } } - - } impl BoxCore for RandomBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "RandomBox()") } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -312,4 +326,4 @@ impl Display for RandomBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/ref_cell_box.rs b/src/boxes/ref_cell_box.rs index af35b8d5..a89388da 100644 --- a/src/boxes/ref_cell_box.rs +++ b/src/boxes/ref_cell_box.rs @@ -1,4 +1,4 @@ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use std::any::Any; use std::sync::{Arc, Mutex}; @@ -10,10 +10,16 @@ pub struct RefCellBox { impl RefCellBox { pub fn new(initial: Box) -> Self { - Self { inner: Arc::new(Mutex::new(initial)), base: BoxBase::new() } + Self { + inner: Arc::new(Mutex::new(initial)), + base: BoxBase::new(), + } } pub fn with_inner(inner: Arc>>) -> Self { - Self { inner, base: BoxBase::new() } + Self { + inner, + base: BoxBase::new(), + } } pub fn borrow(&self) -> Box { self.inner.lock().unwrap().clone_box() @@ -22,31 +28,48 @@ impl RefCellBox { let mut guard = self.inner.lock().unwrap(); *guard = value; } - pub fn inner_arc(&self) -> Arc>> { Arc::clone(&self.inner) } + pub fn inner_arc(&self) -> Arc>> { + Arc::clone(&self.inner) + } } impl BoxCore for RefCellBox { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { self.base.parent_type_id } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + self.base.parent_type_id + } fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "RefCellBox(..)") } - 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 RefCellBox { - fn clone_box(&self) -> Box { Box::new(Self::with_inner(self.inner_arc())) } - fn share_box(&self) -> Box { self.clone_box() } + fn clone_box(&self) -> Box { + Box::new(Self::with_inner(self.inner_arc())) + } + fn share_box(&self) -> Box { + self.clone_box() + } fn to_string_box(&self) -> StringBox { let inner = self.inner.lock().unwrap(); StringBox::new(format!("RefCell({})", inner.to_string_box().value)) } - fn type_name(&self) -> &'static str { "RefCellBox" } + fn type_name(&self) -> &'static str { + "RefCellBox" + } fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(o) = other.as_any().downcast_ref::() { BoolBox::new(Arc::ptr_eq(&self.inner, &o.inner)) - } else { BoolBox::new(false) } + } else { + BoolBox::new(false) + } } } - diff --git a/src/boxes/regex/mod.rs b/src/boxes/regex/mod.rs index 009b17ab..3cd89d6b 100644 --- a/src/boxes/regex/mod.rs +++ b/src/boxes/regex/mod.rs @@ -2,12 +2,12 @@ // Nyashの箱システムによる正規表現処理を提供します。 // 参考: 既存Boxの設計思想 -use regex::Regex; -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use crate::boxes::array::ArrayBox; +use regex::Regex; use std::any::Any; -use std::sync::Arc; use std::fmt::Debug; +use std::sync::Arc; #[derive(Debug, Clone)] pub struct RegexBox { @@ -31,13 +31,13 @@ impl RegexBox { pub fn pattern(&self) -> &str { &self.pattern } - + /// パターンマッチテスト pub fn test(&self, text: Box) -> Box { let text_str = text.to_string_box().value; Box::new(BoolBox::new(self.is_match(&text_str))) } - + /// マッチ箇所を検索 pub fn find(&self, text: Box) -> Box { let text_str = text.to_string_box().value; @@ -47,36 +47,40 @@ impl RegexBox { Box::new(crate::boxes::null_box::NullBox::new()) } } - + /// すべてのマッチを検索 pub fn find_all(&self, text: Box) -> Box { let text_str = text.to_string_box().value; let array = ArrayBox::new(); - + for mat in self.regex.find_iter(&text_str) { let _ = array.push(Box::new(StringBox::new(mat.as_str()))); } - + Box::new(array) } - + /// 文字列置換 - pub fn replace(&self, text: Box, replacement: Box) -> Box { + pub fn replace( + &self, + text: Box, + replacement: Box, + ) -> Box { let text_str = text.to_string_box().value; let replacement_str = replacement.to_string_box().value; let result = self.regex.replace_all(&text_str, replacement_str.as_str()); Box::new(StringBox::new(&result.to_string())) } - + /// 文字列分割 pub fn split(&self, text: Box) -> Box { let text_str = text.to_string_box().value; let array = ArrayBox::new(); - + for part in self.regex.split(&text_str) { let _ = array.push(Box::new(StringBox::new(part))); } - + Box::new(array) } } @@ -85,7 +89,7 @@ impl NyashBox for RegexBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -95,12 +99,10 @@ impl NyashBox for RegexBox { StringBox::new(format!("RegexBox({})", self.pattern.as_str())) } - fn type_name(&self) -> &'static str { "RegexBox" } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_regex) = other.as_any().downcast_ref::() { BoolBox::new(self.pattern.as_str() == other_regex.pattern.as_str()) @@ -114,7 +116,7 @@ impl BoxCore for RegexBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } @@ -122,11 +124,11 @@ impl BoxCore for RegexBox { fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "RegexBox({})", self.pattern.as_str()) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } diff --git a/src/boxes/result/mod.rs b/src/boxes/result/mod.rs index 0cdf7712..68047b44 100644 --- a/src/boxes/result/mod.rs +++ b/src/boxes/result/mod.rs @@ -2,7 +2,7 @@ // Nyashの箱システムによるエラー処理を提供します。 // 参考: 既存Boxの設計思想 -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore}; +use crate::box_trait::{BoolBox, BoxCore, NyashBox, StringBox}; use std::any::Any; #[derive(Debug)] @@ -15,19 +15,19 @@ impl NyashResultBox { pub fn new_ok(value: Box) -> Self { NyashResultBox::Ok(value) } - + pub fn new_err(error: Box) -> Self { NyashResultBox::Err(error) } - + pub fn is_ok_bool(&self) -> bool { matches!(self, NyashResultBox::Ok(_)) } - + pub fn is_err(&self) -> bool { matches!(self, NyashResultBox::Err(_)) } - + pub fn unwrap(self) -> Box { match self { NyashResultBox::Ok(val) => val, @@ -43,7 +43,7 @@ impl NyashBox for NyashResultBox { NyashResultBox::Err(err) => Box::new(NyashResultBox::Err(err.clone_or_share())), } } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { match self { @@ -55,16 +55,16 @@ impl NyashBox for NyashResultBox { fn to_string_box(&self) -> StringBox { match self { NyashResultBox::Ok(val) => StringBox::new(format!("Ok({})", val.to_string_box().value)), - NyashResultBox::Err(err) => StringBox::new(format!("Err({})", err.to_string_box().value)), + NyashResultBox::Err(err) => { + StringBox::new(format!("Err({})", err.to_string_box().value)) + } } } - fn type_name(&self) -> &'static str { "NyashResultBox" } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_result) = other.as_any().downcast_ref::() { match (self, other_result) { @@ -86,7 +86,7 @@ impl BoxCore for NyashResultBox { NyashResultBox::Err(err) => err.box_id(), } } - + fn parent_type_id(&self) -> Option { // For enum variants, we use the contained value's parent type ID match self { @@ -101,11 +101,11 @@ impl BoxCore for NyashResultBox { NyashResultBox::Err(err) => write!(f, "Err({})", err.to_string_box().value), } } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -125,13 +125,17 @@ impl ResultBox { pub fn is_ok(&self) -> Box { Box::new(BoolBox::new(matches!(self, NyashResultBox::Ok(_)))) } - + /// getValue()の実装 - Ok値を取得 pub fn get_value(&self) -> Box { match self { NyashResultBox::Ok(val) => { // Preserve identity for plugin-backed boxes - if val.as_any().downcast_ref::().is_some() { + if val + .as_any() + .downcast_ref::() + .is_some() + { val.share_box() } else { val.clone_box() @@ -140,7 +144,7 @@ impl ResultBox { NyashResultBox::Err(_) => Box::new(StringBox::new("Error: Result is Err")), } } - + /// getError()の実装 - Err値を取得 pub fn get_error(&self) -> Box { match self { diff --git a/src/boxes/socket_box.rs b/src/boxes/socket_box.rs index 2c14dbf0..c464496c 100644 --- a/src/boxes/socket_box.rs +++ b/src/boxes/socket_box.rs @@ -1,23 +1,23 @@ /*! 🔌 SocketBox - TCP/UDP Socket networking - * + * * ## 📝 概要 * Rustの std::net を基盤とした高性能ネットワーキング Box * TCP サーバー・クライアント両対応、HTTPサーバー基盤として利用 - * + * * ## 🛠️ 利用可能メソッド * ### TCP Server * - `bind(address, port)` - TCP ソケット bind * - `listen(backlog)` - 接続待機開始 * - `accept()` - クライアント接続受諾 - * + * * ### TCP Client * - `connect(address, port)` - サーバーへ接続 - * + * * ### IO Operations * - `read()` - データ読み取り * - `write(data)` - データ送信 * - `close()` - ソケット閉鎖 - * + * * ## 💡 使用例 * ```nyash * // TCP Server @@ -25,7 +25,7 @@ * server.bind("0.0.0.0", 8080) * server.listen(128) * client = server.accept() - * + * * // TCP Client * client = new SocketBox() * client.connect("127.0.0.1", 8080) @@ -34,11 +34,11 @@ * ``` */ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use std::any::Any; +use std::io::{BufRead, BufReader, Write}; use std::net::{TcpListener, TcpStream}; -use std::io::{Write, BufRead, BufReader}; -use std::sync::{Arc, RwLock}; // Arc追加 +use std::sync::{Arc, RwLock}; // Arc追加 use std::time::Duration; /// TCP/UDP ソケット操作を提供するBox @@ -46,25 +46,25 @@ use std::time::Duration; pub struct SocketBox { base: BoxBase, // TCP Server - listener: Arc>>, // Arc追加 + listener: Arc>>, // Arc追加 // TCP Client/Connected Socket - stream: Arc>>, // Arc追加 + stream: Arc>>, // Arc追加 // Connection state - is_server: Arc>, // Arc追加 - is_connected: Arc>, // Arc追加 + is_server: Arc>, // Arc追加 + is_connected: Arc>, // Arc追加 } impl Clone for SocketBox { fn clone(&self) -> Self { - // ディープコピー(独立インスタンス) + // ディープコピー(独立インスタンス) let is_server_val = *self.is_server.read().unwrap(); let is_connected_val = *self.is_connected.read().unwrap(); - + Self { - base: BoxBase::new(), // New unique ID for clone - listener: Arc::new(RwLock::new(None)), // 新しいArc - stream: Arc::new(RwLock::new(None)), // 新しいArc - is_server: Arc::new(RwLock::new(is_server_val)), // 状態のみコピー + base: BoxBase::new(), // New unique ID for clone + listener: Arc::new(RwLock::new(None)), // 新しいArc + stream: Arc::new(RwLock::new(None)), // 新しいArc + is_server: Arc::new(RwLock::new(is_server_val)), // 状態のみコピー is_connected: Arc::new(RwLock::new(is_connected_val)), // 状態のみコピー } } @@ -74,41 +74,41 @@ impl SocketBox { pub fn new() -> Self { Self { base: BoxBase::new(), - listener: Arc::new(RwLock::new(None)), // Arc::new追加 - stream: Arc::new(RwLock::new(None)), // Arc::new追加 - is_server: Arc::new(RwLock::new(false)), // Arc::new追加 + listener: Arc::new(RwLock::new(None)), // Arc::new追加 + stream: Arc::new(RwLock::new(None)), // Arc::new追加 + is_server: Arc::new(RwLock::new(false)), // Arc::new追加 is_connected: Arc::new(RwLock::new(false)), // Arc::new追加 } } - + /// TCP ソケットをアドレス・ポートにバインド pub fn bind(&self, address: Box, port: Box) -> Box { let addr_str = address.to_string_box().value; let port_str = port.to_string_box().value; - + let socket_addr = format!("{}:{}", addr_str, port_str); - + eprintln!("🔥 SOCKETBOX DEBUG: bind() called"); eprintln!("🔥 Socket ID = {}", self.base.id); eprintln!("🔥 Address = {}", socket_addr); eprintln!("🔥 Arc pointer = {:p}", &self.is_server); - + match TcpListener::bind(&socket_addr) { Ok(listener) => { eprintln!("✅ TCP bind successful"); - + // listener設定 match self.listener.write() { Ok(mut listener_guard) => { *listener_guard = Some(listener); eprintln!("✅ Listener stored successfully"); - }, + } Err(e) => { eprintln!("❌ Failed to lock listener mutex: {}", e); return Box::new(BoolBox::new(false)); } } - + // is_server状態設定 - 徹底デバッグ match self.is_server.write() { Ok(mut is_server_guard) => { @@ -116,62 +116,62 @@ impl SocketBox { eprintln!("🔥 is_server value = {}", *is_server_guard); eprintln!("🔥 RwLock pointer = {:p}", &self.is_server); eprintln!("🔥 Guard pointer = {:p}", &*is_server_guard); - + // 状態変更 *is_server_guard = true; - + eprintln!("🔥 AFTER MUTATION:"); eprintln!("🔥 is_server value = {}", *is_server_guard); eprintln!("🔥 Value confirmed = {}", *is_server_guard == true); - + // 明示的にドロップしてロック解除 drop(is_server_guard); eprintln!("✅ is_server guard dropped"); - + // 再確認テスト match self.is_server.read() { Ok(check_guard) => { eprintln!("🔥 RECHECK AFTER DROP:"); eprintln!("🔥 is_server value = {}", *check_guard); - }, + } Err(e) => { eprintln!("❌ Failed to recheck: {}", e); } } - }, + } Err(e) => { eprintln!("❌ SOCKETBOX: Failed to lock is_server mutex: {}", e); return Box::new(BoolBox::new(false)); } } - + eprintln!("✅ bind() completed successfully"); Box::new(BoolBox::new(true)) - }, + } Err(e) => { eprintln!("❌ TCP bind failed: {}", e); Box::new(BoolBox::new(false)) } } } - + /// 指定した backlog で接続待機開始 pub fn listen(&self, backlog: Box) -> Box { let _backlog_num = backlog.to_string_box().value.parse::().unwrap_or(128); - + // Check if listener exists and is properly bound let listener_guard = match self.listener.read() { Ok(guard) => guard, Err(_) => return Box::new(BoolBox::new(false)), }; - + if let Some(ref listener) = *listener_guard { // Try to get the local address to confirm the listener is working match listener.local_addr() { Ok(_addr) => { // Listener is properly set up and can accept connections Box::new(BoolBox::new(true)) - }, + } Err(_) => { // Listener exists but has issues Box::new(BoolBox::new(false)) @@ -183,7 +183,7 @@ impl SocketBox { Box::new(BoolBox::new(false)) } } - + /// クライアント接続を受諾(ブロッキング) pub fn accept(&self) -> Box { let listener_guard = self.listener.write().unwrap(); @@ -191,14 +191,14 @@ impl SocketBox { match listener.accept() { Ok((stream, _addr)) => { drop(listener_guard); - + // Create new SocketBox for the client connection let client_socket = SocketBox::new(); *client_socket.stream.write().unwrap() = Some(stream); *client_socket.is_connected.write().unwrap() = true; - + Box::new(client_socket) - }, + } Err(e) => { eprintln!("🚨 SocketBox accept error: {}", e); Box::new(BoolBox::new(false)) @@ -212,7 +212,9 @@ impl SocketBox { /// クライアント接続を受諾(タイムアウトms、タイムアウト時はvoid) pub fn accept_timeout(&self, timeout_ms: Box) -> Box { let ms = timeout_ms.to_string_box().value.parse::().unwrap_or(0); - if ms == 0 { return self.accept(); } + if ms == 0 { + return self.accept(); + } let start = std::time::Instant::now(); if let Ok(guard) = self.listener.write() { @@ -227,7 +229,7 @@ impl SocketBox { *client_socket.stream.write().unwrap() = Some(stream); *client_socket.is_connected.write().unwrap() = true; return Box::new(client_socket); - }, + } Err(e) => { if e.kind() == std::io::ErrorKind::WouldBlock { if start.elapsed() >= Duration::from_millis(ms) { @@ -248,32 +250,36 @@ impl SocketBox { } Box::new(crate::box_trait::VoidBox::new()) } - + /// サーバーに接続(クライアントモード) - pub fn connect(&self, address: Box, port: Box) -> Box { + pub fn connect( + &self, + address: Box, + port: Box, + ) -> Box { let addr_str = address.to_string_box().value; let port_str = port.to_string_box().value; - + let socket_addr = format!("{}:{}", addr_str, port_str); - + match TcpStream::connect(&socket_addr) { Ok(stream) => { // Set timeout for read/write operations let _ = stream.set_read_timeout(Some(Duration::from_secs(30))); let _ = stream.set_write_timeout(Some(Duration::from_secs(30))); - + *self.stream.write().unwrap() = Some(stream); *self.is_connected.write().unwrap() = true; *self.is_server.write().unwrap() = false; Box::new(BoolBox::new(true)) - }, + } Err(e) => { eprintln!("🚨 SocketBox connect error: {}", e); Box::new(BoolBox::new(false)) } } } - + /// データを読み取り(改行まで or EOF) pub fn read(&self) -> Box { let stream_guard = self.stream.write().unwrap(); @@ -282,10 +288,10 @@ impl SocketBox { match stream.try_clone() { Ok(stream_clone) => { drop(stream_guard); - + let mut reader = BufReader::new(stream_clone); let mut buffer = String::new(); - + match reader.read_line(&mut buffer) { Ok(_) => { // Remove trailing newline @@ -296,13 +302,13 @@ impl SocketBox { } } Box::new(StringBox::new(buffer)) - }, + } Err(e) => { eprintln!("🚨 SocketBox read error: {}", e); Box::new(StringBox::new("".to_string())) } } - }, + } Err(e) => { eprintln!("🚨 SocketBox stream clone error: {}", e); Box::new(StringBox::new("".to_string())) @@ -328,12 +334,16 @@ impl SocketBox { Ok(_) => { if buffer.ends_with('\n') { buffer.pop(); - if buffer.ends_with('\r') { buffer.pop(); } + if buffer.ends_with('\r') { + buffer.pop(); + } } Box::new(StringBox::new(&buffer)) } Err(e) => { - if e.kind() == std::io::ErrorKind::WouldBlock || e.kind() == std::io::ErrorKind::TimedOut { + if e.kind() == std::io::ErrorKind::WouldBlock + || e.kind() == std::io::ErrorKind::TimedOut + { return Box::new(StringBox::new("")); } eprintln!("🚨 SocketBox recv_timeout error: {}", e); @@ -350,7 +360,7 @@ impl SocketBox { Box::new(StringBox::new("")) } } - + /// HTTP request を読み取り(ヘッダーまで含む) pub fn read_http_request(&self) -> Box { let stream_guard = self.stream.write().unwrap(); @@ -358,11 +368,11 @@ impl SocketBox { match stream.try_clone() { Ok(stream_clone) => { drop(stream_guard); - + let mut reader = BufReader::new(stream_clone); let mut request = String::new(); let mut line = String::new(); - + // Read HTTP request line by line until empty line loop { line.clear(); @@ -374,16 +384,16 @@ impl SocketBox { if line.trim().is_empty() { break; } - }, + } Err(e) => { eprintln!("🚨 SocketBox HTTP read error: {}", e); break; } } } - + Box::new(StringBox::new(request)) - }, + } Err(e) => { eprintln!("🚨 SocketBox stream clone error: {}", e); Box::new(StringBox::new("".to_string())) @@ -393,21 +403,19 @@ impl SocketBox { Box::new(StringBox::new("".to_string())) } } - + /// データを送信 pub fn write(&self, data: Box) -> Box { let data_str = data.to_string_box().value; - + let mut stream_guard = self.stream.write().unwrap(); if let Some(ref mut stream) = *stream_guard { match stream.write_all(data_str.as_bytes()) { - Ok(_) => { - match stream.flush() { - Ok(_) => Box::new(BoolBox::new(true)), - Err(e) => { - eprintln!("🚨 SocketBox flush error: {}", e); - Box::new(BoolBox::new(false)) - } + Ok(_) => match stream.flush() { + Ok(_) => Box::new(BoolBox::new(true)), + Err(e) => { + eprintln!("🚨 SocketBox flush error: {}", e); + Box::new(BoolBox::new(false)) } }, Err(e) => { @@ -419,7 +427,7 @@ impl SocketBox { Box::new(BoolBox::new(false)) } } - + /// ソケット閉鎖 pub fn close(&self) -> Box { *self.stream.write().unwrap() = None; @@ -428,18 +436,18 @@ impl SocketBox { *self.is_server.write().unwrap() = false; Box::new(BoolBox::new(true)) } - + /// 接続状態確認 pub fn is_connected(&self) -> Box { Box::new(BoolBox::new(*self.is_connected.write().unwrap())) } - + /// サーバーモード確認 pub fn is_server(&self) -> Box { eprintln!("🔥 SOCKETBOX DEBUG: is_server() called"); eprintln!("🔥 Socket ID = {}", self.base.id); eprintln!("🔥 RwLock pointer = {:p}", &self.is_server); - + match self.is_server.read() { Ok(is_server_guard) => { let is_server_value = *is_server_guard; @@ -447,11 +455,14 @@ impl SocketBox { eprintln!("🔥 is_server value = {}", is_server_value); eprintln!("🔥 Guard pointer = {:p}", &*is_server_guard); eprintln!("🔥 Returning BoolBox with value = {}", is_server_value); - + Box::new(BoolBox::new(is_server_value)) - }, + } Err(e) => { - eprintln!("❌ SOCKETBOX: Failed to lock is_server mutex in is_server(): {}", e); + eprintln!( + "❌ SOCKETBOX: Failed to lock is_server mutex in is_server(): {}", + e + ); Box::new(BoolBox::new(false)) } } @@ -462,45 +473,48 @@ impl NyashBox for SocketBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 🎯 状態共有の核心実装 - SocketBox状態保持問題の根本解決 fn share_box(&self) -> Box { let new_instance = SocketBox { - base: BoxBase::new(), // 新しいID - listener: Arc::clone(&self.listener), // 状態共有 - stream: Arc::clone(&self.stream), // 状態共有 - is_server: Arc::clone(&self.is_server), // 状態共有 - is_connected: Arc::clone(&self.is_connected), // 状態共有 + base: BoxBase::new(), // 新しいID + listener: Arc::clone(&self.listener), // 状態共有 + stream: Arc::clone(&self.stream), // 状態共有 + is_server: Arc::clone(&self.is_server), // 状態共有 + is_connected: Arc::clone(&self.is_connected), // 状態共有 }; Box::new(new_instance) } fn to_string_box(&self) -> StringBox { - eprintln!("🔥 SOCKETBOX to_string_box() called - Socket ID = {}", self.base.id); + eprintln!( + "🔥 SOCKETBOX to_string_box() called - Socket ID = {}", + self.base.id + ); eprintln!("🔥 RwLock pointer = {:p}", &self.is_server); - + let is_server = match self.is_server.read() { Ok(guard) => { eprintln!("✅ is_server.read() successful"); *guard - }, + } Err(e) => { eprintln!("❌ is_server.read() failed: {}", e); false // デフォルト値 } }; - + let is_connected = match self.is_connected.read() { Ok(guard) => { eprintln!("✅ is_connected.read() successful"); *guard - }, + } Err(e) => { eprintln!("❌ is_connected.read() failed: {}", e); false // デフォルト値 } }; - + let status = if is_server { "Server" } else if is_connected { @@ -508,8 +522,11 @@ impl NyashBox for SocketBox { } else { "Disconnected" }; - - StringBox::new(format!("SocketBox(id: {}, status: {})", self.base.id, status)) + + StringBox::new(format!( + "SocketBox(id: {}, status: {})", + self.base.id, status + )) } fn type_name(&self) -> &'static str { @@ -529,14 +546,17 @@ impl BoxCore for SocketBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - eprintln!("🔥 SOCKETBOX fmt_box() called - Socket ID = {}", self.base.id); - + eprintln!( + "🔥 SOCKETBOX fmt_box() called - Socket ID = {}", + self.base.id + ); + let is_server = match self.is_server.read() { Ok(guard) => *guard, Err(e) => { @@ -544,7 +564,7 @@ impl BoxCore for SocketBox { false } }; - + let is_connected = match self.is_connected.read() { Ok(guard) => *guard, Err(e) => { @@ -552,7 +572,7 @@ impl BoxCore for SocketBox { false } }; - + let status = if is_server { "Server" } else if is_connected { @@ -560,14 +580,14 @@ impl BoxCore for SocketBox { } else { "Disconnected" }; - + write!(f, "SocketBox(id: {}, status: {})", self.base.id, status) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } diff --git a/src/boxes/sound_box.rs b/src/boxes/sound_box.rs index db6d874a..27ed326e 100644 --- a/src/boxes/sound_box.rs +++ b/src/boxes/sound_box.rs @@ -1,10 +1,10 @@ /*! 🔊 SoundBox - サウンド・音響効果Box - * + * * ## 📝 概要 * システム音・効果音を提供するBox。 * ゲーム効果音、通知音、アラート音の生成に使用。 * クロスプラットフォーム対応のシンプルなサウンドシステム。 - * + * * ## 🛠️ 利用可能メソッド * - `beep()` - 基本ビープ音 * - `beeps(count)` - 指定回数ビープ @@ -13,22 +13,22 @@ * - `playTone(frequency, duration)` - 指定周波数・時間で音生成 * - `playFile(filename)` - 音声ファイル再生 * - `setVolume(level)` - 音量設定 (0.0-1.0) - * + * * ## 💡 使用例 * ```nyash * local sound * sound = new SoundBox() - * + * * // 基本的な音 * sound.beep() // シンプルビープ * sound.beeps(3) // 3回ビープ * sound.bell() // ベル音 - * + * * // ゲーム効果音 * sound.playTone(440, 500) // ラの音を500ms * sound.playTone(880, 200) // 高いラの音を200ms * ``` - * + * * ## 🎮 実用例 - ゲーム効果音 * ```nyash * static box GameSFX { @@ -65,7 +65,7 @@ * } * } * ``` - * + * * ## 🚨 通知・アラート用途 * ```nyash * static box NotificationSystem { @@ -100,7 +100,7 @@ * } * } * ``` - * + * * ## 🎵 音楽生成例 * ```nyash * static box MusicBox { @@ -129,7 +129,7 @@ * } * } * ``` - * + * * ## ⚠️ 注意 * - システムによってはビープ音が無効化されている場合あり * - 音量設定は環境依存 @@ -138,9 +138,9 @@ * - Web環境では制限が多い(ユーザー操作後のみ音声再生可能) */ -use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase}; -use std::fmt::{Debug, Display}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox}; use std::any::Any; +use std::fmt::{Debug, Display}; use std::process::Command; use std::time::Duration; @@ -152,69 +152,81 @@ pub struct SoundBox { impl SoundBox { pub fn new() -> Self { - Self { - base: BoxBase::new() + Self { + base: BoxBase::new(), } } - + /// ビープ音を鳴らす(基本) pub fn beep(&self) -> Box { // 端末ベル文字を出力 print!("\x07"); Box::new(StringBox::new("Beep!")) } - + /// 指定回数ビープ pub fn beeps(&self, count: Box) -> Box { if let Some(count_int) = count.as_any().downcast_ref::() { if count_int.value <= 0 { return Box::new(StringBox::new("Beep count must be positive")); } - + for i in 0..count_int.value { print!("\x07"); if i < count_int.value - 1 { std::thread::sleep(Duration::from_millis(100)); } } - + Box::new(StringBox::new(&format!("Beeped {} times", count_int.value))) } else { Box::new(StringBox::new("Error: beeps() requires integer input")) } } - + /// 指定周波数のビープ(Linuxのみ) - pub fn tone(&self, frequency: Box, duration: Box) -> Box { + pub fn tone( + &self, + frequency: Box, + duration: Box, + ) -> Box { if let (Some(freq_int), Some(dur_int)) = ( frequency.as_any().downcast_ref::(), - duration.as_any().downcast_ref::() + duration.as_any().downcast_ref::(), ) { if freq_int.value <= 0 || dur_int.value <= 0 { return Box::new(StringBox::new("Frequency and duration must be positive")); } - + // Linuxのbeepコマンドを試行 match Command::new("beep") .arg("-f") .arg(&freq_int.value.to_string()) .arg("-l") .arg(&dur_int.value.to_string()) - .output() + .output() { - Ok(_) => Box::new(StringBox::new(&format!("Played {}Hz for {}ms", freq_int.value, dur_int.value))), + Ok(_) => Box::new(StringBox::new(&format!( + "Played {}Hz for {}ms", + freq_int.value, dur_int.value + ))), Err(_) => { // beepコマンドが無い場合は端末ベルを使用 print!("\x07"); std::thread::sleep(Duration::from_millis(dur_int.value as u64)); - Box::new(StringBox::new(&format!("Fallback beep ({}Hz, {}ms)", freq_int.value, dur_int.value))) + Box::new(StringBox::new(&format!( + "Fallback beep ({}Hz, {}ms)", + freq_int.value, dur_int.value + ))) } } } else { - Box::new(StringBox::new("Error: tone() requires two integer inputs (frequency, duration)")) + Box::new(StringBox::new( + "Error: tone() requires two integer inputs (frequency, duration)", + )) } } - + /// 警告音 pub fn alert(&self) -> Box { // 3回短いビープ @@ -226,7 +238,7 @@ impl SoundBox { } Box::new(StringBox::new("Alert sound played")) } - + /// 成功音 pub fn success(&self) -> Box { // 1回長めのビープ @@ -235,7 +247,7 @@ impl SoundBox { print!("\x07"); Box::new(StringBox::new("Success sound played")) } - + /// エラー音 pub fn error(&self) -> Box { // 2回素早いビープ @@ -244,12 +256,12 @@ impl SoundBox { print!("\x07"); Box::new(StringBox::new("Error sound played")) } - + /// カスタムビープパターン pub fn pattern(&self, pattern: Box) -> Box { if let Some(pattern_str) = pattern.as_any().downcast_ref::() { let mut beep_count = 0; - + for ch in pattern_str.value.chars() { match ch { '.' => { @@ -272,43 +284,57 @@ impl SoundBox { // その他の文字は無視 } } - + // 文字間の短い間隔 std::thread::sleep(Duration::from_millis(50)); } - - Box::new(StringBox::new(&format!("Played pattern '{}' ({} beeps)", pattern_str.value, beep_count))) + + Box::new(StringBox::new(&format!( + "Played pattern '{}' ({} beeps)", + pattern_str.value, beep_count + ))) } else { Box::new(StringBox::new("Error: pattern() requires string input (use '.' for short, '-' for long, ' ' for pause)")) } } - + /// システム音量チェック(簡易) pub fn volumeTest(&self) -> Box { print!("\x07"); Box::new(StringBox::new("Volume test beep - can you hear it?")) } - + /// 指定間隔でビープ - pub fn interval(&self, times: Box, interval_ms: Box) -> Box { + pub fn interval( + &self, + times: Box, + interval_ms: Box, + ) -> Box { if let (Some(times_int), Some(interval_int)) = ( times.as_any().downcast_ref::(), - interval_ms.as_any().downcast_ref::() + interval_ms.as_any().downcast_ref::(), ) { if times_int.value <= 0 || interval_int.value < 0 { - return Box::new(StringBox::new("Times must be positive, interval must be non-negative")); + return Box::new(StringBox::new( + "Times must be positive, interval must be non-negative", + )); } - + for i in 0..times_int.value { print!("\x07"); if i < times_int.value - 1 { std::thread::sleep(Duration::from_millis(interval_int.value as u64)); } } - - Box::new(StringBox::new(&format!("Played {} beeps with {}ms intervals", times_int.value, interval_int.value))) + + Box::new(StringBox::new(&format!( + "Played {} beeps with {}ms intervals", + times_int.value, interval_int.value + ))) } else { - Box::new(StringBox::new("Error: interval() requires two integer inputs (times, interval_ms)")) + Box::new(StringBox::new( + "Error: interval() requires two integer inputs (times, interval_ms)", + )) } } } @@ -317,20 +343,20 @@ impl NyashBox for SoundBox { fn type_name(&self) -> &'static str { "SoundBox" } - + fn to_string_box(&self) -> StringBox { StringBox::new("SoundBox()") } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_sound) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_sound.base.id) @@ -338,27 +364,25 @@ impl NyashBox for SoundBox { BoolBox::new(false) } } - - } impl BoxCore for SoundBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "SoundBox()") } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -368,4 +392,4 @@ impl Display for SoundBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/stream/mod.rs b/src/boxes/stream/mod.rs index cd451405..8cb7894d 100644 --- a/src/boxes/stream/mod.rs +++ b/src/boxes/stream/mod.rs @@ -2,12 +2,12 @@ // Nyashの箱システムによるストリーミング処理を提供します。 // 参考: 既存Boxの設計思想 -use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, BoxCore, BoxBase}; -use crate::boxes::buffer::BufferBox; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox}; use crate::boxes::array::ArrayBox; +use crate::boxes::buffer::BufferBox; use std::any::Any; -use std::sync::RwLock; use std::io::Result; +use std::sync::RwLock; pub struct NyashStreamBox { buffer: RwLock>, @@ -23,7 +23,7 @@ impl NyashStreamBox { base: BoxBase::new(), } } - + pub fn from_data(data: Vec) -> Self { NyashStreamBox { buffer: RwLock::new(data), @@ -31,41 +31,41 @@ impl NyashStreamBox { base: BoxBase::new(), } } - + pub fn read(&self, buf: &mut [u8]) -> Result { let buffer = self.buffer.read().unwrap(); let mut position = self.position.write().unwrap(); - + let available = buffer.len().saturating_sub(*position); let to_read = buf.len().min(available); - + if to_read == 0 { return Ok(0); } - + buf[..to_read].copy_from_slice(&buffer[*position..*position + to_read]); *position += to_read; Ok(to_read) } - + pub fn write(&self, buf: &[u8]) -> Result<()> { let mut buffer = self.buffer.write().unwrap(); buffer.extend_from_slice(buf); Ok(()) } - + pub fn len(&self) -> usize { self.buffer.read().unwrap().len() } - + pub fn position(&self) -> usize { *self.position.read().unwrap() } - + pub fn reset(&self) { *self.position.write().unwrap() = 0; } - + /// ストリームに書き込み pub fn stream_write(&self, data: Box) -> Box { // BufferBoxから変換 @@ -96,38 +96,40 @@ impl NyashStreamBox { Err(e) => Box::new(StringBox::new(&format!("Error writing to stream: {}", e))), } } else { - Box::new(StringBox::new("Error: write() requires BufferBox or StringBox")) + Box::new(StringBox::new( + "Error: write() requires BufferBox or StringBox", + )) } } - + /// ストリームから読み込み pub fn stream_read(&self, count: Box) -> Box { if let Some(count_int) = count.as_any().downcast_ref::() { let count_val = count_int.value as usize; let mut buf = vec![0u8; count_val]; - + match self.read(&mut buf) { Ok(bytes_read) => { buf.truncate(bytes_read); Box::new(BufferBox::from_vec(buf)) - }, + } Err(e) => Box::new(StringBox::new(&format!("Error reading from stream: {}", e))), } } else { Box::new(StringBox::new("Error: read() requires integer count")) } } - + /// 現在位置を取得 pub fn get_position(&self) -> Box { Box::new(IntegerBox::new(self.position() as i64)) } - + /// バッファサイズを取得 pub fn get_length(&self) -> Box { Box::new(IntegerBox::new(self.len() as i64)) } - + /// ストリームをリセット pub fn stream_reset(&self) -> Box { self.reset(); @@ -139,7 +141,7 @@ impl NyashBox for NyashStreamBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -148,15 +150,17 @@ impl NyashBox for NyashStreamBox { fn to_string_box(&self) -> StringBox { let buffer = self.buffer.read().unwrap(); let position = self.position.read().unwrap(); - StringBox::new(format!("NyashStreamBox({} bytes, pos: {})", buffer.len(), *position)) + StringBox::new(format!( + "NyashStreamBox({} bytes, pos: {})", + buffer.len(), + *position + )) } - fn type_name(&self) -> &'static str { "NyashStreamBox" } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_stream) = other.as_any().downcast_ref::() { let self_buffer = self.buffer.read().unwrap(); @@ -174,7 +178,7 @@ impl BoxCore for NyashStreamBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } @@ -182,13 +186,18 @@ impl BoxCore for NyashStreamBox { fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let buffer = self.buffer.read().unwrap(); let position = self.position.read().unwrap(); - write!(f, "NyashStreamBox({} bytes, pos: {})", buffer.len(), *position) + write!( + f, + "NyashStreamBox({} bytes, pos: {})", + buffer.len(), + *position + ) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } diff --git a/src/boxes/string_box.rs b/src/boxes/string_box.rs index 4c64a840..4f4767b2 100644 --- a/src/boxes/string_box.rs +++ b/src/boxes/string_box.rs @@ -1,9 +1,9 @@ /*! 🔤 StringBox - 文字列操作Box - * + * * ## 📝 概要 * UTF-8エンコード文字列を扱うためのBox。 * JavaScript風のメソッドで直感的な文字列操作が可能。 - * + * * ## 🛠️ 利用可能メソッド * - `length()` - 文字列長を取得 * - `concat(other)` - 文字列結合 @@ -15,19 +15,19 @@ * - `indexOf(search)` - 文字列検索 * - `replace(from, to)` - 文字列置換 * - `charAt(index)` - 指定位置の文字取得 - * + * * ## 💡 使用例 * ```nyash * local text, parts, result * text = "Hello, World!" - * + * * print(text.length()) // 13 * print(text.toUpperCase()) // "HELLO, WORLD!" * parts = text.split(",") // ["Hello", " World!"] * result = text.concat(" Nyash") // "Hello, World! Nyash" * ``` */ -use crate::box_trait::{NyashBox, BoxCore, BoxBase}; +use crate::box_trait::{BoxBase, BoxCore, NyashBox}; use std::any::Any; use std::fmt::Display; @@ -45,18 +45,19 @@ impl StringBox { base: BoxBase::new(), } } - + pub fn empty() -> Self { Self::new("") } - + // ===== String Methods for Nyash ===== - + /// Split string by delimiter and return ArrayBox pub fn split(&self, delimiter: &str) -> Box { use crate::boxes::array::ArrayBox; let parts: Vec = self.value.split(delimiter).map(|s| s.to_string()).collect(); - let array_elements: Vec> = parts.into_iter() + let array_elements: Vec> = parts + .into_iter() .map(|s| Box::new(StringBox::new(s)) as Box) .collect(); let result = ArrayBox::new(); @@ -65,7 +66,7 @@ impl StringBox { } Box::new(result) } - + /// Find substring and return position (or -1 if not found) pub fn find(&self, search: &str) -> Box { use crate::boxes::integer_box::IntegerBox; @@ -74,50 +75,53 @@ impl StringBox { None => Box::new(IntegerBox::new(-1)), } } - + /// Replace all occurrences of old with new pub fn replace(&self, old: &str, new: &str) -> Box { Box::new(StringBox::new(self.value.replace(old, new))) } - + /// Trim whitespace from both ends pub fn trim(&self) -> Box { Box::new(StringBox::new(self.value.trim())) } - + /// Convert to uppercase pub fn to_upper(&self) -> Box { Box::new(StringBox::new(self.value.to_uppercase())) } - + /// Convert to lowercase pub fn to_lower(&self) -> Box { Box::new(StringBox::new(self.value.to_lowercase())) } - + /// Check if string contains substring pub fn contains(&self, search: &str) -> Box { use crate::boxes::bool_box::BoolBox; Box::new(BoolBox::new(self.value.contains(search))) } - + /// Check if string starts with prefix pub fn starts_with(&self, prefix: &str) -> Box { use crate::boxes::bool_box::BoolBox; Box::new(BoolBox::new(self.value.starts_with(prefix))) } - + /// Check if string ends with suffix pub fn ends_with(&self, suffix: &str) -> Box { use crate::boxes::bool_box::BoolBox; Box::new(BoolBox::new(self.value.ends_with(suffix))) } - + /// Join array elements using this string as delimiter pub fn join(&self, array_box: Box) -> Box { use crate::boxes::array::ArrayBox; if let Some(array) = array_box.as_any().downcast_ref::() { - let strings: Vec = array.items.read().unwrap() + let strings: Vec = array + .items + .read() + .unwrap() .iter() .map(|element| element.to_string_box().value) .collect(); @@ -127,7 +131,7 @@ impl StringBox { Box::new(StringBox::new(array_box.to_string_box().value)) } } - + /// Convert string to integer (parse as i64) pub fn to_integer(&self) -> Box { use crate::boxes::integer_box::IntegerBox; @@ -145,7 +149,7 @@ impl NyashBox for StringBox { fn to_string_box(&self) -> crate::box_trait::StringBox { crate::box_trait::StringBox::new(self.value.clone()) } - + fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox { use crate::box_trait::BoolBox; if let Some(other_string) = other.as_any().downcast_ref::() { @@ -154,16 +158,15 @@ impl NyashBox for StringBox { BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { "StringBox" } - - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -174,19 +177,19 @@ impl BoxCore for StringBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.value) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -196,4 +199,4 @@ impl Display for StringBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/task_group_box.rs b/src/boxes/task_group_box.rs index 6cae36ca..8166b94d 100644 --- a/src/boxes/task_group_box.rs +++ b/src/boxes/task_group_box.rs @@ -1,4 +1,4 @@ -use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox, VoidBox}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox}; use std::any::Any; use std::sync::{Arc, Mutex}; @@ -17,9 +17,17 @@ pub struct TaskGroupBox { impl TaskGroupBox { pub fn new() -> Self { - Self { base: BoxBase::new(), cancelled: false, inner: Arc::new(TaskGroupInner { strong: Mutex::new(Vec::new()) }) } + Self { + base: BoxBase::new(), + cancelled: false, + inner: Arc::new(TaskGroupInner { + strong: Mutex::new(Vec::new()), + }), + } + } + pub fn cancel_all(&mut self) { + self.cancelled = true; } - pub fn cancel_all(&mut self) { self.cancelled = true; } /// Cancel all child tasks (scaffold) and return void pub fn cancelAll(&mut self) -> Box { self.cancel_all(); @@ -31,7 +39,9 @@ impl TaskGroupBox { self.join_all_inner(ms); Box::new(VoidBox::new()) } - pub fn is_cancelled(&self) -> bool { self.cancelled } + pub fn is_cancelled(&self) -> bool { + self.cancelled + } /// Register a Future into this group's ownership pub fn add_future(&self, fut: &crate::boxes::future::FutureBox) { @@ -47,10 +57,16 @@ impl TaskGroupBox { let mut all_ready = true; if let Ok(mut list) = self.inner.strong.lock() { list.retain(|f| !f.ready()); - if !list.is_empty() { all_ready = false; } + if !list.is_empty() { + all_ready = false; + } + } + if all_ready { + break; + } + if Instant::now() >= deadline { + break; } - if all_ready { break; } - if Instant::now() >= deadline { break; } crate::runtime::global_hooks::safepoint_and_poll(); std::thread::yield_now(); } @@ -58,20 +74,38 @@ impl TaskGroupBox { } impl BoxCore for TaskGroupBox { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { None } + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + None + } fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "TaskGroup(cancelled={})", self.cancelled) } - 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 TaskGroupBox { - fn to_string_box(&self) -> StringBox { StringBox::new(format!("TaskGroup(cancelled={})", self.cancelled)) } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { - if let Some(g) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == g.base.id) } else { BoolBox::new(false) } + fn to_string_box(&self) -> StringBox { + StringBox::new(format!("TaskGroup(cancelled={})", self.cancelled)) + } + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + if let Some(g) = other.as_any().downcast_ref::() { + BoolBox::new(self.base.id == g.base.id) + } else { + BoolBox::new(false) + } + } + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + fn share_box(&self) -> Box { + self.clone_box() } - fn clone_box(&self) -> Box { Box::new(self.clone()) } - fn share_box(&self) -> Box { self.clone_box() } } diff --git a/src/boxes/time_box.rs b/src/boxes/time_box.rs index 4c161851..70df63fa 100644 --- a/src/boxes/time_box.rs +++ b/src/boxes/time_box.rs @@ -1,18 +1,18 @@ /*! ⏰ TimeBox - 時間・日付操作Box - * + * * ## 📝 概要 * 高精度な時間・日付操作を提供するBox。 * JavaScript Date、Python datetime、C# DateTimeと同等機能。 * タイムスタンプ処理、フォーマット、時差計算をサポート。 - * + * * ## 🛠️ 利用可能メソッド - * + * * ### 📅 基本操作 * - `now()` - 現在日時取得 * - `fromTimestamp(timestamp)` - UNIXタイムスタンプから日時作成 * - `parse(date_string)` - 文字列から日時パース * - `format(pattern)` - 指定フォーマットで文字列化 - * + * * ### 🔢 値取得 * - `year()` - 年取得 * - `month()` - 月取得 (1-12) @@ -21,34 +21,34 @@ * - `minute()` - 分取得 (0-59) * - `second()` - 秒取得 (0-59) * - `weekday()` - 曜日取得 (0=日曜) - * + * * ### ⏱️ 計算 * - `addDays(days)` - 日数加算 * - `addHours(hours)` - 時間加算 * - `addMinutes(minutes)` - 分加算 * - `diffDays(other)` - 日数差計算 * - `diffHours(other)` - 時間差計算 - * + * * ## 💡 使用例 * ```nyash * local time, now, birthday, age * time = new TimeBox() - * + * * // 現在日時 * now = time.now() * print("現在: " + now.format("yyyy/MM/dd HH:mm:ss")) - * + * * // 誕生日から年齢計算 * birthday = time.parse("1995-03-15") * age = now.diffYears(birthday) * print("年齢: " + age.toString() + "歳") - * + * * // 1週間後 * local next_week * next_week = now.addDays(7) * print("1週間後: " + next_week.format("MM月dd日")) * ``` - * + * * ## 🎮 実用例 - イベントスケジューラー * ```nyash * static box EventScheduler { @@ -83,34 +83,34 @@ * event = me.events.get(i) * hours_until = event.get("datetime").diffHours(me.current) * - * print(event.get("title") + " - " + + * print(event.get("title") + " - " + * hours_until.toString() + "時間後") * } * } * } * ``` - * + * * ## 🕐 時間計算例 * ```nyash * local time, start, end, duration * time = new TimeBox() - * + * * // 作業時間計測 * start = time.now() * // 何か重い処理... * heavyCalculation() * end = time.now() - * + * * duration = end.diffSeconds(start) * print("処理時間: " + duration.toString() + "秒") - * + * * // 締切まで残り時間 * local deadline, remaining * deadline = time.parse("2025-12-31 23:59:59") * remaining = deadline.diffDays(time.now()) * print("締切まで" + remaining.toString() + "日") * ``` - * + * * ## ⚠️ 注意 * - ローカルタイムゾーンに基づく処理 * - パース可能な日時フォーマットは限定的 @@ -118,11 +118,11 @@ * - 夏時間切り替え時は計算に注意 */ -use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase}; -use std::fmt::{Debug, Display}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox}; +use chrono::{DateTime, Datelike, Local, TimeZone, Timelike}; use std::any::Any; -use std::time::{SystemTime, Duration}; -use chrono::{DateTime, Local, TimeZone, Datelike, Timelike}; +use std::fmt::{Debug, Display}; +use std::time::{Duration, SystemTime}; /// 時間操作を提供するBox #[derive(Debug, Clone)] @@ -132,23 +132,27 @@ pub struct TimeBox { impl TimeBox { pub fn new() -> Self { - Self { base: BoxBase::new() } + Self { + base: BoxBase::new(), + } } - + /// 現在時刻を取得 pub fn now(&self) -> Box { Box::new(DateTimeBox::now()) } - + /// UNIXタイムスタンプから日時を作成 pub fn fromTimestamp(&self, timestamp: Box) -> Box { if let Some(int_box) = timestamp.as_any().downcast_ref::() { Box::new(DateTimeBox::from_timestamp(int_box.value)) } else { - Box::new(StringBox::new("Error: fromTimestamp() requires integer input")) + Box::new(StringBox::new( + "Error: fromTimestamp() requires integer input", + )) } } - + /// 日時文字列をパース pub fn parse(&self, date_str: Box) -> Box { if let Some(string_box) = date_str.as_any().downcast_ref::() { @@ -160,7 +164,7 @@ impl TimeBox { Box::new(StringBox::new("Error: parse() requires string input")) } } - + /// ミリ秒スリープ pub fn sleep(&self, millis: Box) -> Box { if let Some(int_box) = millis.as_any().downcast_ref::() { @@ -168,13 +172,15 @@ impl TimeBox { std::thread::sleep(Duration::from_millis(int_box.value as u64)); Box::new(StringBox::new("ok")) } else { - Box::new(StringBox::new("Error: sleep() requires positive milliseconds")) + Box::new(StringBox::new( + "Error: sleep() requires positive milliseconds", + )) } } else { Box::new(StringBox::new("Error: sleep() requires integer input")) } } - + /// 現在時刻をフォーマット pub fn format(&self, format_str: Box) -> Box { if let Some(str_box) = format_str.as_any().downcast_ref::() { @@ -182,7 +188,9 @@ impl TimeBox { let formatted = now.format(&str_box.value).to_string(); Box::new(StringBox::new(formatted)) } else { - Box::new(StringBox::new("Error: format() requires string format pattern")) + Box::new(StringBox::new( + "Error: format() requires string format pattern", + )) } } } @@ -191,20 +199,20 @@ impl NyashBox for TimeBox { fn type_name(&self) -> &'static str { "TimeBox" } - + fn to_string_box(&self) -> StringBox { StringBox::new("TimeBox()") } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_time) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_time.base.id) @@ -212,26 +220,25 @@ impl NyashBox for TimeBox { BoolBox::new(false) } } - } impl BoxCore for TimeBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "TimeBox()") } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -253,23 +260,26 @@ pub struct DateTimeBox { impl DateTimeBox { /// 現在時刻で作成 pub fn now() -> Self { - Self { + Self { datetime: Local::now(), base: BoxBase::new(), } } - + /// UNIXタイムスタンプから作成 pub fn from_timestamp(timestamp: i64) -> Self { let datetime = Local.timestamp_opt(timestamp, 0).unwrap(); - Self { datetime, base: BoxBase::new() } + Self { + datetime, + base: BoxBase::new(), + } } - + /// 文字列からパース pub fn parse(date_str: &str) -> Result { // ISO 8601形式でパース match DateTime::parse_from_rfc3339(date_str) { - Ok(dt) => Ok(Self { + Ok(dt) => Ok(Self { datetime: dt.with_timezone(&Local), base: BoxBase::new(), }), @@ -278,54 +288,57 @@ impl DateTimeBox { match chrono::NaiveDateTime::parse_from_str(date_str, "%Y-%m-%d %H:%M:%S") { Ok(naive_dt) => { let datetime = Local.from_local_datetime(&naive_dt).unwrap(); - Ok(Self { datetime, base: BoxBase::new() }) + Ok(Self { + datetime, + base: BoxBase::new(), + }) } Err(e) => Err(format!("Failed to parse date: {}", e)), } } } } - + /// 年を取得 pub fn year(&self) -> Box { Box::new(IntegerBox::new(self.datetime.year() as i64)) } - + /// 月を取得 pub fn month(&self) -> Box { Box::new(IntegerBox::new(self.datetime.month() as i64)) } - + /// 日を取得 pub fn day(&self) -> Box { Box::new(IntegerBox::new(self.datetime.day() as i64)) } - + /// 時を取得 pub fn hour(&self) -> Box { Box::new(IntegerBox::new(self.datetime.hour() as i64)) } - + /// 分を取得 pub fn minute(&self) -> Box { Box::new(IntegerBox::new(self.datetime.minute() as i64)) } - + /// 秒を取得 pub fn second(&self) -> Box { Box::new(IntegerBox::new(self.datetime.second() as i64)) } - + /// UNIXタイムスタンプを取得 pub fn timestamp(&self) -> Box { Box::new(IntegerBox::new(self.datetime.timestamp())) } - + /// ISO 8601形式でフォーマット pub fn toISOString(&self) -> Box { Box::new(StringBox::new(&self.datetime.to_rfc3339())) } - + /// カスタムフォーマット pub fn format(&self, fmt: Box) -> Box { if let Some(string_box) = fmt.as_any().downcast_ref::() { @@ -335,7 +348,7 @@ impl DateTimeBox { Box::new(StringBox::new("Error: format() requires string input")) } } - + /// 日付を加算 pub fn addDays(&self, days: Box) -> Box { if let Some(int_box) = days.as_any().downcast_ref::() { @@ -348,7 +361,7 @@ impl DateTimeBox { Box::new(StringBox::new("Error: addDays() requires integer input")) } } - + /// 時間を加算 pub fn addHours(&self, hours: Box) -> Box { if let Some(int_box) = hours.as_any().downcast_ref::() { @@ -367,20 +380,20 @@ impl NyashBox for DateTimeBox { fn type_name(&self) -> &'static str { "DateTimeBox" } - + fn to_string_box(&self) -> StringBox { StringBox::new(&self.datetime.format("%Y-%m-%d %H:%M:%S").to_string()) } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_dt) = other.as_any().downcast_ref::() { BoolBox::new(self.datetime == other_dt.datetime) @@ -388,26 +401,25 @@ impl NyashBox for DateTimeBox { BoolBox::new(false) } } - } impl BoxCore for DateTimeBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.datetime.format("%Y-%m-%d %H:%M:%S")) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -433,7 +445,7 @@ impl TimerBox { base: BoxBase::new(), } } - + /// 経過時間をミリ秒で取得 pub fn elapsed(&self) -> Box { match self.start_time.elapsed() { @@ -444,7 +456,7 @@ impl TimerBox { Err(_) => Box::new(IntegerBox::new(0)), } } - + /// タイマーをリセット pub fn reset(&mut self) -> Box { self.start_time = SystemTime::now(); @@ -456,20 +468,20 @@ impl NyashBox for TimerBox { fn type_name(&self) -> &'static str { "TimerBox" } - + fn to_string_box(&self) -> StringBox { StringBox::new("TimerBox()") } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_timer) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_timer.base.id) @@ -477,26 +489,25 @@ impl NyashBox for TimerBox { BoolBox::new(false) } } - } impl BoxCore for TimerBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "TimerBox()") } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -506,4 +517,4 @@ impl Display for TimerBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/timer_box.rs b/src/boxes/timer_box.rs index 5846c23d..46afe94c 100644 --- a/src/boxes/timer_box.rs +++ b/src/boxes/timer_box.rs @@ -1,41 +1,41 @@ /*! * TimerBox - JavaScript風タイマー機能Box - * + * * ## 📝 概要 * setTimeout/setInterval/requestAnimationFrameをNyashから利用可能にするBox。 * アニメーション、遅延実行、定期実行を統一的に管理。 - * + * * ## 🛠️ 利用可能メソッド - * + * * ### ⏱️ 基本タイマー * - `setTimeout(callback, delay)` - 指定時間後に1回実行 * - `setInterval(callback, interval)` - 指定間隔で繰り返し実行 * - `clearTimeout(id)` - タイマーをキャンセル * - `clearInterval(id)` - インターバルをキャンセル - * + * * ### 🎮 アニメーション * - `requestAnimationFrame(callback)` - 次フレームで実行 * - `cancelAnimationFrame(id)` - アニメーションをキャンセル - * + * * ### 📊 時間測定 * - `now()` - 現在時刻(ミリ秒) * - `performance()` - 高精度時刻測定 - * + * * ## 💡 使用例 * ```nyash * local timer, id * timer = new TimerBox() - * + * * // 1秒後に実行 * id = timer.setTimeout(function() { * print("Hello after 1 second!") * }, 1000) - * + * * // 500msごとに実行 * id = timer.setInterval(function() { * print("Tick every 500ms") * }, 500) - * + * * // アニメーションループ * timer.requestAnimationFrame(function() { * // 描画処理 @@ -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; #[cfg(target_arch = "wasm32")] @@ -81,7 +81,7 @@ impl TimerBox { js_sys::Date::now() } } - + #[cfg(not(target_arch = "wasm32"))] { use std::time::{SystemTime, UNIX_EPOCH}; @@ -101,7 +101,8 @@ impl TimerBox { /// setTimeout相当の遅延実行 pub fn set_timeout(&self, callback: &js_sys::Function, delay: i32) -> i32 { if let Some(window) = window() { - window.set_timeout_with_callback_and_timeout_and_arguments_0(callback, delay) + window + .set_timeout_with_callback_and_timeout_and_arguments_0(callback, delay) .unwrap_or(-1) } else { -1 @@ -112,7 +113,8 @@ impl TimerBox { /// setInterval相当の定期実行 pub fn set_interval(&self, callback: &js_sys::Function, interval: i32) -> i32 { if let Some(window) = window() { - window.set_interval_with_callback_and_timeout_and_arguments_0(callback, interval) + window + .set_interval_with_callback_and_timeout_and_arguments_0(callback, interval) .unwrap_or(-1) } else { -1 @@ -192,19 +194,19 @@ impl BoxCore for TimerBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "TimerBox(id={})", self.base.id) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -214,7 +216,7 @@ impl NyashBox for TimerBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -227,7 +229,7 @@ impl NyashBox for TimerBox { fn type_name(&self) -> &'static str { "TimerBox" } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_timer) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_timer.base.id) @@ -241,4 +243,4 @@ impl std::fmt::Display for TimerBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/token_box.rs b/src/boxes/token_box.rs index 7b8394a3..2aa2f0b2 100644 --- a/src/boxes/token_box.rs +++ b/src/boxes/token_box.rs @@ -1,4 +1,4 @@ -use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox, BoolBox}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use std::any::Any; /// Cancellation token as a Box for structured concurrency @@ -9,30 +9,69 @@ pub struct TokenBox { } impl TokenBox { - pub fn new() -> Self { Self { base: BoxBase::new(), token: crate::runtime::scheduler::CancellationToken::new() } } - pub fn from_token(token: crate::runtime::scheduler::CancellationToken) -> Self { Self { base: BoxBase::new(), token } } - pub fn cancel(&self) { self.token.cancel(); } - pub fn is_cancelled(&self) -> bool { self.token.is_cancelled() } - pub fn inner(&self) -> crate::runtime::scheduler::CancellationToken { self.token.clone() } + pub fn new() -> Self { + Self { + base: BoxBase::new(), + token: crate::runtime::scheduler::CancellationToken::new(), + } + } + pub fn from_token(token: crate::runtime::scheduler::CancellationToken) -> Self { + Self { + base: BoxBase::new(), + token, + } + } + pub fn cancel(&self) { + self.token.cancel(); + } + pub fn is_cancelled(&self) -> bool { + self.token.is_cancelled() + } + pub fn inner(&self) -> crate::runtime::scheduler::CancellationToken { + self.token.clone() + } } impl BoxCore for TokenBox { - fn box_id(&self) -> u64 { self.base.id } - fn parent_type_id(&self) -> Option { None } - fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "CancellationToken(cancelled={})", self.token.is_cancelled()) + fn box_id(&self) -> u64 { + self.base.id + } + fn parent_type_id(&self) -> Option { + None + } + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "CancellationToken(cancelled={})", + self.token.is_cancelled() + ) + } + 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 TokenBox { - fn to_string_box(&self) -> StringBox { StringBox::new(format!("CancellationToken(cancelled={})", self.token.is_cancelled())) } + fn to_string_box(&self) -> StringBox { + StringBox::new(format!( + "CancellationToken(cancelled={})", + self.token.is_cancelled() + )) + } fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(o) = other.as_any().downcast_ref::() { BoolBox::new(self.is_cancelled() == o.is_cancelled()) - } else { BoolBox::new(false) } + } else { + BoolBox::new(false) + } + } + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + fn share_box(&self) -> Box { + Box::new(self.clone()) } - fn clone_box(&self) -> Box { Box::new(self.clone()) } - fn share_box(&self) -> Box { Box::new(self.clone()) } } diff --git a/src/boxes/web/mod.rs b/src/boxes/web/mod.rs index 6d405aa3..ef502b21 100644 --- a/src/boxes/web/mod.rs +++ b/src/boxes/web/mod.rs @@ -1,6 +1,6 @@ /*! * Web Boxes Module - ブラウザ専用Box群 - * + * * WebAssembly環境専用のBox群を管理 * HTML5 APIs、DOM操作、Canvas描画等をNyashから利用可能にする */ @@ -21,4 +21,4 @@ pub use web_display_box::WebDisplayBox; pub use web_console_box::WebConsoleBox; #[cfg(target_arch = "wasm32")] -pub use web_canvas_box::WebCanvasBox; \ No newline at end of file +pub use web_canvas_box::WebCanvasBox; diff --git a/src/boxes/web/web_canvas_box.rs b/src/boxes/web/web_canvas_box.rs index 6e217326..8fe303b8 100644 --- a/src/boxes/web/web_canvas_box.rs +++ b/src/boxes/web/web_canvas_box.rs @@ -1,21 +1,18 @@ /*! * WebCanvasBox - ブラウザCanvas完全制御Box - * + * * WebAssembly環境でHTML5 Canvasの完全制御 * ピクセルの世界を制圧する革命的Box! */ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use std::any::Any; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; #[cfg(target_arch = "wasm32")] -use web_sys::{ - HtmlCanvasElement, - CanvasRenderingContext2d, -}; +use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement}; // 🎨 Browser Canvas complete control Box #[cfg(target_arch = "wasm32")] @@ -30,22 +27,22 @@ pub struct WebCanvasBox { #[cfg(target_arch = "wasm32")] impl WebCanvasBox { pub fn new(canvas_id: String, width: u32, height: u32) -> Self { - let instance = Self { + let instance = Self { base: BoxBase::new(), canvas_id: canvas_id.clone(), width, height, }; - + // キャンバス要素を初期化 if let Some(canvas) = instance.get_canvas_element() { canvas.set_width(width); canvas.set_height(height); } - + instance } - + /// Canvas要素を取得 fn get_canvas_element(&self) -> Option { let window = web_sys::window()?; @@ -53,7 +50,7 @@ impl WebCanvasBox { let element = document.get_element_by_id(&self.canvas_id)?; element.dyn_into::().ok() } - + /// 2Dレンダリングコンテキストを取得 fn get_2d_context(&self) -> Option { let canvas = self.get_canvas_element()?; @@ -62,35 +59,35 @@ impl WebCanvasBox { .ok()? .and_then(|ctx| ctx.dyn_into::().ok()) } - + /// キャンバスをクリア pub fn clear(&self) { if let Some(ctx) = self.get_2d_context() { ctx.clear_rect(0.0, 0.0, self.width as f64, self.height as f64); } } - + /// 塗りつぶし色を設定 pub fn set_fill_style(&self, color: &str) { if let Some(ctx) = self.get_2d_context() { ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(color)); } } - + /// 線の色を設定 pub fn set_stroke_style(&self, color: &str) { if let Some(ctx) = self.get_2d_context() { ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color)); } } - + /// 線の太さを設定 pub fn set_line_width(&self, width: f64) { if let Some(ctx) = self.get_2d_context() { ctx.set_line_width(width); } } - + /// 塗りつぶし矩形を描画 pub fn fill_rect(&self, x: f64, y: f64, width: f64, height: f64, color: &str) { if let Some(ctx) = self.get_2d_context() { @@ -98,37 +95,47 @@ impl WebCanvasBox { ctx.fill_rect(x, y, width, height); } } - + /// 枠線矩形を描画 - pub fn stroke_rect(&self, x: f64, y: f64, width: f64, height: f64, color: &str, line_width: f64) { + pub fn stroke_rect( + &self, + x: f64, + y: f64, + width: f64, + height: f64, + color: &str, + line_width: f64, + ) { if let Some(ctx) = self.get_2d_context() { ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color)); ctx.set_line_width(line_width); ctx.stroke_rect(x, y, width, height); } } - + /// 塗りつぶし円を描画 pub fn fill_circle(&self, x: f64, y: f64, radius: f64, color: &str) { if let Some(ctx) = self.get_2d_context() { ctx.set_fill_style(&wasm_bindgen::JsValue::from_str(color)); ctx.begin_path(); - ctx.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI).unwrap_or_default(); + ctx.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI) + .unwrap_or_default(); ctx.fill(); } } - + /// 枠線円を描画 pub fn stroke_circle(&self, x: f64, y: f64, radius: f64, color: &str, line_width: f64) { if let Some(ctx) = self.get_2d_context() { ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color)); ctx.set_line_width(line_width); ctx.begin_path(); - ctx.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI).unwrap_or_default(); + ctx.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI) + .unwrap_or_default(); ctx.stroke(); } } - + /// 直線を描画 pub fn draw_line(&self, x1: f64, y1: f64, x2: f64, y2: f64, color: &str, line_width: f64) { if let Some(ctx) = self.get_2d_context() { @@ -140,7 +147,7 @@ impl WebCanvasBox { ctx.stroke(); } } - + /// テキストを描画(塗りつぶし) pub fn fill_text(&self, text: &str, x: f64, y: f64, font: &str, color: &str) { if let Some(ctx) = self.get_2d_context() { @@ -149,9 +156,17 @@ impl WebCanvasBox { ctx.fill_text(text, x, y).unwrap_or_default(); } } - + /// テキストを描画(枠線) - pub fn stroke_text(&self, text: &str, x: f64, y: f64, font: &str, color: &str, line_width: f64) { + pub fn stroke_text( + &self, + text: &str, + x: f64, + y: f64, + font: &str, + color: &str, + line_width: f64, + ) { if let Some(ctx) = self.get_2d_context() { ctx.set_font(font); ctx.set_stroke_style(&wasm_bindgen::JsValue::from_str(color)); @@ -159,35 +174,35 @@ impl WebCanvasBox { ctx.stroke_text(text, x, y).unwrap_or_default(); } } - + /// パス描画開始 pub fn begin_path(&self) { if let Some(ctx) = self.get_2d_context() { ctx.begin_path(); } } - + /// パスを指定位置に移動 pub fn move_to(&self, x: f64, y: f64) { if let Some(ctx) = self.get_2d_context() { ctx.move_to(x, y); } } - + /// パスに直線を追加 pub fn line_to(&self, x: f64, y: f64) { if let Some(ctx) = self.get_2d_context() { ctx.line_to(x, y); } } - + /// パスを閉じる pub fn close_path(&self) { if let Some(ctx) = self.get_2d_context() { ctx.close_path(); } } - + /// パスを塗りつぶし pub fn fill(&self, color: &str) { if let Some(ctx) = self.get_2d_context() { @@ -195,7 +210,7 @@ impl WebCanvasBox { ctx.fill(); } } - + /// パスを枠線描画 pub fn stroke(&self, color: &str, line_width: f64) { if let Some(ctx) = self.get_2d_context() { @@ -204,56 +219,56 @@ impl WebCanvasBox { ctx.stroke(); } } - + /// 現在の描画状態を保存 pub fn save(&self) { if let Some(ctx) = self.get_2d_context() { ctx.save(); } } - + /// 描画状態を復元 pub fn restore(&self) { if let Some(ctx) = self.get_2d_context() { ctx.restore(); } } - + /// 座標系を回転 pub fn rotate(&self, angle: f64) { if let Some(ctx) = self.get_2d_context() { ctx.rotate(angle).unwrap_or_default(); } } - + /// 座標系をスケール pub fn scale(&self, x: f64, y: f64) { if let Some(ctx) = self.get_2d_context() { ctx.scale(x, y).unwrap_or_default(); } } - + /// 座標系を平行移動 pub fn translate(&self, x: f64, y: f64) { if let Some(ctx) = self.get_2d_context() { ctx.translate(x, y).unwrap_or_default(); } } - + /// キャンバスのサイズを取得 pub fn get_width(&self) -> u32 { self.width } - + pub fn get_height(&self) -> u32 { self.height } - + /// キャンバスのサイズを変更 pub fn resize(&mut self, width: u32, height: u32) { self.width = width; self.height = height; - + if let Some(canvas) = self.get_canvas_element() { canvas.set_width(width); canvas.set_height(height); @@ -266,19 +281,23 @@ impl BoxCore for WebCanvasBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "WebCanvasBox({}, {}x{})", self.canvas_id, self.width, self.height) + write!( + f, + "WebCanvasBox({}, {}x{})", + self.canvas_id, self.width, self.height + ) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -289,7 +308,7 @@ impl NyashBox for WebCanvasBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -297,18 +316,14 @@ impl NyashBox for WebCanvasBox { fn to_string_box(&self) -> StringBox { StringBox::new(format!( - "WebCanvasBox({}, {}x{})", - self.canvas_id, - self.width, - self.height + "WebCanvasBox({}, {}x{})", + self.canvas_id, self.width, self.height )) } - fn type_name(&self) -> &'static str { "WebCanvasBox" } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_canvas) = other.as_any().downcast_ref::() { @@ -324,4 +339,4 @@ impl std::fmt::Display for WebCanvasBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/web/web_console_box.rs b/src/boxes/web/web_console_box.rs index ad5393ec..4e824275 100644 --- a/src/boxes/web/web_console_box.rs +++ b/src/boxes/web/web_console_box.rs @@ -1,11 +1,11 @@ /*! * WebConsoleBox - ブラウザHTML要素コンソール出力Box - * + * * WebAssembly環境でHTML要素へのコンソール風出力 * F12コンソールの代わりに指定要素に出力 */ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use std::any::Any; #[cfg(target_arch = "wasm32")] @@ -25,85 +25,91 @@ pub struct WebConsoleBox { #[cfg(target_arch = "wasm32")] impl WebConsoleBox { pub fn new(element_id: String) -> Self { - Self { + Self { base: BoxBase::new(), target_element_id: element_id, } } - + /// 指定した要素IDのHTML要素を取得 fn get_target_element(&self) -> Option { let window = web_sys::window()?; let document = window.document()?; document.get_element_by_id(&self.target_element_id) } - + /// コンソール出力を追加(改行付き) fn append_console_line(&self, message: &str, level: &str) { if let Some(element) = self.get_target_element() { - let timestamp = js_sys::Date::new_0().to_iso_string().as_string().unwrap_or_default(); - let time_part = timestamp.split('T').nth(1).unwrap_or("00:00:00").split('.').nth(0).unwrap_or("00:00:00"); - + let timestamp = js_sys::Date::new_0() + .to_iso_string() + .as_string() + .unwrap_or_default(); + let time_part = timestamp + .split('T') + .nth(1) + .unwrap_or("00:00:00") + .split('.') + .nth(0) + .unwrap_or("00:00:00"); + let (level_prefix, color) = match level { "log" => ("📝", "white"), "warn" => ("⚠️", "yellow"), - "error" => ("❌", "red"), + "error" => ("❌", "red"), "info" => ("ℹ️", "cyan"), "debug" => ("🔍", "gray"), _ => ("📝", "white"), }; - + let formatted_line = format!( - "[{}] {} {}
", - color, - time_part, - level_prefix, - message + "[{}] {} {}
", + color, time_part, level_prefix, message ); - + let current_content = element.inner_html(); let new_content = format!("{}{}", current_content, formatted_line); element.set_inner_html(&new_content); - + // 自動スクロール if let Some(html_element) = element.dyn_ref::() { html_element.set_scroll_top(html_element.scroll_height()); } } } - + /// ログメッセージを出力 pub fn log(&self, message: &str) { self.append_console_line(message, "log"); } - + /// 警告メッセージを出力 pub fn warn(&self, message: &str) { self.append_console_line(message, "warn"); } - + /// エラーメッセージを出力 pub fn error(&self, message: &str) { self.append_console_line(message, "error"); } - + /// 情報メッセージを出力 pub fn info(&self, message: &str) { self.append_console_line(message, "info"); } - + /// デバッグメッセージを出力 pub fn debug(&self, message: &str) { self.append_console_line(message, "debug"); } - + /// コンソールをクリア pub fn clear(&self) { if let Some(element) = self.get_target_element() { element.set_inner_html(""); } } - + /// 区切り線を追加 pub fn separator(&self) { if let Some(element) = self.get_target_element() { @@ -113,7 +119,7 @@ impl WebConsoleBox { element.set_inner_html(&new_content); } } - + /// グループ開始(見出し付き) pub fn group(&self, title: &str) { if let Some(element) = self.get_target_element() { @@ -126,7 +132,7 @@ impl WebConsoleBox { element.set_inner_html(&new_content); } } - + /// グループ終了 pub fn group_end(&self) { if let Some(element) = self.get_target_element() { @@ -143,19 +149,19 @@ impl BoxCore for WebConsoleBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "WebConsoleBox({})", self.target_element_id) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -166,7 +172,7 @@ impl NyashBox for WebConsoleBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -176,11 +182,9 @@ impl NyashBox for WebConsoleBox { StringBox::new(format!("WebConsoleBox({})", self.target_element_id)) } - fn type_name(&self) -> &'static str { "WebConsoleBox" } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_console) = other.as_any().downcast_ref::() { @@ -196,4 +200,4 @@ impl std::fmt::Display for WebConsoleBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/boxes/web/web_display_box.rs b/src/boxes/web/web_display_box.rs index c7476ca2..d545ada8 100644 --- a/src/boxes/web/web_display_box.rs +++ b/src/boxes/web/web_display_box.rs @@ -1,11 +1,11 @@ /*! * WebDisplayBox - ブラウザHTML要素表示制御Box - * + * * WebAssembly環境でHTML要素への直接出力・スタイル制御 * プレイグラウンドの出力パネル等を完全制御 */ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use std::any::Any; #[cfg(target_arch = "wasm32")] @@ -25,19 +25,19 @@ pub struct WebDisplayBox { #[cfg(target_arch = "wasm32")] impl WebDisplayBox { pub fn new(element_id: String) -> Self { - Self { + Self { base: BoxBase::new(), target_element_id: element_id, } } - + /// 指定した要素IDのHTML要素を取得 fn get_target_element(&self) -> Option { let window = web_sys::window()?; let document = window.document()?; document.get_element_by_id(&self.target_element_id) } - + /// テキストを追加出力 pub fn print(&self, message: &str) { if let Some(element) = self.get_target_element() { @@ -50,7 +50,7 @@ impl WebDisplayBox { element.set_inner_html(&new_content); } } - + /// テキストを改行付きで追加出力 pub fn println(&self, message: &str) { if let Some(element) = self.get_target_element() { @@ -63,14 +63,14 @@ impl WebDisplayBox { element.set_inner_html(&new_content); } } - + /// HTMLコンテンツを完全置換 pub fn set_html(&self, html_content: &str) { if let Some(element) = self.get_target_element() { element.set_inner_html(html_content); } } - + /// HTMLコンテンツを追加 pub fn append_html(&self, html_content: &str) { if let Some(element) = self.get_target_element() { @@ -79,7 +79,7 @@ impl WebDisplayBox { element.set_inner_html(&new_content); } } - + /// CSSスタイルを設定 pub fn set_css(&self, property: &str, value: &str) { if let Some(element) = self.get_target_element() { @@ -89,38 +89,38 @@ impl WebDisplayBox { } } } - + /// CSSクラスを追加 pub fn add_class(&self, class_name: &str) { if let Some(element) = self.get_target_element() { let _ = element.class_list().add_1(class_name); } } - + /// CSSクラスを削除 pub fn remove_class(&self, class_name: &str) { if let Some(element) = self.get_target_element() { let _ = element.class_list().remove_1(class_name); } } - + /// 内容をクリア pub fn clear(&self) { if let Some(element) = self.get_target_element() { element.set_inner_html(""); } } - + /// 要素を表示 pub fn show(&self) { self.set_css("display", "block"); } - + /// 要素を非表示 pub fn hide(&self) { self.set_css("display", "none"); } - + /// スクロールを最下部に移動 pub fn scroll_to_bottom(&self) { if let Some(element) = self.get_target_element() { @@ -136,19 +136,19 @@ impl BoxCore for WebDisplayBox { fn box_id(&self) -> u64 { self.base.id } - + fn parent_type_id(&self) -> Option { self.base.parent_type_id } - + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "WebDisplayBox({})", self.target_element_id) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -159,7 +159,7 @@ impl NyashBox for WebDisplayBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -169,11 +169,9 @@ impl NyashBox for WebDisplayBox { StringBox::new(format!("WebDisplayBox({})", self.target_element_id)) } - fn type_name(&self) -> &'static str { "WebDisplayBox" } - fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_display) = other.as_any().downcast_ref::() { @@ -189,4 +187,4 @@ impl std::fmt::Display for WebDisplayBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/channel_box.rs b/src/channel_box.rs index d470a6a4..fd36ab57 100644 --- a/src/channel_box.rs +++ b/src/channel_box.rs @@ -1,31 +1,32 @@ /*! * Nyash P2P Channel System - Arrow構文によるBox間通信 - * + * * alice >> bob でメッセージ送信を可能にする * Everything is Box哲学に基づくP2P通信システム */ -use crate::box_trait::{NyashBox, StringBox, VoidBox, BoxCore, BoxBase}; -use std::collections::HashMap; -use std::sync::{Arc, Mutex, Weak}; -use std::fmt::{Debug, Display}; +use crate::box_trait::{BoxBase, BoxCore, NyashBox, StringBox, VoidBox}; use std::any::Any; +use std::collections::HashMap; +use std::fmt::{Debug, Display}; +use std::sync::{Arc, Mutex, Weak}; /// チャンネル - Box間の通信路 #[derive(Clone)] pub struct ChannelBox { /// 送信者の名前 pub sender_name: String, - + /// 受信者の名前 pub receiver_name: String, - + /// リンクされたBox(弱参照) linked_boxes: Arc>>>>, - + /// メッセージハンドラー - handlers: Arc) -> Box + Send>>>>, - + handlers: + Arc) -> Box + Send>>>>, + /// Box基底 base: BoxBase, } @@ -41,69 +42,76 @@ impl ChannelBox { base: BoxBase::new(), } } - + /// Boxをリンク pub fn link(&self, name: &str, target: Arc>) { - self.linked_boxes.lock().unwrap().insert( - name.to_string(), - Arc::downgrade(&target) - ); + self.linked_boxes + .lock() + .unwrap() + .insert(name.to_string(), Arc::downgrade(&target)); } - + /// メッセージハンドラーを登録 pub fn register_handler(&self, method: &str, handler: F) where - F: Fn(Box) -> Box + Send + 'static + F: Fn(Box) -> Box + Send + 'static, { - self.handlers.lock().unwrap().insert( - method.to_string(), - Box::new(handler) - ); + self.handlers + .lock() + .unwrap() + .insert(method.to_string(), Box::new(handler)); } - + /// メソッド呼び出しを実行 pub fn invoke(&self, method: &str, args: Vec>) -> Box { // "*" はブロードキャスト if self.receiver_name == "*" { return self.broadcast(method, args); } - + // 通常の送信 let handlers = self.handlers.lock().unwrap(); if let Some(handler) = handlers.get(method) { // 簡易実装:最初の引数のみ使用 - let arg = args.get(0) + let arg = args + .get(0) .map(|a| a.clone_box()) .unwrap_or_else(|| Box::new(VoidBox::new())); handler(arg) } else { - Box::new(StringBox::new(&format!("No handler for method: {}", method))) + Box::new(StringBox::new(&format!( + "No handler for method: {}", + method + ))) } } - + /// 送信者名を取得 pub fn sender(&self) -> Box { Box::new(StringBox::new(&self.sender_name)) } - + /// 受信者名を取得 pub fn receiver(&self) -> Box { Box::new(StringBox::new(&self.receiver_name)) } - + /// ブロードキャスト fn broadcast(&self, _method: &str, _args: Vec>) -> Box { let linked = self.linked_boxes.lock().unwrap(); let mut results = Vec::new(); - + for (name, weak_box) in linked.iter() { if let Some(_strong_box) = weak_box.upgrade() { // 各Boxにメッセージを送信 results.push(format!("Sent to {}", name)); } } - - Box::new(StringBox::new(&format!("Broadcast complete: {:?}", results))) + + Box::new(StringBox::new(&format!( + "Broadcast complete: {:?}", + results + ))) } } @@ -111,25 +119,28 @@ impl NyashBox for ChannelBox { fn type_name(&self) -> &'static str { "ChannelBox" } - + fn to_string_box(&self) -> StringBox { - StringBox::new(&format!("Channel({} >> {})", self.sender_name, self.receiver_name)) + StringBox::new(&format!( + "Channel({} >> {})", + self.sender_name, self.receiver_name + )) } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - + fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox { if let Some(other_channel) = other.as_any().downcast_ref::() { crate::box_trait::BoolBox::new( - self.sender_name == other_channel.sender_name && - self.receiver_name == other_channel.receiver_name + self.sender_name == other_channel.sender_name + && self.receiver_name == other_channel.receiver_name, ) } else { crate::box_trait::BoolBox::new(false) @@ -149,11 +160,11 @@ impl BoxCore for ChannelBox { fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Channel({} >> {})", self.sender_name, self.receiver_name) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -197,25 +208,27 @@ impl NyashBox for MessageBox { fn type_name(&self) -> &'static str { "MessageBox" } - + fn to_string_box(&self) -> StringBox { - StringBox::new(&format!("[{}] {}: {}", self.base.id, self.sender, self.content)) + StringBox::new(&format!( + "[{}] {}: {}", + self.base.id, self.sender, self.content + )) } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } - + fn equals(&self, other: &dyn NyashBox) -> crate::box_trait::BoolBox { if let Some(other_msg) = other.as_any().downcast_ref::() { crate::box_trait::BoolBox::new( - self.sender == other_msg.sender && - self.content == other_msg.content + self.sender == other_msg.sender && self.content == other_msg.content, ) } else { crate::box_trait::BoolBox::new(false) @@ -235,11 +248,11 @@ impl BoxCore for MessageBox { fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "[{}] {}: {}", self.base.id, self.sender, self.content) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -249,4 +262,4 @@ impl Display for MessageBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } -} \ No newline at end of file +} diff --git a/src/cli.rs b/src/cli.rs index 69875566..4e4ceff0 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,11 +1,11 @@ /*! * CLI Argument Parsing Module - Nyash Command Line Interface - * + * * This module handles all command-line argument parsing using clap, * separating CLI concerns from the main execution logic. */ -use clap::{Arg, Command, ArgMatches}; +use clap::{Arg, ArgMatches, Command}; use serde_json; /// Command-line configuration structure @@ -83,7 +83,9 @@ impl CliConfig { } } // Only parse CLI args up to '--' - let matches = Self::build_command().try_get_matches_from(&argv[..pos]).unwrap_or_else(|e| e.exit()); + let matches = Self::build_command() + .try_get_matches_from(&argv[..pos]) + .unwrap_or_else(|e| e.exit()); Self::from_matches(&matches) } else { let matches = Self::build_command().get_matches(); @@ -140,7 +142,7 @@ impl CliConfig { .help("Register a using entry (e.g., 'ns as Alias' or '\"apps/foo.nyash\" as Foo'). Repeatable.") .action(clap::ArgAction::Append) ) - + .arg( Arg::new("debug-fuel") .long("debug-fuel") @@ -425,7 +427,11 @@ impl CliConfig { compile_native: matches.get_flag("compile-native") || matches.get_flag("aot"), output_file: matches.get_one::("output").cloned(), benchmark: matches.get_flag("benchmark"), - iterations: matches.get_one::("iterations").unwrap().parse().unwrap_or(10), + iterations: matches + .get_one::("iterations") + .unwrap() + .parse() + .unwrap_or(10), vm_stats: matches.get_flag("vm-stats"), vm_stats_json: matches.get_flag("vm-stats-json"), jit_exec: matches.get_flag("jit-exec"), @@ -436,7 +442,9 @@ impl CliConfig { jit_events_compile: matches.get_flag("jit-events-compile"), jit_events_runtime: matches.get_flag("jit-events-runtime"), jit_events_path: matches.get_one::("jit-events-path").cloned(), - jit_threshold: matches.get_one::("jit-threshold").and_then(|s| s.parse::().ok()), + jit_threshold: matches + .get_one::("jit-threshold") + .and_then(|s| s.parse::().ok()), jit_phi_min: matches.get_flag("jit-phi-min"), jit_hostcall: matches.get_flag("jit-hostcall"), jit_handle_debug: matches.get_flag("jit-handle-debug"), @@ -448,7 +456,10 @@ impl CliConfig { cli_verbose: matches.get_flag("verbose"), run_task: matches.get_one::("run-task").cloned(), load_ny_plugins: matches.get_flag("load-ny-plugins"), - parser_ny: matches.get_one::("parser").map(|s| s == "ny").unwrap_or(false), + parser_ny: matches + .get_one::("parser") + .map(|s| s == "ny") + .unwrap_or(false), ny_parser_pipe: matches.get_flag("ny-parser-pipe"), json_file: matches.get_one::("json-file").cloned(), // Build system (MVP) @@ -458,7 +469,10 @@ impl CliConfig { build_aot: matches.get_one::("build-aot").cloned(), build_profile: matches.get_one::("build-profile").cloned(), build_target: matches.get_one::("build-target").cloned(), - cli_usings: matches.get_many::("using").map(|v| v.cloned().collect()).unwrap_or_else(|| Vec::new()), + cli_usings: matches + .get_many::("using") + .map(|v| v.cloned().collect()) + .unwrap_or_else(|| Vec::new()), } } } @@ -519,7 +533,7 @@ impl Default for CliConfig { /// Parse debug fuel value ("unlimited" or numeric) fn parse_debug_fuel(value: &str) -> Option { if value == "unlimited" { - None // No limit + None // No limit } else { value.parse::().ok() } diff --git a/src/config/env.rs b/src/config/env.rs index 1f55e12a..3fd29c2c 100644 --- a/src/config/env.rs +++ b/src/config/env.rs @@ -15,12 +15,17 @@ pub struct NyashEnv { impl NyashEnv { pub fn from_env() -> Self { - Self { jit: crate::jit::config::JitConfig::from_env(), overrides: BTreeMap::new() } + Self { + jit: crate::jit::config::JitConfig::from_env(), + overrides: BTreeMap::new(), + } } /// Apply current struct values into process environment pub fn apply_env(&self) { self.jit.apply_env(); - for (k, v) in &self.overrides { std::env::set_var(k, v); } + for (k, v) in &self.overrides { + std::env::set_var(k, v); + } } } @@ -32,14 +37,19 @@ static GLOBAL_ENV: OnceCell> = OnceCell::new(); pub fn current() -> NyashEnv { if let Some(lock) = GLOBAL_ENV.get() { - if let Ok(cfg) = lock.read() { return cfg.clone(); } + if let Ok(cfg) = lock.read() { + return cfg.clone(); + } } NyashEnv::from_env() } pub fn set_current(cfg: NyashEnv) { if let Some(lock) = GLOBAL_ENV.get() { - if let Ok(mut w) = lock.write() { *w = cfg; return; } + if let Ok(mut w) = lock.write() { + *w = cfg; + return; + } } let _ = GLOBAL_ENV.set(RwLock::new(cfg)); } @@ -56,9 +66,16 @@ pub fn bootstrap_from_toml_env() { return; } let path = "nyash.toml"; - let content = match std::fs::read_to_string(path) { Ok(s) => s, Err(_) => return }; - let Ok(value) = toml::from_str::(&content) else { return }; - let Some(env_tbl) = value.get("env").and_then(|v| v.as_table()) else { return }; + let content = match std::fs::read_to_string(path) { + Ok(s) => s, + Err(_) => return, + }; + let Ok(value) = toml::from_str::(&content) else { + return; + }; + let Some(env_tbl) = value.get("env").and_then(|v| v.as_table()) else { + return; + }; let mut overrides: BTreeMap = BTreeMap::new(); for (k, v) in env_tbl { if let Some(s) = v.as_str() { @@ -82,12 +99,17 @@ pub fn bootstrap_from_toml_env() { /// Get await maximum milliseconds, centralized here for consistency. pub fn await_max_ms() -> u64 { - std::env::var("NYASH_AWAIT_MAX_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(5000) + std::env::var("NYASH_AWAIT_MAX_MS") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(5000) } // ---- MIR PHI-less (edge-copy) mode ---- /// Enable MIR PHI non-generation. Bridge/Builder emit edge copies instead of PHI. -pub fn mir_no_phi() -> bool { std::env::var("NYASH_MIR_NO_PHI").ok().as_deref() == Some("1") } +pub fn mir_no_phi() -> bool { + std::env::var("NYASH_MIR_NO_PHI").ok().as_deref() == Some("1") +} /// Allow verifier to skip SSA/dominance/merge checks for PHI-less MIR. pub fn verify_allow_no_phi() -> bool { @@ -95,7 +117,9 @@ pub fn verify_allow_no_phi() -> bool { } // ---- LLVM harness toggle (llvmlite) ---- -pub fn llvm_use_harness() -> bool { std::env::var("NYASH_LLVM_USE_HARNESS").ok().as_deref() == Some("1") } +pub fn llvm_use_harness() -> bool { + std::env::var("NYASH_LLVM_USE_HARNESS").ok().as_deref() == Some("1") +} // ---- Phase 11.8 MIR cleanup toggles ---- /// Core-13 minimal MIR mode toggle @@ -109,30 +133,65 @@ pub fn mir_core13() -> bool { None => true, } } -pub fn mir_ref_boxcall() -> bool { std::env::var("NYASH_MIR_REF_BOXCALL").ok().as_deref() == Some("1") || mir_core13() } -pub fn mir_array_boxcall() -> bool { std::env::var("NYASH_MIR_ARRAY_BOXCALL").ok().as_deref() == Some("1") || mir_core13() } -pub fn mir_plugin_invoke() -> bool { std::env::var("NYASH_MIR_PLUGIN_INVOKE").ok().as_deref() == Some("1") } -pub fn plugin_only() -> bool { std::env::var("NYASH_PLUGIN_ONLY").ok().as_deref() == Some("1") } +pub fn mir_ref_boxcall() -> bool { + std::env::var("NYASH_MIR_REF_BOXCALL").ok().as_deref() == Some("1") || mir_core13() +} +pub fn mir_array_boxcall() -> bool { + std::env::var("NYASH_MIR_ARRAY_BOXCALL").ok().as_deref() == Some("1") || mir_core13() +} +pub fn mir_plugin_invoke() -> bool { + std::env::var("NYASH_MIR_PLUGIN_INVOKE").ok().as_deref() == Some("1") +} +pub fn plugin_only() -> bool { + std::env::var("NYASH_PLUGIN_ONLY").ok().as_deref() == Some("1") +} /// Core-13 "pure" mode: after normalization, only the 13 canonical ops are allowed. /// If enabled, the optimizer will try lightweight rewrites for Load/Store/NewBox/Unary, /// and the final verifier will reject any remaining non-Core-13 ops. -pub fn mir_core13_pure() -> bool { std::env::var("NYASH_MIR_CORE13_PURE").ok().as_deref() == Some("1") } +pub fn mir_core13_pure() -> bool { + std::env::var("NYASH_MIR_CORE13_PURE").ok().as_deref() == Some("1") +} // ---- Optimizer diagnostics ---- -pub fn opt_debug() -> bool { std::env::var("NYASH_OPT_DEBUG").is_ok() } -pub fn opt_diag() -> bool { std::env::var("NYASH_OPT_DIAG").is_ok() } -pub fn opt_diag_forbid_legacy() -> bool { std::env::var("NYASH_OPT_DIAG_FORBID_LEGACY").is_ok() } -pub fn opt_diag_fail() -> bool { std::env::var("NYASH_OPT_DIAG_FAIL").is_ok() } +pub fn opt_debug() -> bool { + std::env::var("NYASH_OPT_DEBUG").is_ok() +} +pub fn opt_diag() -> bool { + std::env::var("NYASH_OPT_DIAG").is_ok() +} +pub fn opt_diag_forbid_legacy() -> bool { + std::env::var("NYASH_OPT_DIAG_FORBID_LEGACY").is_ok() +} +pub fn opt_diag_fail() -> bool { + std::env::var("NYASH_OPT_DIAG_FAIL").is_ok() +} // ---- GC/Runtime tracing (execution-affecting visibility) ---- -pub fn gc_trace() -> bool { std::env::var("NYASH_GC_TRACE").ok().as_deref() == Some("1") } -pub fn gc_barrier_trace() -> bool { std::env::var("NYASH_GC_BARRIER_TRACE").ok().as_deref() == Some("1") } -pub fn runtime_checkpoint_trace() -> bool { std::env::var("NYASH_RUNTIME_CHECKPOINT_TRACE").ok().as_deref() == Some("1") } -pub fn vm_pic_stats() -> bool { std::env::var("NYASH_VM_PIC_STATS").ok().as_deref() == Some("1") } -pub fn vm_vt_trace() -> bool { std::env::var("NYASH_VM_VT_TRACE").ok().as_deref() == Some("1") } -pub fn vm_pic_trace() -> bool { std::env::var("NYASH_VM_PIC_TRACE").ok().as_deref() == Some("1") } -pub fn gc_barrier_strict() -> bool { std::env::var("NYASH_GC_BARRIER_STRICT").ok().as_deref() == Some("1") } +pub fn gc_trace() -> bool { + std::env::var("NYASH_GC_TRACE").ok().as_deref() == Some("1") +} +pub fn gc_barrier_trace() -> bool { + std::env::var("NYASH_GC_BARRIER_TRACE").ok().as_deref() == Some("1") +} +pub fn runtime_checkpoint_trace() -> bool { + std::env::var("NYASH_RUNTIME_CHECKPOINT_TRACE") + .ok() + .as_deref() + == Some("1") +} +pub fn vm_pic_stats() -> bool { + std::env::var("NYASH_VM_PIC_STATS").ok().as_deref() == Some("1") +} +pub fn vm_vt_trace() -> bool { + std::env::var("NYASH_VM_VT_TRACE").ok().as_deref() == Some("1") +} +pub fn vm_pic_trace() -> bool { + std::env::var("NYASH_VM_PIC_TRACE").ok().as_deref() == Some("1") +} +pub fn gc_barrier_strict() -> bool { + std::env::var("NYASH_GC_BARRIER_STRICT").ok().as_deref() == Some("1") +} /// Return 0 (off) to 3 (max) for `NYASH_GC_TRACE`. pub fn gc_trace_level() -> u8 { @@ -146,42 +205,97 @@ pub fn gc_trace_level() -> u8 { } // ---- Rewriter flags (optimizer transforms) -pub fn rewrite_debug() -> bool { std::env::var("NYASH_REWRITE_DEBUG").ok().as_deref() == Some("1") } -pub fn rewrite_safepoint() -> bool { std::env::var("NYASH_REWRITE_SAFEPOINT").ok().as_deref() == Some("1") } -pub fn rewrite_future() -> bool { std::env::var("NYASH_REWRITE_FUTURE").ok().as_deref() == Some("1") } +pub fn rewrite_debug() -> bool { + std::env::var("NYASH_REWRITE_DEBUG").ok().as_deref() == Some("1") +} +pub fn rewrite_safepoint() -> bool { + std::env::var("NYASH_REWRITE_SAFEPOINT").ok().as_deref() == Some("1") +} +pub fn rewrite_future() -> bool { + std::env::var("NYASH_REWRITE_FUTURE").ok().as_deref() == Some("1") +} // ---- Phase 12: Nyash ABI (vtable) toggles ---- -pub fn abi_vtable() -> bool { std::env::var("NYASH_ABI_VTABLE").ok().as_deref() == Some("1") } -pub fn abi_strict() -> bool { std::env::var("NYASH_ABI_STRICT").ok().as_deref() == Some("1") } +pub fn abi_vtable() -> bool { + std::env::var("NYASH_ABI_VTABLE").ok().as_deref() == Some("1") +} +pub fn abi_strict() -> bool { + std::env::var("NYASH_ABI_STRICT").ok().as_deref() == Some("1") +} // ---- ExternCall strict diagnostics ---- -pub fn extern_strict() -> bool { std::env::var("NYASH_EXTERN_STRICT").ok().as_deref() == Some("1") } -pub fn extern_trace() -> bool { std::env::var("NYASH_EXTERN_TRACE").ok().as_deref() == Some("1") } +pub fn extern_strict() -> bool { + std::env::var("NYASH_EXTERN_STRICT").ok().as_deref() == Some("1") +} +pub fn extern_trace() -> bool { + std::env::var("NYASH_EXTERN_TRACE").ok().as_deref() == Some("1") +} // ---- Phase 12: thresholds and routing policies ---- /// PIC hotness threshold before promoting to mono cache. pub fn vm_pic_threshold() -> u32 { - std::env::var("NYASH_VM_PIC_THRESHOLD").ok().and_then(|s| s.parse().ok()).unwrap_or(8) + std::env::var("NYASH_VM_PIC_THRESHOLD") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(8) } /// Route VM ExternCall via name→slot handlers when available -pub fn extern_route_slots() -> bool { std::env::var("NYASH_EXTERN_ROUTE_SLOTS").ok().as_deref() == Some("1") } +pub fn extern_route_slots() -> bool { + std::env::var("NYASH_EXTERN_ROUTE_SLOTS").ok().as_deref() == Some("1") +} // ---- Runner/CLI common toggles (hot-path centralization) -pub fn cli_verbose() -> bool { std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") } -pub fn enable_using() -> bool { std::env::var("NYASH_ENABLE_USING").ok().as_deref() == Some("1") } -pub fn vm_use_py() -> bool { std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1") } -pub fn pipe_use_pyvm() -> bool { std::env::var("NYASH_PIPE_USE_PYVM").ok().as_deref() == Some("1") } -pub fn vm_use_dispatch() -> bool { std::env::var("NYASH_VM_USE_DISPATCH").ok().as_deref() == Some("1") } +pub fn cli_verbose() -> bool { + std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") +} +pub fn enable_using() -> bool { + std::env::var("NYASH_ENABLE_USING").ok().as_deref() == Some("1") +} +pub fn vm_use_py() -> bool { + std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1") +} +pub fn pipe_use_pyvm() -> bool { + std::env::var("NYASH_PIPE_USE_PYVM").ok().as_deref() == Some("1") +} +pub fn vm_use_dispatch() -> bool { + std::env::var("NYASH_VM_USE_DISPATCH").ok().as_deref() == Some("1") +} // Self-host compiler knobs -pub fn ny_compiler_timeout_ms() -> u64 { std::env::var("NYASH_NY_COMPILER_TIMEOUT_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(2000) } -pub fn ny_compiler_emit_only() -> bool { std::env::var("NYASH_NY_COMPILER_EMIT_ONLY").unwrap_or_else(|_| "1".to_string()) == "1" } -pub fn ny_compiler_skip_py() -> bool { std::env::var("NYASH_NY_COMPILER_SKIP_PY").ok().as_deref() == Some("1") } -pub fn use_ny_compiler_exe() -> bool { std::env::var("NYASH_USE_NY_COMPILER_EXE").ok().as_deref() == Some("1") } -pub fn ny_compiler_exe_path() -> Option { std::env::var("NYASH_NY_COMPILER_EXE_PATH").ok() } -pub fn ny_compiler_min_json() -> bool { std::env::var("NYASH_NY_COMPILER_MIN_JSON").ok().as_deref() == Some("1") } -pub fn selfhost_read_tmp() -> bool { std::env::var("NYASH_SELFHOST_READ_TMP").ok().as_deref() == Some("1") } -pub fn ny_compiler_stage3() -> bool { std::env::var("NYASH_NY_COMPILER_STAGE3").ok().as_deref() == Some("1") } -pub fn ny_compiler_child_args() -> Option { std::env::var("NYASH_NY_COMPILER_CHILD_ARGS").ok() } -pub fn ny_compiler_use_tmp_only() -> bool { std::env::var("NYASH_NY_COMPILER_USE_TMP_ONLY").ok().as_deref() == Some("1") } +pub fn ny_compiler_timeout_ms() -> u64 { + std::env::var("NYASH_NY_COMPILER_TIMEOUT_MS") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(2000) +} +pub fn ny_compiler_emit_only() -> bool { + std::env::var("NYASH_NY_COMPILER_EMIT_ONLY").unwrap_or_else(|_| "1".to_string()) == "1" +} +pub fn ny_compiler_skip_py() -> bool { + std::env::var("NYASH_NY_COMPILER_SKIP_PY").ok().as_deref() == Some("1") +} +pub fn use_ny_compiler_exe() -> bool { + std::env::var("NYASH_USE_NY_COMPILER_EXE").ok().as_deref() == Some("1") +} +pub fn ny_compiler_exe_path() -> Option { + std::env::var("NYASH_NY_COMPILER_EXE_PATH").ok() +} +pub fn ny_compiler_min_json() -> bool { + std::env::var("NYASH_NY_COMPILER_MIN_JSON").ok().as_deref() == Some("1") +} +pub fn selfhost_read_tmp() -> bool { + std::env::var("NYASH_SELFHOST_READ_TMP").ok().as_deref() == Some("1") +} +pub fn ny_compiler_stage3() -> bool { + std::env::var("NYASH_NY_COMPILER_STAGE3").ok().as_deref() == Some("1") +} +pub fn ny_compiler_child_args() -> Option { + std::env::var("NYASH_NY_COMPILER_CHILD_ARGS").ok() +} +pub fn ny_compiler_use_tmp_only() -> bool { + std::env::var("NYASH_NY_COMPILER_USE_TMP_ONLY") + .ok() + .as_deref() + == Some("1") +} diff --git a/src/config/mod.rs b/src/config/mod.rs index db692e97..b9151a32 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,8 +1,8 @@ //! Nyash configuration module -//! +//! //! Handles nyash.toml parsing and configuration management -pub mod nyash_toml_v2; pub mod env; +pub mod nyash_toml_v2; -pub use nyash_toml_v2::{NyashConfigV2, LibraryDefinition, BoxTypeConfig, MethodDefinition}; +pub use nyash_toml_v2::{BoxTypeConfig, LibraryDefinition, MethodDefinition, NyashConfigV2}; diff --git a/src/config/nyash_toml_v2.rs b/src/config/nyash_toml_v2.rs index bef25b62..3c5822bd 100644 --- a/src/config/nyash_toml_v2.rs +++ b/src/config/nyash_toml_v2.rs @@ -1,5 +1,5 @@ //! nyash.toml v2 configuration parser -//! +//! //! Ultimate simple design: nyash.toml-centric architecture + minimal FFI //! No Host VTable, single entry point (nyash_plugin_invoke) @@ -12,7 +12,7 @@ pub struct NyashConfigV2 { /// Library definitions (multi-box capable) #[serde(default)] pub libraries: HashMap, - + /// Plugin search paths #[serde(default)] pub plugin_paths: PluginPaths, @@ -31,7 +31,7 @@ pub struct NyashConfigV2 { pub struct LibraryDefinition { /// Box types provided by this library pub boxes: Vec, - + /// Path to the shared library pub path: String, } @@ -48,11 +48,11 @@ pub struct PluginPaths { pub struct BoxTypeConfig { /// Box type ID pub type_id: u32, - + /// ABI version (default: 1) #[serde(default = "default_abi_version")] pub abi_version: u32, - + /// Method definitions pub methods: HashMap, @@ -68,7 +68,7 @@ pub struct MethodDefinition { pub method_id: u32, /// Optional argument declarations (v2.1+) #[serde(default)] - pub args: Option>, + pub args: Option>, /// Optional: wrap return and errors into ResultBox when true #[serde(default)] pub returns_result: bool, @@ -128,29 +128,47 @@ impl NyashConfigV2 { let plugins = if let Some(tbl) = config.get("plugins").and_then(|v| v.as_table()) { let mut m = HashMap::new(); for (k, v) in tbl.iter() { - if let Some(s) = v.as_str() { m.insert(k.clone(), s.to_string()); } + if let Some(s) = v.as_str() { + m.insert(k.clone(), s.to_string()); + } } m - } else { HashMap::new() }; + } else { + HashMap::new() + }; // Extract optional box_types map let box_types = if let Some(tbl) = config.get("box_types").and_then(|v| v.as_table()) { let mut m = HashMap::new(); - for (k, v) in tbl.iter() { if let Some(id) = v.as_integer() { m.insert(k.clone(), id as u32); } } + for (k, v) in tbl.iter() { + if let Some(id) = v.as_integer() { + m.insert(k.clone(), id as u32); + } + } m - } else { HashMap::new() }; + } else { + HashMap::new() + }; - Ok(NyashConfigV2 { libraries, plugin_paths, plugins, box_types }) + Ok(NyashConfigV2 { + libraries, + plugin_paths, + plugins, + box_types, + }) } - + /// Parse library definitions with nested box configs - fn parse_libraries(config: &mut toml::Value) -> Result, Box> { + fn parse_libraries( + config: &mut toml::Value, + ) -> Result, Box> { let mut libraries = HashMap::new(); - + if let Some(libs_section) = config.get("libraries").and_then(|v| v.as_table()) { for (lib_name, lib_value) in libs_section { if let Some(lib_table) = lib_value.as_table() { - let boxes = lib_table.get("boxes") + let boxes = lib_table + .get("boxes") .and_then(|v| v.as_array()) .map(|arr| { arr.iter() @@ -159,40 +177,44 @@ impl NyashConfigV2 { .collect() }) .unwrap_or_default(); - - let path = lib_table.get("path") + + let path = lib_table + .get("path") .and_then(|v| v.as_str()) .unwrap_or(lib_name) .to_string(); - - libraries.insert(lib_name.clone(), LibraryDefinition { - boxes, - path, - }); + + libraries.insert(lib_name.clone(), LibraryDefinition { boxes, path }); } } } - + Ok(libraries) } - + /// Get box configuration from nested structure /// e.g., [libraries."libnyash_filebox_plugin.so".FileBox] - pub fn get_box_config(&self, lib_name: &str, box_name: &str, config_value: &toml::Value) -> Option { + pub fn get_box_config( + &self, + lib_name: &str, + box_name: &str, + config_value: &toml::Value, + ) -> Option { config_value .get("libraries") .and_then(|v| v.get(lib_name)) .and_then(|v| v.get(box_name)) .and_then(|v| v.clone().try_into::().ok()) } - + /// Find library that provides a specific box type pub fn find_library_for_box(&self, box_type: &str) -> Option<(&str, &LibraryDefinition)> { - self.libraries.iter() + self.libraries + .iter() .find(|(_, lib)| lib.boxes.contains(&box_type.to_string())) .map(|(name, lib)| (name.as_str(), lib)) } - + /// Resolve plugin path from search paths pub fn resolve_plugin_path(&self, plugin_name: &str) -> Option { // Try exact path first @@ -203,8 +225,14 @@ impl NyashConfigV2 { let mut paths: Vec = Vec::new(); paths.extend(self.plugin_paths.search_paths.iter().cloned()); if let Ok(envp) = std::env::var("NYASH_PLUGIN_PATHS") { - let sep = if cfg!(target_os = "windows") { ';' } else { ':' }; - for p in envp.split(sep).filter(|s| !s.is_empty()) { paths.push(p.to_string()); } + let sep = if cfg!(target_os = "windows") { + ';' + } else { + ':' + }; + for p in envp.split(sep).filter(|s| !s.is_empty()) { + paths.push(p.to_string()); + } } // Search in effective paths for search_path in &paths { @@ -213,7 +241,7 @@ impl NyashConfigV2 { return Some(path.to_string_lossy().to_string()); } } - + None } } @@ -221,7 +249,7 @@ impl NyashConfigV2 { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_parse_v2_config() { let toml_str = r#" @@ -240,13 +268,17 @@ open = { method_id = 1 } close = { method_id = 4 } "#; let nyash_config = NyashConfigV2::from_str(toml_str).unwrap(); - assert!(nyash_config.libraries.contains_key("libnyash_filebox_plugin.so")); + assert!(nyash_config + .libraries + .contains_key("libnyash_filebox_plugin.so")); let (lib_name, lib_def) = nyash_config.find_library_for_box("FileBox").unwrap(); assert_eq!(lib_name, "libnyash_filebox_plugin.so"); assert_eq!(lib_def.boxes, vec!["FileBox".to_string()]); // Nested box config should be retrievable let raw: toml::Value = toml::from_str(toml_str).unwrap(); - let box_conf = nyash_config.get_box_config("libnyash_filebox_plugin.so", "FileBox", &raw).unwrap(); + let box_conf = nyash_config + .get_box_config("libnyash_filebox_plugin.so", "FileBox", &raw) + .unwrap(); assert_eq!(box_conf.type_id, 6); } } diff --git a/src/core/mod.rs b/src/core/mod.rs index eb58a64c..02c8cb29 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,4 +1,3 @@ //! Core model types shared across interpreter and VM pub mod model; - diff --git a/src/debug/log.rs b/src/debug/log.rs index 1d586772..e5fa186c 100644 --- a/src/debug/log.rs +++ b/src/debug/log.rs @@ -7,7 +7,9 @@ pub fn on(var: &str) -> bool { /// Log a message to stderr if the env var is enabled. pub fn log(var: &str, msg: &str) { - if on(var) { eprintln!("{}", msg); } + if on(var) { + eprintln!("{}", msg); + } } /// Log with formatting if the env var is enabled. @@ -19,4 +21,3 @@ macro_rules! debug_logf { } }}; } - diff --git a/src/debug/mod.rs b/src/debug/mod.rs index 465aa0d3..f4ee9bc8 100644 --- a/src/debug/mod.rs +++ b/src/debug/mod.rs @@ -1,2 +1 @@ pub mod log; - diff --git a/src/environment.rs b/src/environment.rs index 3fbe612c..eac7c6c9 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -1,6 +1,6 @@ /*! * Nyash Environment System - Rust所有権ベースのスコープ管理 - * + * * Python版の曖昧なメモリ管理をRustの借用チェッカーで完全解決! * Everything is Box哲学 + 所有権システム = 完璧なスコープ管理 */ @@ -16,16 +16,16 @@ use thiserror::Error; pub struct Environment { /// 現在のスコープの変数バインディング bindings: Mutex>>, - + /// 親環境への参照 (Arc>でスレッド安全) parent: Option>>, - + /// スコープレベル (デバッグ用) level: usize, - + /// スコープ名 (関数名、クラス名など) scope_name: String, - + /// Box解放管理 finalizer: Mutex, } @@ -35,10 +35,10 @@ pub struct Environment { pub enum EnvironmentError { #[error("Variable '{name}' is not defined")] UndefinedVariable { name: String }, - + #[error("Cannot access parent environment: {reason}")] ParentAccessError { reason: String }, - + #[error("Borrowing conflict in environment: {details}")] BorrowError { details: String }, } @@ -54,14 +54,14 @@ impl Environment { finalizer: Mutex::new(BoxFinalizer::new()), })) } - + /// 親環境を持つ新しい環境を作成 (関数、クラス用) pub fn new_child( - parent: Arc>, - scope_name: impl Into + parent: Arc>, + scope_name: impl Into, ) -> Arc> { let level = parent.lock().unwrap().level + 1; - + Arc::new(Mutex::new(Self { bindings: Mutex::new(HashMap::new()), parent: Some(parent), @@ -70,41 +70,45 @@ impl Environment { finalizer: Mutex::new(BoxFinalizer::new()), })) } - + /// 変数を現在のスコープに定義 pub fn define(&self, name: impl Into, value: Box) { let name = name.into(); self.bindings.lock().unwrap().insert(name, value); } - + /// 変数を取得 (スコープチェーンを辿る) pub fn get(&self, name: &str) -> Result, EnvironmentError> { // 現在のスコープから検索 if let Some(value) = self.bindings.lock().unwrap().get(name) { return Ok(value.clone_or_share()); } - + // 親スコープから検索 if let Some(parent) = &self.parent { return parent.lock().unwrap().get(name); } - + // 見つからない - Err(EnvironmentError::UndefinedVariable { - name: name.to_string() + Err(EnvironmentError::UndefinedVariable { + name: name.to_string(), }) } - + /// 変数を設定 (既存変数の更新 or 新規定義) - pub fn set(&self, name: impl Into, value: Box) -> Result<(), EnvironmentError> { + pub fn set( + &self, + name: impl Into, + value: Box, + ) -> Result<(), EnvironmentError> { let name = name.into(); - + // 現在のスコープにある場合は更新 if self.bindings.lock().unwrap().contains_key(&name) { self.bindings.lock().unwrap().insert(name, value); return Ok(()); } - + // 親スコープで再帰的に検索・設定 if let Some(parent) = &self.parent { match parent.lock().unwrap().set(&name, value.clone_or_share()) { @@ -115,76 +119,84 @@ impl Environment { Err(e) => return Err(e), } } - + // 新規定義として現在のスコープに追加 self.bindings.lock().unwrap().insert(name, value); Ok(()) } - + /// 変数が存在するかチェック pub fn exists(&self, name: &str) -> bool { self.get(name).is_ok() } - + /// 変数を削除 (現在のスコープからのみ) pub fn undefine(&self, name: &str) -> bool { self.bindings.lock().unwrap().remove(name).is_some() } - + /// 現在のスコープの変数一覧を取得 pub fn list_variables(&self) -> Vec { self.bindings.lock().unwrap().keys().cloned().collect() } - + /// スコープ情報を取得 (デバッグ用) pub fn scope_info(&self) -> String { - format!("{}[{}] (level {})", self.scope_name, self.bindings.lock().unwrap().len(), self.level) + format!( + "{}[{}] (level {})", + self.scope_name, + self.bindings.lock().unwrap().len(), + self.level + ) } - + /// スコープチェーン全体の情報を取得 pub fn scope_chain_info(&self) -> Vec { let mut chain = vec![self.scope_info()]; - + if let Some(parent) = &self.parent { chain.extend(parent.lock().unwrap().scope_chain_info()); } - + chain } - + /// 全変数をダンプ (デバッグ用) pub fn dump_all_variables(&self) -> HashMap { let mut all_vars = HashMap::new(); - + // 現在のスコープの変数 for (name, value) in self.bindings.lock().unwrap().iter() { all_vars.insert( format!("{}::{}", self.scope_name, name), - value.to_string_box().value + value.to_string_box().value, ); } - + // 親スコープの変数 (再帰的) if let Some(parent) = &self.parent { all_vars.extend(parent.lock().unwrap().dump_all_variables()); } - + all_vars } - + /// 新しいBoxを追跡対象に追加 pub fn track_box(&self, nyash_box: Box) { self.finalizer.lock().unwrap().track(nyash_box); } - + /// スコープ終了時にすべてのBoxを解放 pub fn finalize_all_boxes(&self) { self.finalizer.lock().unwrap().finalize_all(); } - + /// 指定したBoxを解放対象から除外(関数の返り値など) pub fn exclude_from_finalization(&self, nyash_box: &Box) { - self.finalizer.lock().unwrap().exclude_from_finalization(nyash_box); + self.finalizer + .lock() + .unwrap() + .exclude_from_finalization(nyash_box); } } @@ -202,28 +214,30 @@ impl PythonCompatEnvironment { _bindings: HashMap::new(), } } - + pub fn new_with_parent(parent: Arc>) -> Self { Self { inner: Environment::new_child(parent, "python_compat"), _bindings: HashMap::new(), } } - + /// Python版のdefineメソッド互換 pub fn define(&mut self, name: impl Into, value: Box) { let name = name.into(); self.inner.lock().unwrap().define(&name, value.clone_box()); self._bindings.insert(name, value); } - + /// Python版のgetメソッド互換 pub fn get(&self, name: &str) -> Box { - self.inner.lock().unwrap().get(name).unwrap_or_else(|_| { - Box::new(VoidBox::new()) - }) + self.inner + .lock() + .unwrap() + .get(name) + .unwrap_or_else(|_| Box::new(VoidBox::new())) } - + /// Rustネイティブ環境への参照を取得 pub fn as_native(&self) -> Arc> { self.inner.clone() @@ -235,89 +249,122 @@ impl PythonCompatEnvironment { #[cfg(test)] mod tests { use super::*; - use crate::box_trait::{StringBox, IntegerBox, BoolBox}; - + use crate::box_trait::{BoolBox, IntegerBox, StringBox}; + #[test] fn test_global_environment() { let env = Environment::new_global(); - + // 変数定義 - env.lock().unwrap().define("test_var", Box::new(StringBox::new("hello"))); - + env.lock() + .unwrap() + .define("test_var", Box::new(StringBox::new("hello"))); + // 変数取得 let value = env.lock().unwrap().get("test_var").unwrap(); let string_val = value.as_any().downcast_ref::().unwrap(); assert_eq!(string_val.value, "hello"); } - + #[test] fn test_nested_scopes() { let global = Environment::new_global(); let function_scope = Environment::new_child(global.clone(), "test_function"); - + // グローバルスコープに変数定義 - global.lock().unwrap().define("global_var", Box::new(StringBox::new("global"))); - + global + .lock() + .unwrap() + .define("global_var", Box::new(StringBox::new("global"))); + // 関数スコープに変数定義 - function_scope.lock().unwrap().define("local_var", Box::new(StringBox::new("local"))); - + function_scope + .lock() + .unwrap() + .define("local_var", Box::new(StringBox::new("local"))); + // 関数スコープからグローバル変数にアクセス可能 let global_from_function = function_scope.lock().unwrap().get("global_var").unwrap(); - let global_str = global_from_function.as_any().downcast_ref::().unwrap(); + let global_str = global_from_function + .as_any() + .downcast_ref::() + .unwrap(); assert_eq!(global_str.value, "global"); - + // グローバルスコープからローカル変数にはアクセス不可 assert!(global.lock().unwrap().get("local_var").is_err()); } - + #[test] fn test_variable_shadowing() { let global = Environment::new_global(); let local = Environment::new_child(global.clone(), "local_scope"); - + // 同名変数を両スコープに定義 - global.lock().unwrap().define("same_name", Box::new(StringBox::new("global_value"))); - local.lock().unwrap().define("same_name", Box::new(StringBox::new("local_value"))); - + global + .lock() + .unwrap() + .define("same_name", Box::new(StringBox::new("global_value"))); + local + .lock() + .unwrap() + .define("same_name", Box::new(StringBox::new("local_value"))); + // ローカルスコープからはローカル値が取得される (シャドウイング) let value = local.lock().unwrap().get("same_name").unwrap(); let string_val = value.as_any().downcast_ref::().unwrap(); assert_eq!(string_val.value, "local_value"); - + // グローバルスコープからはグローバル値が取得される let global_value = global.lock().unwrap().get("same_name").unwrap(); let global_str = global_value.as_any().downcast_ref::().unwrap(); assert_eq!(global_str.value, "global_value"); } - + #[test] fn test_variable_setting() { let global = Environment::new_global(); let local = Environment::new_child(global.clone(), "local_scope"); - + // グローバルに変数定義 - global.lock().unwrap().define("shared_var", Box::new(IntegerBox::new(100))); - + global + .lock() + .unwrap() + .define("shared_var", Box::new(IntegerBox::new(100))); + // ローカルスコープから変数を更新 - local.lock().unwrap().set("shared_var", Box::new(IntegerBox::new(200))).unwrap(); - + local + .lock() + .unwrap() + .set("shared_var", Box::new(IntegerBox::new(200))) + .unwrap(); + // グローバルスコープの値が更新されている let updated_value = global.lock().unwrap().get("shared_var").unwrap(); let int_val = updated_value.as_any().downcast_ref::().unwrap(); assert_eq!(int_val.value, 200); } - + #[test] fn test_scope_info() { let global = Environment::new_global(); let func1 = Environment::new_child(global.clone(), "function1"); let func2 = Environment::new_child(func1.clone(), "function2"); - + // 各スコープに変数を追加 - global.lock().unwrap().define("g1", Box::new(StringBox::new("global1"))); - func1.lock().unwrap().define("f1", Box::new(StringBox::new("func1"))); - func2.lock().unwrap().define("f2", Box::new(StringBox::new("func2"))); - + global + .lock() + .unwrap() + .define("g1", Box::new(StringBox::new("global1"))); + func1 + .lock() + .unwrap() + .define("f1", Box::new(StringBox::new("func1"))); + func2 + .lock() + .unwrap() + .define("f2", Box::new(StringBox::new("func2"))); + // スコープチェーン情報を確認 let chain = func2.lock().unwrap().scope_chain_info(); assert_eq!(chain.len(), 3); @@ -325,30 +372,30 @@ mod tests { assert!(chain[1].contains("function1")); assert!(chain[2].contains("global")); } - + #[test] fn test_python_compat() { let mut env = PythonCompatEnvironment::new(); - + // Python互換インターフェースで変数操作 env.define("test", Box::new(BoolBox::new(true))); - + let value = env.get("test"); let bool_val = value.as_any().downcast_ref::().unwrap(); assert_eq!(bool_val.value, true); - + // _bindingsでも確認可能 assert!(env._bindings.contains_key("test")); } - + #[test] fn test_error_handling() { let env = Environment::new_global(); - + // 存在しない変数へのアクセス let result = env.lock().unwrap().get("nonexistent"); assert!(result.is_err()); - + match result { Err(EnvironmentError::UndefinedVariable { name }) => { assert_eq!(name, "nonexistent"); diff --git a/src/exception_box.rs b/src/exception_box.rs index bd2bd826..413d75a9 100644 --- a/src/exception_box.rs +++ b/src/exception_box.rs @@ -1,10 +1,10 @@ /*! * Exception Boxes for Nyash try/catch/throw system - * + * * Everything is 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::collections::HashMap; @@ -26,7 +26,7 @@ impl ErrorBox { base: BoxBase::new(), } } - + pub fn with_cause(message: &str, cause: ErrorBox) -> Self { Self { message: message.to_string(), @@ -35,11 +35,11 @@ impl ErrorBox { base: BoxBase::new(), } } - + pub fn add_stack_frame(&mut self, frame: String) { self.stack_trace.push(frame); } - + pub fn get_full_message(&self) -> String { let mut msg = self.message.clone(); if let Some(ref cause) = self.cause { @@ -53,12 +53,11 @@ impl NyashBox for ErrorBox { fn type_name(&self) -> &'static str { "ErrorBox" } - + fn to_string_box(&self) -> StringBox { StringBox::new(format!("ErrorBox({})", self.message)) } - - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_error) = other.as_any().downcast_ref::() { BoolBox::new(self.message == other_error.message) @@ -66,11 +65,11 @@ impl NyashBox for ErrorBox { BoolBox::new(false) } } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -89,11 +88,11 @@ impl BoxCore for ErrorBox { fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "ErrorBox({})", self.message) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -114,7 +113,11 @@ pub fn is_exception_type(exception: &dyn NyashBox, type_name: &str) -> bool { } /// 例外の作成ヘルパー -pub fn create_exception(_type_name: &str, message: &str, _extra_info: &HashMap) -> Box { +pub fn create_exception( + _type_name: &str, + message: &str, + _extra_info: &HashMap, +) -> Box { // 現在はErrorBoxのみサポート(シンプル実装) Box::new(ErrorBox::new(message)) -} \ No newline at end of file +} diff --git a/src/finalization.rs b/src/finalization.rs index 69ea5e5d..7cb09e6a 100644 --- a/src/finalization.rs +++ b/src/finalization.rs @@ -1,18 +1,18 @@ /*! * Nyash Finalization System - Memory Management - * + * * fini()によるメモリ管理システムの実装 * - 解放済みBoxの追跡 * - スコープベースの自動解放 * - 再代入時の自動解放 */ -use std::collections::HashSet; -use std::sync::{Arc, Mutex}; -use std::fmt; use crate::box_trait::NyashBox; use crate::instance_v2::InstanceBox; use lazy_static::lazy_static; +use std::collections::HashSet; +use std::fmt; +use std::sync::{Arc, Mutex}; lazy_static! { /// グローバルな解放済みBox ID管理 @@ -44,19 +44,19 @@ impl BoxFinalizer { excluded_boxes: HashSet::new(), } } - + /// 新しいBoxを追跡対象に追加 pub fn track(&mut self, nyash_box: Box) { let box_id = nyash_box.box_id(); self.created_boxes.push((box_id, nyash_box)); } - + /// 指定したBoxを解放対象から除外(関数の返り値など) pub fn exclude_from_finalization(&mut self, nyash_box: &Box) { let box_id = nyash_box.box_id(); self.excluded_boxes.insert(box_id); } - + /// スコープ終了時に全てのBoxを解放(除外対象を除く) pub fn finalize_all(&mut self) { // 作成順(古い順)に解放 @@ -65,7 +65,7 @@ impl BoxFinalizer { if self.excluded_boxes.contains(box_id) { continue; } - + if !is_finalized(*box_id) { // fini()メソッドを呼び出す(存在する場合) if let Some(instance) = nyash_box.as_any().downcast_ref::() { @@ -96,17 +96,17 @@ impl fmt::Debug for BoxFinalizer { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_finalization_tracking() { let box_id = 12345; assert!(!is_finalized(box_id)); - + mark_as_finalized(box_id); assert!(is_finalized(box_id)); - + // 二重解放チェック mark_as_finalized(box_id); // 問題なし assert!(is_finalized(box_id)); } -} \ No newline at end of file +} diff --git a/src/grammar/engine.rs b/src/grammar/engine.rs index 019ea378..f3e895f9 100644 --- a/src/grammar/engine.rs +++ b/src/grammar/engine.rs @@ -5,7 +5,9 @@ use super::generated; pub struct UnifiedGrammarEngine; impl UnifiedGrammarEngine { - pub fn load() -> Self { Self } + pub fn load() -> Self { + Self + } pub fn is_keyword_str(&self, word: &str) -> Option<&'static str> { generated::lookup_keyword(word) } @@ -15,45 +17,93 @@ impl UnifiedGrammarEngine { pub fn add_rules(&self) -> &'static [(&'static str, &'static str, &'static str, &'static str)] { generated::OPERATORS_ADD_RULES } - pub fn decide_add_result(&self, left_ty: &str, right_ty: &str) -> Option<(&'static str, &'static str)> { + pub fn decide_add_result( + &self, + left_ty: &str, + right_ty: &str, + ) -> Option<(&'static str, &'static str)> { for (l, r, res, act) in self.add_rules() { - if *l == left_ty && *r == right_ty { return Some((*res, *act)); } + if *l == left_ty && *r == right_ty { + return Some((*res, *act)); + } } None } - pub fn sub_coercion_strategy(&self) -> &'static str { generated::OPERATORS_SUB_COERCION } - pub fn sub_rules(&self) -> &'static [(&'static str, &'static str, &'static str, &'static str)] { generated::OPERATORS_SUB_RULES } - pub fn decide_sub_result(&self, left_ty: &str, right_ty: &str) -> Option<(&'static str, &'static str)> { - for (l, r, res, act) in self.sub_rules() { if *l == left_ty && *r == right_ty { return Some((*res, *act)); } } + pub fn sub_coercion_strategy(&self) -> &'static str { + generated::OPERATORS_SUB_COERCION + } + pub fn sub_rules(&self) -> &'static [(&'static str, &'static str, &'static str, &'static str)] { + generated::OPERATORS_SUB_RULES + } + pub fn decide_sub_result( + &self, + left_ty: &str, + right_ty: &str, + ) -> Option<(&'static str, &'static str)> { + for (l, r, res, act) in self.sub_rules() { + if *l == left_ty && *r == right_ty { + return Some((*res, *act)); + } + } None } - pub fn mul_coercion_strategy(&self) -> &'static str { generated::OPERATORS_MUL_COERCION } - pub fn mul_rules(&self) -> &'static [(&'static str, &'static str, &'static str, &'static str)] { generated::OPERATORS_MUL_RULES } - pub fn decide_mul_result(&self, left_ty: &str, right_ty: &str) -> Option<(&'static str, &'static str)> { - for (l, r, res, act) in self.mul_rules() { if *l == left_ty && *r == right_ty { return Some((*res, *act)); } } + pub fn mul_coercion_strategy(&self) -> &'static str { + generated::OPERATORS_MUL_COERCION + } + pub fn mul_rules(&self) -> &'static [(&'static str, &'static str, &'static str, &'static str)] { + generated::OPERATORS_MUL_RULES + } + pub fn decide_mul_result( + &self, + left_ty: &str, + right_ty: &str, + ) -> Option<(&'static str, &'static str)> { + for (l, r, res, act) in self.mul_rules() { + if *l == left_ty && *r == right_ty { + return Some((*res, *act)); + } + } None } - pub fn div_coercion_strategy(&self) -> &'static str { generated::OPERATORS_DIV_COERCION } - pub fn div_rules(&self) -> &'static [(&'static str, &'static str, &'static str, &'static str)] { generated::OPERATORS_DIV_RULES } - pub fn decide_div_result(&self, left_ty: &str, right_ty: &str) -> Option<(&'static str, &'static str)> { - for (l, r, res, act) in self.div_rules() { if *l == left_ty && *r == right_ty { return Some((*res, *act)); } } + pub fn div_coercion_strategy(&self) -> &'static str { + generated::OPERATORS_DIV_COERCION + } + pub fn div_rules(&self) -> &'static [(&'static str, &'static str, &'static str, &'static str)] { + generated::OPERATORS_DIV_RULES + } + pub fn decide_div_result( + &self, + left_ty: &str, + right_ty: &str, + ) -> Option<(&'static str, &'static str)> { + for (l, r, res, act) in self.div_rules() { + if *l == left_ty && *r == right_ty { + return Some((*res, *act)); + } + } None } } pub static ENGINE: Lazy = Lazy::new(UnifiedGrammarEngine::load); -pub fn get() -> &'static UnifiedGrammarEngine { &ENGINE } +pub fn get() -> &'static UnifiedGrammarEngine { + &ENGINE +} // --- Syntax rule helpers (generated-backed) --- impl UnifiedGrammarEngine { pub fn syntax_is_allowed_statement(&self, keyword: &str) -> bool { - super::generated::SYNTAX_ALLOWED_STATEMENTS.iter().any(|k| *k == keyword) + super::generated::SYNTAX_ALLOWED_STATEMENTS + .iter() + .any(|k| *k == keyword) } pub fn syntax_is_allowed_binop(&self, op: &str) -> bool { - super::generated::SYNTAX_ALLOWED_BINOPS.iter().any(|k| *k == op) + super::generated::SYNTAX_ALLOWED_BINOPS + .iter() + .any(|k| *k == op) } } diff --git a/src/grammar/generated.rs b/src/grammar/generated.rs index 0f04d259..8e402b39 100644 --- a/src/grammar/generated.rs +++ b/src/grammar/generated.rs @@ -1,9 +1,5 @@ // Auto-generated from grammar/unified-grammar.toml -pub static KEYWORDS: &[(&str, &str)] = &[ - ("me", "ME"), - ("from", "FROM"), - ("loop", "LOOP"), -]; +pub static KEYWORDS: &[(&str, &str)] = &[("me", "ME"), ("from", "FROM"), ("loop", "LOOP")]; pub static OPERATORS_ADD_COERCION: &str = "string_priority"; pub static OPERATORS_SUB_COERCION: &str = "numeric_only"; pub static OPERATORS_MUL_COERCION: &str = "numeric_only"; @@ -33,37 +29,15 @@ pub static OPERATORS_DIV_RULES: &[(&str, &str, &str, &str)] = &[ ]; pub fn lookup_keyword(word: &str) -> Option<&'static str> { for (k, t) in KEYWORDS { - if *k == word { return Some(*t); } + if *k == word { + return Some(*t); + } } None } pub static SYNTAX_ALLOWED_STATEMENTS: &[&str] = &[ - "box", - "global", - "function", - "static", - "if", - "loop", - "break", - "return", - "print", - "nowait", - "include", - "local", - "outbox", - "try", - "throw", - "using", - "from", + "box", "global", "function", "static", "if", "loop", "break", "return", "print", "nowait", + "include", "local", "outbox", "try", "throw", "using", "from", ]; -pub static SYNTAX_ALLOWED_BINOPS: &[&str] = &[ - "add", - "sub", - "mul", - "div", - "and", - "or", - "eq", - "ne", -]; \ No newline at end of file +pub static SYNTAX_ALLOWED_BINOPS: &[&str] = &["add", "sub", "mul", "div", "and", "or", "eq", "ne"]; diff --git a/src/grammar/mod.rs b/src/grammar/mod.rs index 03b8e2a5..fcba932e 100644 --- a/src/grammar/mod.rs +++ b/src/grammar/mod.rs @@ -3,4 +3,3 @@ pub mod engine; #[path = "generated.rs"] mod generated; pub use generated::*; - diff --git a/src/instance_v2.rs b/src/instance_v2.rs index fe77e71c..f99819de 100644 --- a/src/instance_v2.rs +++ b/src/instance_v2.rs @@ -1,22 +1,22 @@ /*! * Nyash Instance System v2 - Simplified Box Instance Implementation - * + * * 🎯 Phase 9.78d: 簡素化InstanceBox統一実装 * Everything is Box哲学に基づく統一オブジェクト指向システム - * + * * 🔄 設計方針: trait objectによる完全統一 * - すべてのBox型を同じように扱う * - Optionによる柔軟性 * - レガシー負債の完全削除 */ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase, SharedNyashBox}; use crate::ast::ASTNode; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, SharedNyashBox, StringBox}; +use crate::interpreter::NyashInterpreter; // レガシー互換用 use crate::value::NyashValue; -use crate::interpreter::NyashInterpreter; // レガシー互換用 +use std::any::Any; use std::collections::HashMap; use std::fmt::{Debug, Display}; -use std::any::Any; use std::sync::{Arc, Mutex}; /// 🎯 簡素化InstanceBox - すべてのBox型を統一管理 @@ -24,20 +24,20 @@ use std::sync::{Arc, Mutex}; pub struct InstanceBox { /// クラス名(StringBox, MyUserBox等統一) pub class_name: String, - + /// 統一フィールド管理(レガシーfields削除) pub fields_ng: Arc>>, - + /// メソッド定義(ユーザー定義時のみ使用、ビルトインは空) pub methods: Arc>, - + /// 🏭 統一内容 - すべてのBox型を同じように扱う pub inner_content: Option>, - + /// Box基底 + ライフサイクル管理 base: BoxBase, finalized: Arc>, - + /// 🔄 Phase 9.78e: レガシー互換フィールド(段階的移行用) pub fields: Option>>>, init_field_order: Vec, @@ -52,7 +52,7 @@ impl InstanceBox { class_name, fields_ng: Arc::new(Mutex::new(HashMap::new())), methods: Arc::new(HashMap::new()), // ビルトインは空、ユーザー定義時は設定 - inner_content: Some(inner), // 統一内包 + inner_content: Some(inner), // 統一内包 base: BoxBase::new(), finalized: Arc::new(Mutex::new(false)), // レガシー互換フィールド @@ -62,20 +62,27 @@ impl InstanceBox { in_finalization: Arc::new(Mutex::new(false)), } } - + /// ユーザー定義Box専用コンストラクタ - pub fn from_declaration(class_name: String, fields: Vec, methods: HashMap) -> Self { + pub fn from_declaration( + class_name: String, + fields: Vec, + methods: HashMap, + ) -> Self { // Invalidate caches for this class since methods layout may change between runs crate::runtime::cache_versions::bump_version(&format!("BoxRef:{}", class_name)); let mut field_map = HashMap::new(); let mut legacy_field_map = HashMap::new(); - + // 両方のフィールドマップを初期化 for field in &fields { field_map.insert(field.clone(), NyashValue::Null); - legacy_field_map.insert(field.clone(), Arc::new(crate::box_trait::VoidBox::new()) as SharedNyashBox); + legacy_field_map.insert( + field.clone(), + Arc::new(crate::box_trait::VoidBox::new()) as SharedNyashBox, + ); } - + Self { class_name, fields_ng: Arc::new(Mutex::new(field_map)), @@ -90,19 +97,19 @@ impl InstanceBox { in_finalization: Arc::new(Mutex::new(false)), } } - + /// 🔄 レガシー互換性メソッド - 段階移行用 pub fn new(class_name: String, fields: Vec, methods: HashMap) -> Self { Self::from_declaration(class_name, fields, methods) } - + /// 🔄 レガシー互換性 - 高度なfiniシステムを簡素化して対応 pub fn new_with_box_info( - class_name: String, - fields: Vec, + class_name: String, + fields: Vec, methods: HashMap, init_field_order: Vec, - weak_fields: Vec + weak_fields: Vec, ) -> Self { let mut instance = Self::from_declaration(class_name, fields, methods); // レガシー互換:init順序とweak fieldsを設定 @@ -110,33 +117,33 @@ impl InstanceBox { instance.weak_fields_union = weak_fields.into_iter().collect(); instance } - + /// 🎯 統一フィールドアクセス(NyashValue版) pub fn get_field_ng(&self, field_name: &str) -> Option { self.fields_ng.lock().unwrap().get(field_name).cloned() } - + /// 🎯 統一フィールド設定(NyashValue版) pub fn set_field_ng(&self, field_name: String, value: NyashValue) -> Result<(), String> { self.fields_ng.lock().unwrap().insert(field_name, value); Ok(()) } - + /// 動的フィールド追加(GlobalBox用) pub fn set_field_dynamic(&self, field_name: String, value: NyashValue) { self.fields_ng.lock().unwrap().insert(field_name, value); } - + /// メソッド定義を取得 pub fn get_method(&self, method_name: &str) -> Option<&ASTNode> { self.methods.get(method_name) } - + /// メソッドが存在するかチェック pub fn has_method(&self, method_name: &str) -> bool { self.methods.contains_key(method_name) } - + /// メソッド動的追加(GlobalBox用) pub fn add_method(&mut self, method_name: String, method_ast: ASTNode) -> Result<(), String> { let mut new_methods = (*self.methods).clone(); @@ -144,7 +151,7 @@ impl InstanceBox { self.methods = Arc::new(new_methods); Ok(()) } - + /// 🎯 統一初期化処理 pub fn init(&mut self, _args: &[Box]) -> Result<(), String> { match &self.inner_content { @@ -156,47 +163,54 @@ impl InstanceBox { } } } - + /// 🎯 統一解放処理 pub fn fini(&self) -> Result<(), String> { let mut finalized = self.finalized.lock().unwrap(); if *finalized { return Ok(()); // 既に解放済み } - + // フィールドクリア self.fields_ng.lock().unwrap().clear(); - + *finalized = true; - eprintln!("🎯 fini(): Instance {} (ID: {}) finalized", self.class_name, self.base.id); + eprintln!( + "🎯 fini(): Instance {} (ID: {}) finalized", + self.class_name, self.base.id + ); Ok(()) } - + /// 解放済みかチェック pub fn is_finalized(&self) -> bool { *self.finalized.lock().unwrap() } - + // ========== レガシー互換メソッド (Phase 9.78e) ========== - + /// レガシー互換:統一フィールドアクセス pub fn get_field_unified(&self, field_name: &str) -> Option { self.get_field_ng(field_name) } - + /// レガシー互換:統一フィールド設定 pub fn set_field_unified(&self, field_name: String, value: NyashValue) -> Result<(), String> { self.set_field_ng(field_name, value) } - + /// レガシー互換:weak field設定 pub fn set_weak_field(&self, field_name: String, value: NyashValue) -> Result<(), String> { // 簡易実装:通常のフィールドとして保存 self.set_field_ng(field_name, value) } - + /// レガシー互換:weak field設定(Boxから) - pub fn set_weak_field_from_legacy(&self, field_name: String, legacy_box: Box) -> Result<(), String> { + pub fn set_weak_field_from_legacy( + &self, + field_name: String, + legacy_box: Box, + ) -> Result<(), String> { // 一時的にレガシーfieldsに保存する簡易実装 if let Some(ref fields) = self.fields { let arc_box: SharedNyashBox = Arc::from(legacy_box); @@ -206,12 +220,16 @@ impl InstanceBox { Err("Legacy fields not initialized".to_string()) } } - + /// レガシー互換:weak field取得 - pub fn get_weak_field(&self, field_name: &str, _interpreter: &NyashInterpreter) -> Option { + pub fn get_weak_field( + &self, + field_name: &str, + _interpreter: &NyashInterpreter, + ) -> Option { self.get_field_ng(field_name) } - + /// レガシー互換:レガシーフィールドアクセス pub fn get_field_legacy(&self, field_name: &str) -> Option { if let Some(fields) = &self.fields { @@ -220,54 +238,66 @@ impl InstanceBox { None } } - + /// レガシー互換:レガシーフィールド設定 pub fn set_field_legacy(&self, field_name: &str, value: SharedNyashBox) -> Result<(), String> { if let Some(fields) = &self.fields { - fields.lock().unwrap().insert(field_name.to_string(), value.clone()); - + fields + .lock() + .unwrap() + .insert(field_name.to_string(), value.clone()); + // fields_ngにも同期 // 一時的にNullを設定(型変換が複雑なため) // TODO: SharedNyashBox -> NyashValueの適切な変換を実装 - self.fields_ng.lock().unwrap().insert(field_name.to_string(), NyashValue::Null); - + self.fields_ng + .lock() + .unwrap() + .insert(field_name.to_string(), NyashValue::Null); + Ok(()) } else { Err("Legacy fields not initialized".to_string()) } } - + /// レガシー互換:動的フィールド設定 pub fn set_field_dynamic_legacy(&mut self, field_name: String, value: SharedNyashBox) { if self.fields.is_none() { self.fields = Some(Arc::new(Mutex::new(HashMap::new()))); } - + if let Some(fields) = &self.fields { - fields.lock().unwrap().insert(field_name.clone(), value.clone()); - + fields + .lock() + .unwrap() + .insert(field_name.clone(), value.clone()); + // fields_ngにも同期 // 一時的にNullを設定(型変換が複雑なため) // TODO: SharedNyashBox -> NyashValueの適切な変換を実装 - self.fields_ng.lock().unwrap().insert(field_name, NyashValue::Null); + self.fields_ng + .lock() + .unwrap() + .insert(field_name, NyashValue::Null); } } - + /// レガシー互換:weakフィールドチェック pub fn is_weak_field(&self, field_name: &str) -> bool { self.weak_fields_union.contains(field_name) } - + /// レガシー互換:weak参照無効化(簡易実装) pub fn invalidate_weak_references_to(&self, _target_info: &str) { // 簡易実装:何もしない } - + /// レガシー互換:グローバルweak参照無効化(簡易実装) pub fn global_invalidate_weak_references(_target_info: &str) { // 簡易実装:何もしない } - + /// レガシー互換:旧fields参照(直接参照用) pub fn get_fields(&self) -> Arc>> { if let Some(ref fields) = self.fields { @@ -277,7 +307,7 @@ impl InstanceBox { Arc::new(Mutex::new(HashMap::new())) } } - + /// レガシー互換:get_field(SharedNyashBoxを返す) pub fn get_field(&self, field_name: &str) -> Option { // まずレガシーfieldsをチェック @@ -286,7 +316,7 @@ impl InstanceBox { return Some(Arc::clone(value)); } } - + // fields_ngから取得して変換を試みる if let Some(_nyash_value) = self.fields_ng.lock().unwrap().get(field_name) { // NyashValue -> SharedNyashBox 変換(簡易実装) @@ -296,7 +326,7 @@ impl InstanceBox { None } } - + /// レガシー互換:set_field(SharedNyashBoxを受け取る) pub fn set_field(&self, field_name: &str, value: SharedNyashBox) -> Result<(), String> { self.set_field_legacy(field_name, value) @@ -308,7 +338,7 @@ impl NyashBox for InstanceBox { fn to_string_box(&self) -> StringBox { StringBox::new(format!("<{} instance #{}>", self.class_name, self.base.id)) } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_instance) = other.as_any().downcast_ref::() { BoolBox::new(self.base.id == other_instance.base.id) @@ -316,7 +346,7 @@ impl NyashBox for InstanceBox { BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { // 内包Boxがあれば、その型名を返す(ビルトインBox用) if let Some(inner) = &self.inner_content { @@ -328,7 +358,7 @@ impl NyashBox for InstanceBox { "InstanceBox" } } - + fn clone_box(&self) -> Box { Box::new(InstanceBox { class_name: self.class_name.clone(), @@ -344,12 +374,11 @@ impl NyashBox for InstanceBox { in_finalization: Arc::clone(&self.in_finalization), }) } - + fn share_box(&self) -> Box { // TODO: 正しいshare_boxセマンティクス実装 self.clone_box() } - } impl BoxCore for InstanceBox { @@ -364,11 +393,11 @@ impl BoxCore for InstanceBox { fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "<{} instance #{}>", self.class_name, self.base.id) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -388,38 +417,42 @@ mod tests { use crate::box_trait::{IntegerBox, NyashBox}; use std::sync::{Arc, Mutex}; type SharedNyashBox = Arc; - + #[test] fn test_from_any_box_creation() { let string_box = Box::new(crate::box_trait::StringBox::new("hello")); let instance = InstanceBox::from_any_box("StringBox".to_string(), string_box); - + assert_eq!(instance.class_name, "StringBox"); assert!(instance.inner_content.is_some()); assert!(instance.methods.is_empty()); // ビルトインは空 } - + #[test] fn test_from_declaration_creation() { let fields = vec!["x".to_string(), "y".to_string()]; let methods = HashMap::new(); let instance = InstanceBox::from_declaration("Point".to_string(), fields, methods); - + assert_eq!(instance.class_name, "Point"); assert!(instance.inner_content.is_none()); // ユーザー定義は内包なし - // フィールドが初期化されているかチェック + // フィールドが初期化されているかチェック assert!(instance.get_field("x").is_some()); assert!(instance.get_field("y").is_some()); } - + #[test] fn test_field_operations() { - let instance = InstanceBox::from_declaration("TestBox".to_string(), vec!["value".to_string()], HashMap::new()); - + let instance = InstanceBox::from_declaration( + "TestBox".to_string(), + vec!["value".to_string()], + HashMap::new(), + ); + // フィールド設定 let int_box: SharedNyashBox = Arc::new(IntegerBox::new(42)); instance.set_field("value", int_box.clone()).unwrap(); - + // フィールド取得 let field_value = instance.get_field("value").unwrap(); if let Some(int_box) = field_value.as_any().downcast_ref::() { @@ -428,25 +461,25 @@ mod tests { panic!("Expected IntegerBox"); } } - + #[test] fn test_unified_approach() { // ビルトインBox let string_instance = InstanceBox::from_any_box( - "StringBox".to_string(), - Box::new(crate::box_trait::StringBox::new("test")) + "StringBox".to_string(), + Box::new(crate::box_trait::StringBox::new("test")), ); - + // ユーザー定義Box let user_instance = InstanceBox::from_declaration( - "MyBox".to_string(), - vec!["field1".to_string()], - HashMap::new() + "MyBox".to_string(), + vec!["field1".to_string()], + HashMap::new(), ); - + // どちらも同じ型として扱える! let instances: Vec = vec![string_instance, user_instance]; - + for instance in instances { println!("Instance: {}", instance.class_name); // すべて Box として統一処理可能 diff --git a/src/interpreter/async_methods.rs b/src/interpreter/async_methods.rs index 10c6e4fc..76ecc207 100644 --- a/src/interpreter/async_methods.rs +++ b/src/interpreter/async_methods.rs @@ -1,12 +1,12 @@ /*! * Async Methods Module - * + * * Extracted from interpreter/box_methods.rs * Contains asynchronous Box type method implementations: - * + * * - execute_future_method (FutureBox) * - execute_channel_method (ChannelBox) - * + * * These methods handle asynchronous operations, futures, and * communication channels in the Nyash interpreter. */ @@ -17,15 +17,19 @@ use crate::channel_box::{ChannelBox, MessageBox}; impl NyashInterpreter { /// FutureBoxのメソッド呼び出しを実行 - /// + /// /// 非同期計算の結果を管理するFutureBoxの基本操作を提供します。 - /// + /// /// サポートメソッド: /// - get() -> 計算結果を取得 (ブロッキング) /// - ready() -> 計算完了状態をチェック /// - equals(other) -> 他のFutureBoxと比較 - pub(super) fn execute_future_method(&mut self, future_box: &FutureBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_future_method( + &mut self, + future_box: &FutureBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "get" => { if !arguments.is_empty() { @@ -54,35 +58,42 @@ impl NyashInterpreter { } _ => Err(RuntimeError::InvalidOperation { message: format!("Unknown method '{}' for FutureBox", method), - }) + }), } } /// ChannelBoxのメソッド呼び出しを実行 - /// + /// /// 非同期通信チャンネルを管理するChannelBoxの操作を提供します。 /// プロセス間通信やイベント駆動プログラミングに使用されます。 - /// + /// /// サポートメソッド: /// - sendMessage(content) -> メッセージを送信 /// - announce(content) -> ブロードキャスト送信 /// - toString() -> チャンネル情報を文字列化 /// - sender() -> 送信者情報を取得 /// - receiver() -> 受信者情報を取得 - pub(super) fn execute_channel_method(&mut self, channel_box: &ChannelBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_channel_method( + &mut self, + channel_box: &ChannelBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドを実行 match method { "sendMessage" => { if arg_values.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("sendMessage() expects 1 argument, got {}", arg_values.len()), + message: format!( + "sendMessage() expects 1 argument, got {}", + arg_values.len() + ), }); } // 簡易実装:メッセージを作成して返す @@ -97,12 +108,18 @@ impl NyashInterpreter { }); } let content = arg_values[0].to_string_box().value; - Ok(Box::new(StringBox::new(&format!("Broadcast from {}: {}", channel_box.sender_name, content)))) + Ok(Box::new(StringBox::new(&format!( + "Broadcast from {}: {}", + channel_box.sender_name, content + )))) } "toString" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("toString() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "toString() expects 0 arguments, got {}", + arg_values.len() + ), }); } Ok(Box::new(channel_box.to_string_box())) @@ -118,7 +135,10 @@ impl NyashInterpreter { "receiver" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("receiver() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "receiver() expects 0 arguments, got {}", + arg_values.len() + ), }); } Ok(channel_box.receiver()) @@ -129,4 +149,4 @@ impl NyashInterpreter { } } } -} \ No newline at end of file +} diff --git a/src/interpreter/box_methods.rs b/src/interpreter/box_methods.rs index 1ed040b8..9733721a 100644 --- a/src/interpreter/box_methods.rs +++ b/src/interpreter/box_methods.rs @@ -1,46 +1,46 @@ /*! * Box Method Handlers Module - * + * * Extracted from interpreter.rs lines 1389-2515 (1,126 lines) * Contains Box type-specific method implementations: - * + * * MOVED TO methods/basic_methods.rs: * - execute_string_method (StringBox) * - execute_integer_method (IntegerBox) * - execute_bool_method (BoolBox) - NEW * - execute_float_method (FloatBox) - NEW - * + * * MOVED TO methods/collection_methods.rs: * - execute_array_method (ArrayBox) * - execute_map_method (MapBox) - * + * * MOVED TO methods/io_methods.rs: * - execute_file_method (FileBox) * - execute_result_method (ResultBox) - * + * * MOVED TO methods/math_methods.rs: * - execute_math_method (MathBox) * - execute_random_method (RandomBox) - * + * * MOVED TO system_methods.rs: * - execute_time_method (TimeBox) * - execute_datetime_method (DateTimeBox) * - execute_timer_method (TimerBox) * - execute_debug_method (DebugBox) - * + * * MOVED TO async_methods.rs: * - execute_future_method (FutureBox) * - execute_channel_method (ChannelBox) - * + * * MOVED TO web_methods.rs: * - execute_web_display_method (WebDisplayBox) * - execute_web_console_method (WebConsoleBox) * - execute_web_canvas_method (WebCanvasBox) - * + * * MOVED TO special_methods.rs: * - execute_sound_method (SoundBox) * - execute_method_box_method (MethodBox) - * + * * REMAINING IN THIS MODULE: * - execute_console_method * - execute_null_method @@ -55,9 +55,9 @@ impl NyashInterpreter { // IntegerBox methods moved to methods/basic_methods.rs // ArrayBox methods moved to methods/collection_methods.rs - + // FileBox methods moved to methods/io_methods.rs - + // ResultBox methods moved to methods/io_methods.rs // FutureBox methods moved to async_methods.rs @@ -67,14 +67,18 @@ impl NyashInterpreter { // MathBox methods moved to methods/math_methods.rs /// NullBoxのメソッド呼び出しを実行 - pub(super) fn execute_null_method(&mut self, _null_box: &NullBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_null_method( + &mut self, + _null_box: &NullBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドを実行 match method { "is_null" => { @@ -88,7 +92,10 @@ impl NyashInterpreter { "is_not_null" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("is_not_null() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "is_not_null() expects 0 arguments, got {}", + arg_values.len() + ), }); } Ok(Box::new(BoolBox::new(false))) @@ -96,7 +103,10 @@ impl NyashInterpreter { "toString" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("toString() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "toString() expects 0 arguments, got {}", + arg_values.len() + ), }); } Ok(Box::new(StringBox::new("null".to_string()))) @@ -115,17 +125,18 @@ impl NyashInterpreter { "get_or_default" => { if arg_values.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("get_or_default() expects 1 argument, got {}", arg_values.len()), + message: format!( + "get_or_default() expects 1 argument, got {}", + arg_values.len() + ), }); } // nullの場合はデフォルト値を返す Ok(arg_values[0].clone_box()) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown NullBox method: {}", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown NullBox method: {}", method), + }), } } @@ -145,14 +156,18 @@ impl NyashInterpreter { /// EguiBoxのメソッド呼び出しを実行(非WASM環境のみ) #[cfg(all(feature = "gui", not(target_arch = "wasm32")))] - pub(super) fn execute_egui_method(&mut self, _egui_box: &crate::boxes::EguiBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_egui_method( + &mut self, + _egui_box: &crate::boxes::EguiBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドを実行 match method { "setTitle" => { @@ -185,23 +200,25 @@ impl NyashInterpreter { message: "EguiBox.run() must be called from main thread".to_string(), }) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown method '{}' for EguiBox", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for EguiBox", method), + }), } } /// ConsoleBoxのメソッド呼び出しを実行 - pub(super) fn execute_console_method(&mut self, console_box: &crate::boxes::console_box::ConsoleBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_console_method( + &mut self, + console_box: &crate::boxes::console_box::ConsoleBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドを実行 match method { "log" => { @@ -210,15 +227,16 @@ impl NyashInterpreter { message: "console.log() requires at least 1 argument".to_string(), }); } - + // 引数をすべて文字列に変換 - let messages: Vec = arg_values.iter() + let messages: Vec = arg_values + .iter() .map(|arg| arg.to_string_box().value) .collect(); - + let combined_message = messages.join(" "); console_box.log(&combined_message); - + Ok(Box::new(VoidBox::new())) } "warn" => { @@ -227,14 +245,15 @@ impl NyashInterpreter { message: "console.warn() requires at least 1 argument".to_string(), }); } - - let messages: Vec = arg_values.iter() + + let messages: Vec = arg_values + .iter() .map(|arg| arg.to_string_box().value) .collect(); - + let combined_message = messages.join(" "); console_box.warn(&combined_message); - + Ok(Box::new(VoidBox::new())) } "error" => { @@ -243,35 +262,37 @@ impl NyashInterpreter { message: "console.error() requires at least 1 argument".to_string(), }); } - - let messages: Vec = arg_values.iter() + + let messages: Vec = arg_values + .iter() .map(|arg| arg.to_string_box().value) .collect(); - + let combined_message = messages.join(" "); console_box.error(&combined_message); - + Ok(Box::new(VoidBox::new())) } "clear" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("console.clear() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "console.clear() expects 0 arguments, got {}", + arg_values.len() + ), }); } - + console_box.clear(); Ok(Box::new(VoidBox::new())) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown ConsoleBox method: {}", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown ConsoleBox method: {}", method), + }), } } // MethodBox methods moved to special_methods.rs - + // Web methods moved to web_methods.rs -} \ No newline at end of file +} diff --git a/src/interpreter/calls.rs b/src/interpreter/calls.rs index 48b9838f..acf96df7 100644 --- a/src/interpreter/calls.rs +++ b/src/interpreter/calls.rs @@ -1,8 +1,8 @@ //! Call helpers: centralizes call paths (PluginHost, functions) +use super::{NyashInterpreter, RuntimeError}; use crate::ast::ASTNode; use crate::box_trait::{NyashBox, VoidBox}; -use super::{NyashInterpreter, RuntimeError}; #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] use crate::runtime::plugin_loader_v2::PluginBoxV2; @@ -17,18 +17,31 @@ impl NyashInterpreter { arg_nodes: &[ASTNode], ) -> Result, RuntimeError> { if plugin_box.is_finalized() { - return Err(RuntimeError::RuntimeFailure { message: format!("Use after fini: {}", plugin_box.box_type) }); + return Err(RuntimeError::RuntimeFailure { + message: format!("Use after fini: {}", plugin_box.box_type), + }); } let mut arg_values: Vec> = Vec::new(); for arg in arg_nodes { arg_values.push(self.execute_expression(arg)?); } let host_guard = crate::runtime::get_global_plugin_host(); - let host = host_guard.read().map_err(|_| RuntimeError::RuntimeFailure { message: "Plugin host lock poisoned".into() })?; - match host.invoke_instance_method(&plugin_box.box_type, method, plugin_box.instance_id(), &arg_values) { + let host = host_guard + .read() + .map_err(|_| RuntimeError::RuntimeFailure { + message: "Plugin host lock poisoned".into(), + })?; + match host.invoke_instance_method( + &plugin_box.box_type, + method, + plugin_box.instance_id(), + &arg_values, + ) { Ok(Some(result_box)) => Ok(result_box), Ok(None) => Ok(Box::new(VoidBox::new())), - Err(e) => Err(RuntimeError::RuntimeFailure { message: format!("Plugin method {} failed: {:?}", method, e) }), + Err(e) => Err(RuntimeError::RuntimeFailure { + message: format!("Plugin method {} failed: {:?}", method, e), + }), } } @@ -44,9 +57,15 @@ impl NyashInterpreter { arg_values.push(self.execute_expression(arg)?); } let host_guard = crate::runtime::get_global_plugin_host(); - let host = host_guard.read().map_err(|_| RuntimeError::RuntimeFailure { message: "Plugin host lock poisoned".into() })?; + let host = host_guard + .read() + .map_err(|_| RuntimeError::RuntimeFailure { + message: "Plugin host lock poisoned".into(), + })?; host.create_box(box_type, &arg_values) - .map_err(|e| RuntimeError::InvalidOperation { message: format!("Failed to construct plugin '{}': {:?}", box_type, e) }) + .map_err(|e| RuntimeError::InvalidOperation { + message: format!("Failed to construct plugin '{}': {:?}", box_type, e), + }) } /// Check if a given box type is provided by plugins (per current config). diff --git a/src/interpreter/core.rs b/src/interpreter/core.rs index 105ac30a..b6e28074 100644 --- a/src/interpreter/core.rs +++ b/src/interpreter/core.rs @@ -1,29 +1,29 @@ /*! * Nyash Interpreter - Rust Implementation - * + * * Python版nyashc_v4.pyのインタープリターをRustで完全再実装 * Everything is Box哲学に基づくAST実行エンジン */ -use crate::ast::ASTNode; -use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox, SharedNyashBox}; -use crate::instance_v2::InstanceBox; use super::BuiltinStdlib; -use crate::runtime::{NyashRuntime, NyashRuntimeBuilder}; -use crate::box_factory::BoxFactory; -use std::sync::{Arc, Mutex}; -use std::collections::{HashMap, HashSet}; -use super::{ControlFlow, ConstructorContext, StaticBoxDefinition, StaticBoxState}; +use super::{ConstructorContext, ControlFlow, StaticBoxDefinition, StaticBoxState}; use super::{RuntimeError, SharedState}; +use crate::ast::ASTNode; +use crate::box_factory::BoxFactory; +use crate::box_trait::{BoolBox, IntegerBox, NyashBox, SharedNyashBox, StringBox, VoidBox}; +use crate::instance_v2::InstanceBox; +use crate::runtime::{NyashRuntime, NyashRuntimeBuilder}; +use std::collections::{HashMap, HashSet}; use std::fs::OpenOptions; use std::io::Write; +use std::sync::{Arc, Mutex}; // ファイルロガー(expressions.rsと同じ) pub(crate) fn debug_log(msg: &str) { if let Ok(mut file) = OpenOptions::new() .create(true) .append(true) - .open("/mnt/c/git/nyash/development/debug_hang_issue/debug_trace.log") + .open("/mnt/c/git/nyash/development/debug_hang_issue/debug_trace.log") { let _ = writeln!(file, "{}", msg); let _ = file.flush(); @@ -44,31 +44,30 @@ macro_rules! idebug { }; } - /// Nyashインタープリター - AST実行エンジン pub struct NyashInterpreter { /// 共有状態(スレッド間で共有) pub(super) shared: SharedState, - + /// 📦 local変数スタック(関数呼び出し時の一時変数) pub(super) local_vars: HashMap, - + /// 📤 outbox変数スタック(static関数内の所有権移転変数) pub(super) outbox_vars: HashMap, - + /// 制御フロー状態 pub(super) control_flow: ControlFlow, - + /// 現在実行中のコンストラクタ情報 pub(super) current_constructor_context: Option, - + /// 🔄 評価スタック - 循環参照検出用 #[allow(dead_code)] pub(super) evaluation_stack: Vec, - + /// 🔗 Invalidated object IDs for weak reference system pub invalidated_ids: Arc>>, - + /// 📚 組み込み標準ライブラリ pub(super) stdlib: Option, @@ -125,7 +124,9 @@ impl NyashInterpreter { if std::path::Path::new("nyash.toml").exists() { let needs_init = { let host = crate::runtime::get_global_plugin_host(); - host.read().map(|h| h.config_ref().is_none()).unwrap_or(true) + host.read() + .map(|h| h.config_ref().is_none()) + .unwrap_or(true) }; if needs_init { let _ = crate::runtime::init_global_plugin_host("nyash.toml"); @@ -137,11 +138,19 @@ impl NyashInterpreter { std::env::set_var("NYASH_USE_PLUGIN_BUILTINS", "1"); } // Merge override list with FileBox/TOMLBox only (safe defaults for interpreter flows) - let mut override_types: Vec = 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() - } else { vec![] }; + let mut override_types: Vec = + 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() + } else { + vec![] + }; for t in ["FileBox", "TOMLBox"] { - if !override_types.iter().any(|x| x == t) { override_types.push(t.to_string()); } + if !override_types.iter().any(|x| x == t) { + override_types.push(t.to_string()); + } } std::env::set_var("NYASH_PLUGIN_OVERRIDE_TYPES", override_types.join(",")); } @@ -151,8 +160,13 @@ impl NyashInterpreter { } /// 互換API: 旧BuiltinGroupsを受け取るコンストラクタ(無視して new() 相当) - pub fn new_with_groups(_groups: T) -> Self where T: core::fmt::Debug { Self::new() } - + pub fn new_with_groups(_groups: T) -> Self + where + T: core::fmt::Debug, + { + Self::new() + } + /// 共有状態から新しいインタープリターを作成(非同期実行用) pub fn with_shared(shared: SharedState) -> Self { // 共有状態に紐づいたランタイムを先に構築 @@ -188,8 +202,12 @@ impl NyashInterpreter { } /// 互換API: 共有状態+旧BuiltinGroups(無視) - pub fn with_shared_and_groups(shared: SharedState, _groups: T) -> Self where T: core::fmt::Debug { Self::with_shared(shared) } - + pub fn with_shared_and_groups(shared: SharedState, _groups: T) -> Self + where + T: core::fmt::Debug, + { + Self::with_shared(shared) + } /// Register an additional BoxFactory into this interpreter's runtime registry. /// This allows tests or embedders to inject custom factories without globals. @@ -198,14 +216,16 @@ impl NyashInterpreter { reg.register(factory); } } - - + // ========== 🌍 GlobalBox変数解決システム ========== - + /// 革命的変数解決: local変数 → GlobalBoxフィールド → エラー pub(super) fn resolve_variable(&self, name: &str) -> Result { - let log_msg = format!("resolve_variable: name='{}', local_vars={:?}", - name, self.local_vars.keys().collect::>()); + let log_msg = format!( + "resolve_variable: name='{}', local_vars={:?}", + name, + self.local_vars.keys().collect::>() + ); debug_log(&log_msg); // 1. outbox変数を最初にチェック(static関数内で優先) if let Some(outbox_value) = self.outbox_vars.get(name) { @@ -213,80 +233,87 @@ impl NyashInterpreter { let shared_value = Arc::clone(outbox_value); return Ok(shared_value); } - + // 2. local変数をチェック if let Some(local_value) = self.local_vars.get(name) { // 🔧 修正:clone_box() → Arc::clone() で参照共有 let shared_value = Arc::clone(local_value); return Ok(shared_value); } - + // 3. GlobalBoxのフィールドをチェック let global_box = self.shared.global_box.lock().unwrap(); if let Some(field_value) = global_box.get_field(name) { return Ok(field_value); } - + // 4. statics名前空間内のstatic boxをチェック if let Some(statics_namespace) = global_box.get_field("statics") { - // MapBoxとして試す - if let Some(map_box) = statics_namespace.as_any().downcast_ref::() { + if let Some(map_box) = statics_namespace + .as_any() + .downcast_ref::() + { let key_box: Box = Box::new(StringBox::new(name)); let static_box_result = map_box.get(key_box); - + // NullBoxでないかチェック(MapBoxは見つからない場合NullBoxを返す) if static_box_result.type_name() != "NullBox" { return Ok(Arc::from(static_box_result)); } - } else if let Some(instance) = statics_namespace.as_any().downcast_ref::() { + } else if let Some(instance) = statics_namespace.as_any().downcast_ref::() + { if let Some(static_box) = instance.get_field(name) { return Ok(static_box); } } } - + drop(global_box); // lockを解放してからstdlibチェック - - // 5. nyashstd標準ライブラリ名前空間をチェック + + // 5. nyashstd標準ライブラリ名前空間をチェック if let Some(ref stdlib) = self.stdlib { if let Some(nyashstd_namespace) = stdlib.namespaces.get("nyashstd") { if let Some(_static_box) = nyashstd_namespace.static_boxes.get(name) { // BuiltinStaticBoxをInstanceBoxとしてラップ let static_instance = InstanceBox::new( format!("{}_builtin", name), - vec![], // フィールドなし + vec![], // フィールドなし HashMap::new(), // メソッドは動的に解決される ); - + return Ok(Arc::new(static_instance)); } } } - + // 6. エラー:見つからない idebug!("🔍 DEBUG: '{}' not found anywhere!", name); Err(RuntimeError::UndefinedVariable { name: name.to_string(), }) } - + /// 🔥 厳密変数設定: 明示的宣言のみ許可 - Everything is Box哲学 - pub(crate) fn set_variable(&mut self, name: &str, value: Box) -> Result<(), RuntimeError> { + pub(crate) fn set_variable( + &mut self, + name: &str, + value: Box, + ) -> Result<(), RuntimeError> { let shared_value = Arc::from(value); // Convert Box to Arc - + // 1. outbox変数が存在する場合は更新 if self.outbox_vars.contains_key(name) { self.outbox_vars.insert(name.to_string(), shared_value); return Ok(()); } - + // 2. local変数が存在する場合は更新 if self.local_vars.contains_key(name) { self.local_vars.insert(name.to_string(), shared_value); return Ok(()); } - + // 3. GlobalBoxのフィールドが既に存在する場合は更新 { let global_box = self.shared.global_box.lock().unwrap(); @@ -297,7 +324,7 @@ impl NyashInterpreter { return Ok(()); } } - + // 4. グローバル変数として新規作成(従来の緩い挙動に合わせる) { let mut global_box = self.shared.global_box.lock().unwrap(); @@ -305,7 +332,7 @@ impl NyashInterpreter { } Ok(()) } - + /// local変数を宣言(関数内でのみ有効) pub(crate) fn declare_local_variable(&mut self, name: &str, value: Box) { // Pass-by-share for plugin handle types; by-value (clone) semantics can be applied at call sites @@ -313,40 +340,54 @@ impl NyashInterpreter { let mut store_value = value; #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] { - if store_value.as_any().downcast_ref::().is_some() { + if store_value + .as_any() + .downcast_ref::() + .is_some() + { store_value = store_value.share_box(); } } - self.local_vars.insert(name.to_string(), Arc::from(store_value)); + self.local_vars + .insert(name.to_string(), Arc::from(store_value)); } - + /// outbox変数を宣言(static関数内で所有権移転) pub(crate) fn declare_outbox_variable(&mut self, name: &str, value: Box) { #[allow(unused_mut)] let mut store_value = value; #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] { - if store_value.as_any().downcast_ref::().is_some() { + if store_value + .as_any() + .downcast_ref::() + .is_some() + { store_value = store_value.share_box(); } } - self.outbox_vars.insert(name.to_string(), Arc::from(store_value)); + self.outbox_vars + .insert(name.to_string(), Arc::from(store_value)); } - + /// local変数スタックを保存・復元(関数呼び出し時) pub(super) fn save_local_vars(&self) -> HashMap> { - self.local_vars.iter() + self.local_vars + .iter() .map(|(k, v)| { let b: &dyn NyashBox = &**v; #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] - if b.as_any().downcast_ref::().is_some() { + if b.as_any() + .downcast_ref::() + .is_some() + { return (k.clone(), b.share_box()); } (k.clone(), b.clone_box()) }) .collect() } - + pub(super) fn restore_local_vars(&mut self, saved: HashMap>) { // 🎯 スコープ離脱時:現在のローカル変数に対してfiniを呼ぶ // ただし「me」は特別扱い(インスタンス自身なのでfiniしない) @@ -355,72 +396,85 @@ impl NyashInterpreter { if name == "me" { continue; } - + // ユーザー定義Box(InstanceBox)の場合 if let Some(instance) = (**value).as_any().downcast_ref::() { let _ = instance.fini(); - idebug!("🔄 Scope exit: Called fini() on local variable '{}' (InstanceBox)", name); + idebug!( + "🔄 Scope exit: Called fini() on local variable '{}' (InstanceBox)", + name + ); } // プラグインBoxは共有ハンドルの可能性が高いため自動finiしない(明示呼び出しのみ) // ビルトインBoxは元々finiメソッドを持たないので呼ばない // (StringBox、IntegerBox等はリソース管理不要) } - + // その後、保存されていた変数で復元 - self.local_vars = saved.into_iter() - .map(|(k, v)| (k, Arc::from(v))) // Convert Box to Arc + self.local_vars = saved + .into_iter() + .map(|(k, v)| (k, Arc::from(v))) // Convert Box to Arc .collect(); } - + /// outbox変数スタックを保存・復元(static関数呼び出し時) pub(super) fn save_outbox_vars(&self) -> HashMap> { - self.outbox_vars.iter() + self.outbox_vars + .iter() .map(|(k, v)| { let b: &dyn NyashBox = &**v; #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] - if b.as_any().downcast_ref::().is_some() { + if b.as_any() + .downcast_ref::() + .is_some() + { return (k.clone(), b.share_box()); } (k.clone(), b.clone_box()) }) .collect() } - + pub(super) fn restore_outbox_vars(&mut self, saved: HashMap>) { // 🎯 スコープ離脱時:現在のoutbox変数に対してもfiniを呼ぶ for (name, value) in &self.outbox_vars { // ユーザー定義Box(InstanceBox)の場合 if let Some(instance) = (**value).as_any().downcast_ref::() { let _ = instance.fini(); - idebug!("🔄 Scope exit: Called fini() on outbox variable '{}' (InstanceBox)", name); + idebug!( + "🔄 Scope exit: Called fini() on outbox variable '{}' (InstanceBox)", + name + ); } // プラグインBoxは共有ハンドルの可能性が高いため自動finiしない // ビルトインBoxは元々finiメソッドを持たないので呼ばない(要修正) } - + // その後、保存されていた変数で復元 - self.outbox_vars = saved.into_iter() - .map(|(k, v)| (k, Arc::from(v))) // Convert Box to Arc + self.outbox_vars = saved + .into_iter() + .map(|(k, v)| (k, Arc::from(v))) // Convert Box to Arc .collect(); } - + /// トップレベル関数をGlobalBoxのメソッドとして登録 - 🔥 暗黙オーバーライド禁止対応 - pub(super) fn register_global_function(&mut self, name: String, func_ast: ASTNode) -> Result<(), RuntimeError> { + pub(super) fn register_global_function( + &mut self, + name: String, + func_ast: ASTNode, + ) -> Result<(), RuntimeError> { let mut global_box = self.shared.global_box.lock().unwrap(); - global_box.add_method(name, func_ast) + global_box + .add_method(name, func_ast) .map_err(|e| RuntimeError::InvalidOperation { message: e })?; Ok(()) } - - - - - + /// 値が真と評価されるかチェック pub(super) fn is_truthy(&self, value: &Box) -> bool { #[allow(unused_imports)] use std::any::Any; - + if let Some(bool_box) = value.as_any().downcast_ref::() { bool_box.value } else if let Some(int_box) = value.as_any().downcast_ref::() { @@ -433,11 +487,11 @@ impl NyashInterpreter { true // 他のBoxは真とみなす } } - + /// 🌍 革命的変数取得(テスト用):GlobalBoxのフィールドから取得 pub fn get_variable(&self, name: &str) -> Result, RuntimeError> { let shared_var = self.resolve_variable(name)?; - Ok((*shared_var).clone_box()) // Convert Arc back to Box for external interface + Ok((*shared_var).clone_box()) // Convert Arc back to Box for external interface } } @@ -448,9 +502,13 @@ pub(crate) fn run_function_box( ) -> Result, RuntimeError> { use crate::box_trait::{NyashBox, VoidBox}; if args.len() != fun.params.len() { - return Err(RuntimeError::InvalidOperation { message: format!( - "Function expects {} args, got {}", fun.params.len(), args.len() - )}); + return Err(RuntimeError::InvalidOperation { + message: format!( + "Function expects {} args, got {}", + fun.params.len(), + args.len() + ), + }); } let mut interp = NyashInterpreter::new(); @@ -482,7 +540,10 @@ pub(crate) fn run_function_box( break; } } - Err(e) => { crate::runtime::global_hooks::pop_task_scope(); return Err(e); } + Err(e) => { + crate::runtime::global_hooks::pop_task_scope(); + return Err(e); + } } } crate::runtime::global_hooks::pop_task_scope(); @@ -495,36 +556,36 @@ pub(crate) fn run_function_box( mod tests { use super::*; use crate::parser::NyashParser; - + #[test] fn test_simple_execution() { let code = r#" x = 42 print(x) "#; - + let ast = NyashParser::parse_from_string(code).unwrap(); let mut interpreter = NyashInterpreter::new(); let result = interpreter.execute(ast); - + assert!(result.is_ok()); } - + #[test] fn test_arithmetic() { let code = r#" result = 10 + 32 "#; - + let ast = NyashParser::parse_from_string(code).unwrap(); let mut interpreter = NyashInterpreter::new(); interpreter.execute(ast).unwrap(); - + // 🌍 革命的変数取得:GlobalBoxから let result = interpreter.get_variable("result").unwrap(); assert_eq!(result.to_string_box().value, "42"); } - + #[test] fn test_if_statement() { let code = r#" @@ -535,16 +596,16 @@ mod tests { y = "failure" } "#; - + let ast = NyashParser::parse_from_string(code).unwrap(); let mut interpreter = NyashInterpreter::new(); interpreter.execute(ast).unwrap(); - + // 🌍 革命的変数取得:GlobalBoxから let result = interpreter.get_variable("y").unwrap(); assert_eq!(result.to_string_box().value, "success"); } - + #[test] fn test_box_instance_creation() { let code = r#" @@ -564,15 +625,15 @@ mod tests { obj.value = "test123" result = obj.getValue() "#; - + let ast = NyashParser::parse_from_string(code).unwrap(); let mut interpreter = NyashInterpreter::new(); interpreter.execute(ast).unwrap(); - + // 🌍 革命的変数取得:インスタンス作成確認 let obj = interpreter.get_variable("obj").unwrap(); assert!(obj.as_any().downcast_ref::().is_some()); - + // 🌍 革命的変数取得:メソッド呼び出し結果確認 let result = interpreter.get_variable("result").unwrap(); assert_eq!(result.to_string_box().value, "test123"); @@ -582,93 +643,107 @@ mod tests { // ===== 🔥 Static Box管理システム ===== impl NyashInterpreter { - /// Static Box定義を登録 - pub fn register_static_box(&mut self, definition: StaticBoxDefinition) -> Result<(), RuntimeError> { - let mut definitions = self.shared.static_box_definitions.write() - .map_err(|_| RuntimeError::RuntimeFailure { - message: "Failed to acquire write lock for static box definitions".to_string() - })?; - + pub fn register_static_box( + &mut self, + definition: StaticBoxDefinition, + ) -> Result<(), RuntimeError> { + let mut definitions = self.shared.static_box_definitions.write().map_err(|_| { + RuntimeError::RuntimeFailure { + message: "Failed to acquire write lock for static box definitions".to_string(), + } + })?; + definitions.insert(definition.name.clone(), definition); Ok(()) } - + /// Static Box宣言を登録(AST処理から呼ばれる) pub fn register_static_box_declaration( - &mut self, + &mut self, name: String, fields: Vec, methods: HashMap, init_fields: Vec, - weak_fields: Vec, // 🔗 weak修飾子が付いたフィールドのリスト + weak_fields: Vec, // 🔗 weak修飾子が付いたフィールドのリスト static_init: Option>, - extends: Vec, // 🚀 Multi-delegation: Changed from Option to Vec + extends: Vec, // 🚀 Multi-delegation: Changed from Option to Vec implements: Vec, - type_parameters: Vec + type_parameters: Vec, ) -> Result<(), RuntimeError> { // 🌍 Static Box定義時にstatics名前空間を確実に作成 self.ensure_statics_namespace()?; - + let definition = StaticBoxDefinition { name: name.clone(), fields, methods, init_fields, - weak_fields, // 🔗 Add weak_fields to static box definition + weak_fields, // 🔗 Add weak_fields to static box definition static_init, extends, implements, type_parameters, initialization_state: StaticBoxState::NotInitialized, }; - - idebug!("🔥 Static Box '{}' definition registered in statics namespace", name); + + idebug!( + "🔥 Static Box '{}' definition registered in statics namespace", + name + ); self.register_static_box(definition) } - + /// Static Boxの初期化を実行(遅延初期化) pub fn ensure_static_box_initialized(&mut self, name: &str) -> Result<(), RuntimeError> { // 1. 定義を取得 let definition = { - let definitions = self.shared.static_box_definitions.read() - .map_err(|_| RuntimeError::RuntimeFailure { - message: "Failed to acquire read lock for static box definitions".to_string() - })?; - + let definitions = self.shared.static_box_definitions.read().map_err(|_| { + RuntimeError::RuntimeFailure { + message: "Failed to acquire read lock for static box definitions".to_string(), + } + })?; + match definitions.get(name) { Some(def) => def.clone(), - None => return Err(RuntimeError::UndefinedClass { name: name.to_string() }), + None => { + return Err(RuntimeError::UndefinedClass { + name: name.to_string(), + }) + } } }; - + // 2. 初期化状態をチェック if definition.initialization_state == StaticBoxState::Initialized { return Ok(()); // 既に初期化済み } - + if definition.initialization_state == StaticBoxState::Initializing { return Err(RuntimeError::RuntimeFailure { - message: format!("Circular dependency detected during initialization of static box '{}'", name) + message: format!( + "Circular dependency detected during initialization of static box '{}'", + name + ), }); } - + // 3. 初期化開始をマーク self.set_static_box_state(name, StaticBoxState::Initializing)?; - + // 4. 「statics」名前空間をGlobalBoxに作成(未存在の場合) self.ensure_statics_namespace()?; - + // 5. シングルトンインスタンスを作成(メソッドも含む) let singleton = InstanceBox::new( format!("{}_singleton", name), definition.init_fields.clone(), definition.methods.clone(), // ★ メソッドを正しく設定 ); - + // 6. GlobalBox.staticsに登録 self.set_static_instance(name, singleton)?; - + // 7. static初期化ブロックを実行(me変数をバインドして) if let Some(ref init_statements) = definition.static_init { // statics名前空間からシングルトンインスタンスを取得 @@ -678,99 +753,116 @@ impl NyashInterpreter { let statics_instance = statics_box.as_any().downcast_ref::().unwrap(); statics_instance.get_field(name).unwrap() }; - + // 🌍 this変数をバインドしてstatic初期化実行(me構文のため) self.declare_local_variable("me", (*static_instance).clone_or_share()); - + for stmt in init_statements { self.execute_statement(stmt)?; } - + // 🌍 this変数をクリーンアップ self.local_vars.remove("me"); } - + // 8. 初期化完了をマーク self.set_static_box_state(name, StaticBoxState::Initialized)?; - + Ok(()) } - + /// Static Box初期化状態を設定 - fn set_static_box_state(&mut self, name: &str, state: StaticBoxState) -> Result<(), RuntimeError> { - let mut definitions = self.shared.static_box_definitions.write() - .map_err(|_| RuntimeError::RuntimeFailure { - message: "Failed to acquire write lock for static box definitions".to_string() - })?; - + fn set_static_box_state( + &mut self, + name: &str, + state: StaticBoxState, + ) -> Result<(), RuntimeError> { + let mut definitions = self.shared.static_box_definitions.write().map_err(|_| { + RuntimeError::RuntimeFailure { + message: "Failed to acquire write lock for static box definitions".to_string(), + } + })?; + if let Some(definition) = definitions.get_mut(name) { definition.initialization_state = state; } - + Ok(()) } - + /// 「statics」名前空間をGlobalBoxに作成 fn ensure_statics_namespace(&mut self) -> Result<(), RuntimeError> { - let global_box = self.shared.global_box.lock() - .map_err(|_| RuntimeError::RuntimeFailure { - message: "Failed to acquire global box lock".to_string() - })?; - + let global_box = + self.shared + .global_box + .lock() + .map_err(|_| RuntimeError::RuntimeFailure { + message: "Failed to acquire global box lock".to_string(), + })?; + // 既に存在する場合はスキップ if global_box.get_field("statics").is_some() { idebug!("🌍 statics namespace already exists - skipping creation"); return Ok(()); } - + // 「statics」用のInstanceBoxを作成 - let statics_box = InstanceBox::new( - "statics".to_string(), - vec![], - HashMap::new(), - ); - + let statics_box = InstanceBox::new("statics".to_string(), vec![], HashMap::new()); + // GlobalBoxのfieldsに直接挿入 { let fields = global_box.get_fields(); let mut fields_locked = fields.lock().unwrap(); fields_locked.insert("statics".to_string(), Arc::new(statics_box)); } - + idebug!("🌍 statics namespace created in GlobalBox successfully"); Ok(()) } - + /// Static Boxシングルトンインスタンスを設定 - fn set_static_instance(&mut self, name: &str, instance: InstanceBox) -> Result<(), RuntimeError> { - let global_box = self.shared.global_box.lock() - .map_err(|_| RuntimeError::RuntimeFailure { - message: "Failed to acquire global box lock".to_string() - })?; - + fn set_static_instance( + &mut self, + name: &str, + instance: InstanceBox, + ) -> Result<(), RuntimeError> { + let global_box = + self.shared + .global_box + .lock() + .map_err(|_| RuntimeError::RuntimeFailure { + message: "Failed to acquire global box lock".to_string(), + })?; + // statics名前空間を取得 - let statics_box = global_box.get_field("statics") + let statics_box = global_box + .get_field("statics") .ok_or(RuntimeError::TypeError { - message: "statics namespace not found in GlobalBox".to_string() + message: "statics namespace not found in GlobalBox".to_string(), })?; - - let statics_instance = statics_box.as_any() - .downcast_ref::() - .ok_or(RuntimeError::TypeError { - message: "statics field is not an InstanceBox".to_string() - })?; - + + let statics_instance = + statics_box + .as_any() + .downcast_ref::() + .ok_or(RuntimeError::TypeError { + message: "statics field is not an InstanceBox".to_string(), + })?; + // statics InstanceBoxのfieldsに直接挿入(動的フィールド追加) { let fields = statics_instance.get_fields(); let mut fields_locked = fields.lock().unwrap(); fields_locked.insert(name.to_string(), Arc::new(instance)); } - - idebug!("🔥 Static box '{}' instance registered in statics namespace", name); + + idebug!( + "🔥 Static box '{}' instance registered in statics namespace", + name + ); Ok(()) } - + /// 🔥 Static Boxかどうかをチェック pub(super) fn is_static_box(&self, name: &str) -> bool { if let Ok(definitions) = self.shared.static_box_definitions.read() { @@ -779,11 +871,11 @@ impl NyashInterpreter { false } } - + /// 🔗 Trigger weak reference invalidation (expert-validated implementation) pub(super) fn trigger_weak_reference_invalidation(&mut self, target_info: &str) { idebug!("🔗 DEBUG: Registering invalidation for: {}", target_info); - + // Extract actual object ID from target_info string // Format: "" -> extract ID if let Some(hash_pos) = target_info.find('#') { @@ -791,7 +883,7 @@ impl NyashInterpreter { // Find the end of the ID (before '>') let id_end = id_str.find('>').unwrap_or(id_str.len()); let clean_id_str = &id_str[..id_end]; - + if let Ok(id) = clean_id_str.parse::() { self.invalidated_ids.lock().unwrap().insert(id); idebug!("🔗 DEBUG: Object with ID {} marked as invalidated", id); @@ -810,28 +902,49 @@ impl NyashInterpreter { } // ==== MethodBox Invoker Bridge ========================================== fn register_methodbox_invoker() { - use crate::method_box::{MethodBox, MethodInvoker, FunctionDefinition, set_method_invoker}; - use crate::box_trait::{VoidBox}; + use crate::box_trait::VoidBox; + use crate::method_box::{set_method_invoker, FunctionDefinition, MethodBox, MethodInvoker}; use std::sync::Arc; struct SimpleMethodInvoker; impl MethodInvoker for SimpleMethodInvoker { - fn invoke(&self, method: &MethodBox, args: Vec>) -> Result, String> { + fn invoke( + &self, + method: &MethodBox, + args: Vec>, + ) -> Result, String> { // 1) 取得: メソッド定義 let def: FunctionDefinition = if let Some(def) = &method.method_def { def.clone() } else { let inst_guard = method.get_instance(); - let inst_locked = inst_guard.lock().map_err(|_| "MethodBox instance lock poisoned".to_string())?; + let inst_locked = inst_guard + .lock() + .map_err(|_| "MethodBox instance lock poisoned".to_string())?; if let Some(inst) = inst_locked.as_any().downcast_ref::() { if let Some(ast) = inst.get_method(&method.method_name) { - if let ASTNode::FunctionDeclaration { name, params, body, is_static, .. } = ast { - FunctionDefinition { name: name.clone(), params: params.clone(), body: body.clone(), is_static: *is_static } + if let ASTNode::FunctionDeclaration { + name, + params, + body, + is_static, + .. + } = ast + { + FunctionDefinition { + name: name.clone(), + params: params.clone(), + body: body.clone(), + is_static: *is_static, + } } else { return Err("Method AST is not a function declaration".to_string()); } } else { - return Err(format!("Method '{}' not found on instance", method.method_name)); + return Err(format!( + "Method '{}' not found on instance", + method.method_name + )); } } else { return Err("MethodBox instance is not an InstanceBox".to_string()); @@ -843,14 +956,20 @@ fn register_methodbox_invoker() { // me = instance let me_box = { let inst_guard = method.get_instance(); - let inst_locked = inst_guard.lock().map_err(|_| "MethodBox instance lock poisoned".to_string())?; + let inst_locked = inst_guard + .lock() + .map_err(|_| "MethodBox instance lock poisoned".to_string())?; inst_locked.clone_or_share() }; interp.declare_local_variable("me", me_box); // 引数をローカルへ if def.params.len() != args.len() { - return Err(format!("Argument mismatch: expected {}, got {}", def.params.len(), args.len())); + return Err(format!( + "Argument mismatch: expected {}, got {}", + def.params.len(), + args.len() + )); } for (p, v) in def.params.iter().zip(args.into_iter()) { interp.declare_local_variable(p, v); diff --git a/src/interpreter/errors.rs b/src/interpreter/errors.rs index 2d4df83d..b02ba4eb 100644 --- a/src/interpreter/errors.rs +++ b/src/interpreter/errors.rs @@ -36,7 +36,6 @@ pub enum RuntimeError { EnvironmentError(String), // === 🔥 Enhanced Errors with Span Information === - #[error("Undefined variable '{name}' at {span}")] UndefinedVariableAt { name: String, span: Span }, diff --git a/src/interpreter/eval.rs b/src/interpreter/eval.rs index e66ce504..a5b3b788 100644 --- a/src/interpreter/eval.rs +++ b/src/interpreter/eval.rs @@ -1,8 +1,8 @@ //! Evaluation entry points: execute program and nodes +use super::{ControlFlow, NyashInterpreter, RuntimeError}; use crate::ast::ASTNode; use crate::box_trait::{NyashBox, VoidBox}; -use super::{NyashInterpreter, RuntimeError, ControlFlow}; impl NyashInterpreter { /// ASTを実行 @@ -73,22 +73,41 @@ impl NyashInterpreter { if params.len() == 1 { // Try to read script args from env (JSON array); fallback to empty ArrayBox if let Ok(json) = std::env::var("NYASH_SCRIPT_ARGS_JSON") { - if let Ok(vals) = serde_json::from_str::>(&json) { - let mut str_nodes: Vec = Vec::with_capacity(vals.len()); + if let Ok(vals) = + serde_json::from_str::>(&json) + { + let mut str_nodes: Vec = + Vec::with_capacity(vals.len()); for s in vals { - str_nodes.push(ASTNode::Literal { value: crate::ast::LiteralValue::String(s), span: crate::ast::Span::unknown() }); + str_nodes.push(ASTNode::Literal { + value: crate::ast::LiteralValue::String(s), + span: crate::ast::Span::unknown(), + }); } default_args.push(ASTNode::MethodCall { - object: Box::new(ASTNode::Variable { name: "ArrayBox".to_string(), span: crate::ast::Span::unknown() }), + object: Box::new(ASTNode::Variable { + name: "ArrayBox".to_string(), + span: crate::ast::Span::unknown(), + }), method: "of".to_string(), arguments: str_nodes, span: crate::ast::Span::unknown(), }); } else { - default_args.push(ASTNode::New { class: "ArrayBox".to_string(), arguments: vec![], type_arguments: vec![], span: crate::ast::Span::unknown() }); + default_args.push(ASTNode::New { + class: "ArrayBox".to_string(), + arguments: vec![], + type_arguments: vec![], + span: crate::ast::Span::unknown(), + }); } } else { - default_args.push(ASTNode::New { class: "ArrayBox".to_string(), arguments: vec![], type_arguments: vec![], span: crate::ast::Span::unknown() }); + default_args.push(ASTNode::New { + class: "ArrayBox".to_string(), + arguments: vec![], + type_arguments: vec![], + span: crate::ast::Span::unknown(), + }); } } } @@ -97,7 +116,10 @@ impl NyashInterpreter { } let main_call_ast = ASTNode::MethodCall { object: Box::new(ASTNode::FieldAccess { - object: Box::new(ASTNode::Variable { name: "statics".to_string(), span: crate::ast::Span::unknown() }), + object: Box::new(ASTNode::Variable { + name: "statics".to_string(), + span: crate::ast::Span::unknown(), + }), field: "Main".to_string(), span: crate::ast::Span::unknown(), }), diff --git a/src/interpreter/expressions/access.rs b/src/interpreter/expressions/access.rs index 0813460b..c7f96933 100644 --- a/src/interpreter/expressions/access.rs +++ b/src/interpreter/expressions/access.rs @@ -21,9 +21,11 @@ macro_rules! debug_trace { impl NyashInterpreter { /// フィールドアクセスを実行 - Field access processing with weak reference support - pub(super) fn execute_field_access(&mut self, object: &ASTNode, field: &str) - -> Result { - + pub(super) fn execute_field_access( + &mut self, + object: &ASTNode, + field: &str, + ) -> Result { // 🔥 Static Boxアクセスチェック if let ASTNode::Variable { name, .. } = object { // Static boxの可能性をチェック @@ -32,8 +34,7 @@ impl NyashInterpreter { return Ok(Arc::from(static_result)); } } - - + // 外からのフィールドアクセスか(me/this以外)を判定 let is_internal_access = match object { ASTNode::This { .. } | ASTNode::Me { .. } => true, @@ -41,22 +42,26 @@ impl NyashInterpreter { _ => false, }; - // オブジェクトを評価(通常のフィールドアクセス) + // オブジェクトを評価(通常のフィールドアクセス) let obj_value = self.execute_expression(object); - + let obj_value = obj_value?; - + // InstanceBoxにキャスト if let Some(instance) = obj_value.as_any().downcast_ref::() { // 可視性チェック(互換性: public/privateのどちらかが定義されていれば強制) if !is_internal_access { let box_decls = self.shared.box_declarations.read().unwrap(); if let Some(box_decl) = box_decls.get(&instance.class_name) { - let has_visibility = !box_decl.public_fields.is_empty() || !box_decl.private_fields.is_empty(); + let has_visibility = + !box_decl.public_fields.is_empty() || !box_decl.private_fields.is_empty(); if has_visibility { if !box_decl.public_fields.contains(&field.to_string()) { return Err(RuntimeError::InvalidOperation { - message: format!("Field '{}' is private in {}", field, instance.class_name), + message: format!( + "Field '{}' is private in {}", + field, instance.class_name + ), }); } } @@ -64,28 +69,35 @@ impl NyashInterpreter { } // 🔥 finiは何回呼ばれてもエラーにしない(ユーザー要求) // is_finalized()チェックを削除 - + // フィールドの値を取得 - let field_value = instance.get_field(field) + let field_value = instance + .get_field(field) .ok_or(RuntimeError::InvalidOperation { message: format!("Field '{}' not found in {}", field, instance.class_name), })?; - + // 🔗 Weak Reference Check: Use unified accessor for weak fields let box_decls = self.shared.box_declarations.read().unwrap(); if let Some(box_decl) = box_decls.get(&instance.class_name) { if box_decl.weak_fields.contains(&field.to_string()) { - // 🎯 PHASE 2: Use unified accessor for auto-nil weak reference handling - if let Some(weak_value) = instance.get_weak_field(field, self) { // Pass self + if let Some(weak_value) = instance.get_weak_field(field, self) { + // Pass self match &weak_value { crate::value::NyashValue::Null => { - debug_trace!("🔗 DEBUG: Weak field '{}' is null (reference dropped)", field); + debug_trace!( + "🔗 DEBUG: Weak field '{}' is null (reference dropped)", + field + ); // Return null box for compatibility return Ok(Arc::new(crate::boxes::null_box::NullBox::new())); } _ => { - debug_trace!("🔗 DEBUG: Weak field '{}' still has valid reference", field); + debug_trace!( + "🔗 DEBUG: Weak field '{}' still has valid reference", + field + ); // Convert back to Box for now if let Ok(box_value) = weak_value.to_box() { if let Ok(inner_box) = box_value.try_lock() { @@ -98,63 +110,88 @@ impl NyashInterpreter { // If weak field access failed, fall through to normal access } } - + // Return the shared Arc reference directly Ok(field_value) } else { Err(RuntimeError::TypeError { - message: format!("Cannot access field '{}' on non-instance type. Type: {}", field, obj_value.type_name()), + message: format!( + "Cannot access field '{}' on non-instance type. Type: {}", + field, + obj_value.type_name() + ), }) } } - + /// 🔥 Static Box名前空間のフィールドアクセス - fn execute_static_field_access(&mut self, static_box_name: &str, field: &str) - -> Result, RuntimeError> { + fn execute_static_field_access( + &mut self, + static_box_name: &str, + field: &str, + ) -> Result, RuntimeError> { // 1. Static Boxの初期化を確実に実行 self.ensure_static_box_initialized(static_box_name)?; - + // 2. GlobalBox.statics.{static_box_name} からインスタンスを取得 - let global_box = self.shared.global_box.lock() - .map_err(|_| RuntimeError::RuntimeFailure { - message: "Failed to acquire global box lock".to_string() - })?; - - let statics_box = global_box.get_field("statics") + let global_box = + self.shared + .global_box + .lock() + .map_err(|_| RuntimeError::RuntimeFailure { + message: "Failed to acquire global box lock".to_string(), + })?; + + let statics_box = global_box + .get_field("statics") .ok_or(RuntimeError::RuntimeFailure { - message: "statics namespace not found in GlobalBox".to_string() + message: "statics namespace not found in GlobalBox".to_string(), })?; - - let statics_instance = statics_box.as_any() + + let statics_instance = + statics_box + .as_any() + .downcast_ref::() + .ok_or(RuntimeError::TypeError { + message: "statics field is not an InstanceBox".to_string(), + })?; + + let static_box_instance = + statics_instance + .get_field(static_box_name) + .ok_or(RuntimeError::RuntimeFailure { + message: format!( + "Static box '{}' instance not found in statics namespace", + static_box_name + ), + })?; + + let instance = static_box_instance + .as_any() .downcast_ref::() .ok_or(RuntimeError::TypeError { - message: "statics field is not an InstanceBox".to_string() + message: format!("Static box '{}' is not an InstanceBox", static_box_name), })?; - - let static_box_instance = statics_instance.get_field(static_box_name) - .ok_or(RuntimeError::RuntimeFailure { - message: format!("Static box '{}' instance not found in statics namespace", static_box_name) - })?; - - let instance = static_box_instance.as_any() - .downcast_ref::() - .ok_or(RuntimeError::TypeError { - message: format!("Static box '{}' is not an InstanceBox", static_box_name) - })?; - + // 3. フィールドアクセス - let shared_field = instance.get_field(field) + let shared_field = instance + .get_field(field) .ok_or(RuntimeError::InvalidOperation { - message: format!("Field '{}' not found in static box '{}'", field, static_box_name), + message: format!( + "Field '{}' not found in static box '{}'", + field, static_box_name + ), })?; - + // Convert Arc to Box for compatibility Ok((*shared_field).clone_or_share()) } - - + /// await式を実行 - Execute await expression (Result.Ok/Err統一) - pub(super) fn execute_await(&mut self, expression: &ASTNode) -> Result, RuntimeError> { + pub(super) fn execute_await( + &mut self, + expression: &ASTNode, + ) -> Result, RuntimeError> { let value = self.execute_expression(expression)?; if let Some(future) = value.as_any().downcast_ref::() { let max_ms: u64 = crate::config::env::await_max_ms(); @@ -164,7 +201,9 @@ impl NyashInterpreter { crate::runtime::global_hooks::safepoint_and_poll(); std::thread::yield_now(); 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) { let err = Box::new(crate::box_trait::StringBox::new("Timeout")); return Ok(Box::new(crate::boxes::result::NyashResultBox::new_err(err))); @@ -173,7 +212,9 @@ impl NyashInterpreter { let v = future.get(); Ok(Box::new(crate::boxes::result::NyashResultBox::new_ok(v))) } else { - Ok(Box::new(crate::boxes::result::NyashResultBox::new_ok(value))) + Ok(Box::new(crate::boxes::result::NyashResultBox::new_ok( + value, + ))) } } } diff --git a/src/interpreter/expressions/builtins.rs b/src/interpreter/expressions/builtins.rs index c80d3ed5..d670ba1a 100644 --- a/src/interpreter/expressions/builtins.rs +++ b/src/interpreter/expressions/builtins.rs @@ -3,29 +3,41 @@ */ use crate::ast::ASTNode; -use crate::box_trait::{NyashBox, StringBox, IntegerBox, VoidBox}; -use crate::boxes::{ArrayBox, MapBox, MathBox, ConsoleBox, TimeBox, RandomBox, DebugBox, SoundBox, SocketBox}; -use crate::boxes::{HTTPServerBox, HTTPRequestBox, HTTPResponseBox}; +use crate::box_trait::{IntegerBox, NyashBox, StringBox, VoidBox}; +use crate::boxes::{ + ArrayBox, ConsoleBox, DebugBox, MapBox, MathBox, RandomBox, SocketBox, SoundBox, TimeBox, +}; +use crate::boxes::{HTTPRequestBox, HTTPResponseBox, HTTPServerBox}; use crate::interpreter::{NyashInterpreter, RuntimeError}; use std::sync::{Arc, Mutex}; impl NyashInterpreter { /// 🔥 ビルトインBoxのメソッド呼び出し - pub(super) fn execute_builtin_box_method(&mut self, parent: &str, method: &str, _current_instance: Box, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_builtin_box_method( + &mut self, + parent: &str, + method: &str, + _current_instance: Box, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // Strict plugin-only mode: disallow builtin paths if std::env::var("NYASH_PLUGIN_ONLY").ok().as_deref() == Some("1") { - return Err(RuntimeError::InvalidOperation { message: format!("Builtin path disabled: {}.{}, use plugin invoke", parent, method) }); + return Err(RuntimeError::InvalidOperation { + message: format!( + "Builtin path disabled: {}.{}, use plugin invoke", + parent, method + ), + }); } - + // 🌟 Phase 8.9: birth method support for builtin boxes if method == "birth" { return self.execute_builtin_birth_method(parent, _current_instance, arguments); } - + // ビルトインBoxのインスタンスを作成または取得 // 現在のインスタンスからビルトインBoxのデータを取得し、ビルトインBoxとしてメソッド実行 - + match parent { "StringBox" => { // StringBoxのインスタンスを作成(デフォルト値) @@ -60,7 +72,10 @@ impl NyashInterpreter { // P2PBoxの場合、現在のインスタンスからP2PBoxインスタンスを取得する必要がある // TODO: 現在のインスタンスのフィールドからP2PBoxを取得 return Err(RuntimeError::InvalidOperation { - message: format!("P2PBox delegation not yet fully implemented: {}.{}", parent, method), + message: format!( + "P2PBox delegation not yet fully implemented: {}.{}", + parent, method + ), }); } "FileBox" => { @@ -87,21 +102,27 @@ impl NyashInterpreter { } "RandomBox" => { if let Ok(reg) = self.runtime.box_registry.lock() { - if let Ok(_b) = reg.create_box("RandomBox", &[]) { return Ok(Box::new(VoidBox::new())); } + if let Ok(_b) = reg.create_box("RandomBox", &[]) { + return Ok(Box::new(VoidBox::new())); + } } let random_box = RandomBox::new(); self.execute_random_method(&random_box, method, arguments) } "DebugBox" => { if let Ok(reg) = self.runtime.box_registry.lock() { - if let Ok(_b) = reg.create_box("DebugBox", &[]) { return Ok(Box::new(VoidBox::new())); } + if let Ok(_b) = reg.create_box("DebugBox", &[]) { + return Ok(Box::new(VoidBox::new())); + } } let debug_box = DebugBox::new(); self.execute_debug_method(&debug_box, method, arguments) } "SoundBox" => { if let Ok(reg) = self.runtime.box_registry.lock() { - if let Ok(_b) = reg.create_box("SoundBox", &[]) { return Ok(Box::new(VoidBox::new())); } + if let Ok(_b) = reg.create_box("SoundBox", &[]) { + return Ok(Box::new(VoidBox::new())); + } } let sound_box = SoundBox::new(); self.execute_sound_method(&sound_box, method, arguments) @@ -122,62 +143,78 @@ impl NyashInterpreter { let http_response_box = HTTPResponseBox::new(); self.execute_http_response_method(&http_response_box, method, arguments) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown built-in Box type for delegation: {}", parent), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown built-in Box type for delegation: {}", parent), + }), } } - + /// 🌟 Phase 8.9: Execute birth method for builtin boxes /// Provides constructor functionality for builtin boxes through explicit birth() calls - pub(super) fn execute_builtin_birth_method(&mut self, builtin_name: &str, current_instance: Box, arguments: &[ASTNode]) - -> Result, RuntimeError> { - + pub(super) fn execute_builtin_birth_method( + &mut self, + builtin_name: &str, + current_instance: Box, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // ビルトインBoxの種類に応じて適切なインスタンスを作成して返す match builtin_name { "StringBox" => { if arg_values.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("StringBox.birth() expects 1 argument, got {}", arg_values.len()), + message: format!( + "StringBox.birth() expects 1 argument, got {}", + arg_values.len() + ), }); } - + let content = arg_values[0].to_string_box().value; let string_box = StringBox::new(content.clone()); - + // 現在のインスタンスがInstanceBoxの場合、StringBoxを特別なフィールドに保存 - if let Some(instance) = current_instance.as_any().downcast_ref::() { + if let Some(instance) = current_instance + .as_any() + .downcast_ref::() + { // 特別な内部フィールド "__builtin_content" にStringBoxを保存 let string_box_arc: Arc> = Arc::new(Mutex::new(string_box)); - instance.set_field_dynamic("__builtin_content".to_string(), - crate::value::NyashValue::Box(string_box_arc)); + instance.set_field_dynamic( + "__builtin_content".to_string(), + crate::value::NyashValue::Box(string_box_arc), + ); } - + Ok(Box::new(VoidBox::new())) // Return void to indicate successful initialization } "IntegerBox" => { if arg_values.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("IntegerBox.birth() expects 1 argument, got {}", arg_values.len()), + message: format!( + "IntegerBox.birth() expects 1 argument, got {}", + arg_values.len() + ), }); } - - let value = if let Ok(int_val) = arg_values[0].to_string_box().value.parse::() { + + let value = if let Ok(int_val) = arg_values[0].to_string_box().value.parse::() + { int_val } else { return Err(RuntimeError::TypeError { - message: format!("Cannot convert '{}' to integer", arg_values[0].to_string_box().value), + message: format!( + "Cannot convert '{}' to integer", + arg_values[0].to_string_box().value + ), }); }; - + let _integer_box = IntegerBox::new(value); Ok(Box::new(VoidBox::new())) } @@ -185,11 +222,16 @@ impl NyashInterpreter { // MathBoxは引数なしのコンストラクタ if arg_values.len() != 0 { return Err(RuntimeError::InvalidOperation { - message: format!("MathBox.birth() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "MathBox.birth() expects 0 arguments, got {}", + arg_values.len() + ), }); } if let Ok(reg) = self.runtime.box_registry.lock() { - if let Ok(_b) = reg.create_box("MathBox", &[]) { return Ok(Box::new(VoidBox::new())); } + if let Ok(_b) = reg.create_box("MathBox", &[]) { + return Ok(Box::new(VoidBox::new())); + } } let _math_box = MathBox::new(); Ok(Box::new(VoidBox::new())) @@ -198,10 +240,13 @@ impl NyashInterpreter { // ArrayBoxも引数なしのコンストラクタ if arg_values.len() != 0 { return Err(RuntimeError::InvalidOperation { - message: format!("ArrayBox.birth() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "ArrayBox.birth() expects 0 arguments, got {}", + arg_values.len() + ), }); } - + let _array_box = ArrayBox::new(); eprintln!("🌟 DEBUG: ArrayBox.birth() created"); Ok(Box::new(VoidBox::new())) @@ -209,7 +254,10 @@ impl NyashInterpreter { _ => { // 他のビルトインBoxは今後追加 Err(RuntimeError::InvalidOperation { - message: format!("birth() method not yet implemented for builtin box '{}'", builtin_name), + message: format!( + "birth() method not yet implemented for builtin box '{}'", + builtin_name + ), }) } } diff --git a/src/interpreter/expressions/calls.rs b/src/interpreter/expressions/calls.rs index 36252051..b89f2949 100644 --- a/src/interpreter/expressions/calls.rs +++ b/src/interpreter/expressions/calls.rs @@ -4,11 +4,11 @@ use super::*; use crate::ast::ASTNode; -use crate::box_trait::{NyashBox, StringBox, IntegerBox, VoidBox}; +use crate::box_trait::{IntegerBox, NyashBox, StringBox, VoidBox}; use crate::boxes::MapBox; +use crate::boxes::{DateTimeBox, HTTPRequestBox, HTTPResponseBox, HTTPServerBox}; +use crate::boxes::{DebugBox, RandomBox, SoundBox}; use crate::boxes::{IntentBox, SocketBox}; -use crate::boxes::{HTTPServerBox, HTTPRequestBox, HTTPResponseBox, DateTimeBox}; -use crate::boxes::{RandomBox, SoundBox, DebugBox}; use crate::instance_v2::InstanceBox; use crate::interpreter::{NyashInterpreter, RuntimeError}; @@ -24,9 +24,12 @@ use std::sync::Arc; impl NyashInterpreter { /// メソッド呼び出しを実行 - Method call processing - pub(super) fn execute_method_call(&mut self, object: &ASTNode, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { - + pub(super) fn execute_method_call( + &mut self, + object: &ASTNode, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 🔥 static関数のチェック if let ASTNode::Variable { name, .. } = object { // static関数が存在するかチェック @@ -42,62 +45,67 @@ impl NyashInterpreter { None } }; - + if let Some(static_func) = static_func { // static関数を実行 if let ASTNode::FunctionDeclaration { params, body, .. } = static_func { - // 引数を評価 - let mut arg_values = Vec::new(); - for arg in arguments { - arg_values.push(self.execute_expression(arg)?); + // 引数を評価 + let mut arg_values = Vec::new(); + for arg in arguments { + arg_values.push(self.execute_expression(arg)?); + } + + // パラメータ数チェック + if arg_values.len() != params.len() { + return Err(RuntimeError::InvalidOperation { + message: format!( + "Static method {}.{} expects {} arguments, got {}", + name, + method, + params.len(), + arg_values.len() + ), + }); + } + + // 🌍 local変数スタックを保存・クリア(static関数呼び出し開始) + let saved_locals = self.save_local_vars(); + self.local_vars.clear(); + + // 📤 outbox変数スタックも保存・クリア(static関数専用) + let saved_outbox = self.save_outbox_vars(); + self.outbox_vars.clear(); + + // 引数をlocal変数として設定 + for (param, value) in params.iter().zip(arg_values.iter()) { + self.declare_local_variable(param, value.clone_or_share()); + } + + // static関数の本体を実行(TaskGroupスコープ) + crate::runtime::global_hooks::push_task_scope(); + let mut result = Box::new(VoidBox::new()) as Box; + for statement in &body { + result = self.execute_statement(statement)?; + + // return文チェック + if let super::ControlFlow::Return(return_val) = &self.control_flow { + result = return_val.clone_box(); + self.control_flow = super::ControlFlow::None; + break; } - - // パラメータ数チェック - if arg_values.len() != params.len() { - return Err(RuntimeError::InvalidOperation { - message: format!("Static method {}.{} expects {} arguments, got {}", - name, method, params.len(), arg_values.len()), - }); - } - - // 🌍 local変数スタックを保存・クリア(static関数呼び出し開始) - let saved_locals = self.save_local_vars(); - self.local_vars.clear(); - - // 📤 outbox変数スタックも保存・クリア(static関数専用) - let saved_outbox = self.save_outbox_vars(); - self.outbox_vars.clear(); - - // 引数をlocal変数として設定 - for (param, value) in params.iter().zip(arg_values.iter()) { - self.declare_local_variable(param, value.clone_or_share()); - } - - // static関数の本体を実行(TaskGroupスコープ) - crate::runtime::global_hooks::push_task_scope(); - let mut result = Box::new(VoidBox::new()) as Box; - for statement in &body { - result = self.execute_statement(statement)?; - - // return文チェック - if let super::ControlFlow::Return(return_val) = &self.control_flow { - result = return_val.clone_box(); - self.control_flow = super::ControlFlow::None; - break; - } - } - - // local変数スタックを復元 - crate::runtime::global_hooks::pop_task_scope(); - self.restore_local_vars(saved_locals); - - // outbox変数スタックを復元 - self.restore_outbox_vars(saved_outbox); - - return Ok(result); + } + + // local変数スタックを復元 + crate::runtime::global_hooks::pop_task_scope(); + self.restore_local_vars(saved_locals); + + // outbox変数スタックを復元 + self.restore_outbox_vars(saved_outbox); + + return Ok(result); } } - + // 📚 nyashstd標準ライブラリのメソッドチェック let stdlib_method = if let Some(ref stdlib) = self.stdlib { if let Some(nyashstd_namespace) = stdlib.namespaces.get("nyashstd") { @@ -120,136 +128,158 @@ impl NyashInterpreter { idebug!("🔍 stdlib not initialized for method call"); None }; - + if let Some(builtin_method) = stdlib_method { idebug!("🌟 Calling nyashstd method: {}.{}", name, method); - + // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // 標準ライブラリのメソッドを実行 let result = builtin_method(&arg_values)?; idebug!("✅ nyashstd method completed: {}.{}", name, method); return Ok(result); } - + // 🔥 ユーザー定義のStatic Boxメソッドチェック if self.is_static_box(name) { idebug!("🔍 Checking user-defined static box: {}", name); - + // Static Boxの初期化を確実に実行 self.ensure_static_box_initialized(name)?; - + // GlobalBox.statics.{name} からメソッドを取得してクローン - let (method_clone, static_instance_clone) = { - let global_box = self.shared.global_box.lock() - .map_err(|_| RuntimeError::RuntimeFailure { - message: "Failed to acquire global box lock".to_string() + let (method_clone, static_instance_clone) = + { + let global_box = self.shared.global_box.lock().map_err(|_| { + RuntimeError::RuntimeFailure { + message: "Failed to acquire global box lock".to_string(), + } })?; - - let statics_box = global_box.get_field("statics") - .ok_or(RuntimeError::RuntimeFailure { - message: "statics namespace not found in GlobalBox".to_string() + + let statics_box = global_box.get_field("statics").ok_or( + RuntimeError::RuntimeFailure { + message: "statics namespace not found in GlobalBox".to_string(), + }, + )?; + + let statics_instance = statics_box + .as_any() + .downcast_ref::() + .ok_or(RuntimeError::TypeError { + message: "statics field is not an InstanceBox".to_string(), })?; - - let statics_instance = statics_box.as_any() - .downcast_ref::() - .ok_or(RuntimeError::TypeError { - message: "statics field is not an InstanceBox".to_string() - })?; - - let static_instance = statics_instance.get_field(name) - .ok_or(RuntimeError::InvalidOperation { - message: format!("Static box '{}' not found in statics namespace", name), - })?; - - let instance = static_instance.as_any() - .downcast_ref::() - .ok_or(RuntimeError::TypeError { - message: format!("Static box '{}' is not an InstanceBox", name), - })?; - - // メソッドを探す - if let Some(method_node) = instance.get_method(method) { - (method_node.clone(), static_instance.clone_box()) - } else { - return Err(RuntimeError::InvalidOperation { - message: format!("Method '{}' not found in static box '{}'", method, name), - }); - } - }; // lockはここで解放される - + + let static_instance = statics_instance.get_field(name).ok_or( + RuntimeError::InvalidOperation { + message: format!( + "Static box '{}' not found in statics namespace", + name + ), + }, + )?; + + let instance = static_instance + .as_any() + .downcast_ref::() + .ok_or(RuntimeError::TypeError { + message: format!("Static box '{}' is not an InstanceBox", name), + })?; + + // メソッドを探す + if let Some(method_node) = instance.get_method(method) { + (method_node.clone(), static_instance.clone_box()) + } else { + return Err(RuntimeError::InvalidOperation { + message: format!( + "Method '{}' not found in static box '{}'", + method, name + ), + }); + } + }; // lockはここで解放される + idebug!("🌟 Calling static box method: {}.{}", name, method); - + // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドのパラメータと本体を取得 if let ASTNode::FunctionDeclaration { params, body, .. } = &method_clone { // local変数スタックを保存 let saved_locals = self.save_local_vars(); self.local_vars.clear(); - + // meをstatic boxインスタンスに設定 self.declare_local_variable("me", static_instance_clone); - + // 引数をlocal変数として設定 for (param, value) in params.iter().zip(arg_values.iter()) { self.declare_local_variable(param, value.clone_or_share()); } - - // メソッドの本体を実行(TaskGroupスコープ) - crate::runtime::global_hooks::push_task_scope(); - let mut result = Box::new(VoidBox::new()) as Box; - for statement in body { - result = self.execute_statement(statement)?; - - // return文チェック - if let super::ControlFlow::Return(return_val) = &self.control_flow { - result = return_val.clone_box(); - self.control_flow = super::ControlFlow::None; - break; + + // メソッドの本体を実行(TaskGroupスコープ) + crate::runtime::global_hooks::push_task_scope(); + let mut result = Box::new(VoidBox::new()) as Box; + for statement in body { + result = self.execute_statement(statement)?; + + // return文チェック + if let super::ControlFlow::Return(return_val) = &self.control_flow { + result = return_val.clone_box(); + self.control_flow = super::ControlFlow::None; + break; + } } + + // local変数スタックを復元 + crate::runtime::global_hooks::pop_task_scope(); + self.restore_local_vars(saved_locals); + + idebug!("✅ Static box method completed: {}.{}", name, method); + return Ok(result); } - - // local変数スタックを復元 - crate::runtime::global_hooks::pop_task_scope(); - self.restore_local_vars(saved_locals); - - idebug!("✅ Static box method completed: {}.{}", name, method); - return Ok(result); - } } } - + // オブジェクトを評価(通常のメソッド呼び出し) let obj_value = self.execute_expression(object)?; - idebug!("🔍 DEBUG: execute_method_call - object type: {}, method: {}", obj_value.type_name(), method); + idebug!( + "🔍 DEBUG: execute_method_call - object type: {}, method: {}", + obj_value.type_name(), + method + ); // 🌟 ユニバーサルメソッド前段ディスパッチ(非侵襲) // toString()/type()/equals(x)/clone() をトレイトに直結 match method { "toString" => { if !arguments.is_empty() { - return Err(RuntimeError::InvalidOperation { message: format!("toString() expects 0 arguments, got {}", arguments.len()) }); + return Err(RuntimeError::InvalidOperation { + message: format!("toString() expects 0 arguments, got {}", arguments.len()), + }); } return Ok(Box::new(obj_value.to_string_box())); } "type" => { if !arguments.is_empty() { - return Err(RuntimeError::InvalidOperation { message: format!("type() expects 0 arguments, got {}", arguments.len()) }); + return Err(RuntimeError::InvalidOperation { + message: format!("type() expects 0 arguments, got {}", arguments.len()), + }); } return Ok(Box::new(StringBox::new(obj_value.type_name()))); } "equals" => { if arguments.len() != 1 { - return Err(RuntimeError::InvalidOperation { message: format!("equals() expects 1 argument, got {}", arguments.len()) }); + return Err(RuntimeError::InvalidOperation { + message: format!("equals() expects 1 argument, got {}", arguments.len()), + }); } let rhs = self.execute_expression(&arguments[0])?; let eq = obj_value.equals(&*rhs); @@ -257,83 +287,105 @@ impl NyashInterpreter { } "clone" => { if !arguments.is_empty() { - return Err(RuntimeError::InvalidOperation { message: format!("clone() expects 0 arguments, got {}", arguments.len()) }); + return Err(RuntimeError::InvalidOperation { + message: format!("clone() expects 0 arguments, got {}", arguments.len()), + }); } return Ok(obj_value.clone_box()); } _ => {} } - + // Builtin dispatch (centralized) if let Some(res) = self.dispatch_builtin_method(&obj_value, method, arguments) { return res; } - + // DateTimeBox method calls if let Some(datetime_box) = obj_value.as_any().downcast_ref::() { return self.execute_datetime_method(datetime_box, method, arguments); } - + // TimerBox method calls - if let Some(timer_box) = obj_value.as_any().downcast_ref::() { + if let Some(timer_box) = obj_value + .as_any() + .downcast_ref::() + { return self.execute_timer_method(timer_box, method, arguments); } - + // MapBox method calls if let Some(map_box) = obj_value.as_any().downcast_ref::() { return self.execute_map_method(map_box, method, arguments); } - + // RandomBox method calls if let Some(random_box) = obj_value.as_any().downcast_ref::() { return self.execute_random_method(random_box, method, arguments); } - + // SoundBox method calls if let Some(sound_box) = obj_value.as_any().downcast_ref::() { return self.execute_sound_method(sound_box, method, arguments); } - + // DebugBox method calls if let Some(debug_box) = obj_value.as_any().downcast_ref::() { return self.execute_debug_method(debug_box, method, arguments); } - + // ConsoleBox method calls - if let Some(console_box) = obj_value.as_any().downcast_ref::() { + if let Some(console_box) = obj_value + .as_any() + .downcast_ref::() + { return self.execute_console_method(console_box, method, arguments); } - + // IntentBox method calls if let Some(intent_box) = obj_value.as_any().downcast_ref::() { return self.execute_intent_box_method(intent_box, method, arguments); } - + // SocketBox method calls if let Some(socket_box) = obj_value.as_any().downcast_ref::() { let result = self.execute_socket_method(socket_box, method, arguments)?; - + // 🔧 FIX: Update stored variable for stateful SocketBox methods // These methods modify the SocketBox internal state, so we need to update // the stored variable/field to ensure subsequent accesses get the updated state if matches!(method, "bind" | "connect" | "close") { - idebug!("🔧 DEBUG: Stateful method '{}' called, updating stored instance", method); + idebug!( + "🔧 DEBUG: Stateful method '{}' called, updating stored instance", + method + ); let updated_instance = socket_box.clone(); - idebug!("🔧 DEBUG: Updated instance created with ID={}", updated_instance.box_id()); - + idebug!( + "🔧 DEBUG: Updated instance created with ID={}", + updated_instance.box_id() + ); + match object { ASTNode::Variable { name, .. } => { idebug!("🔧 DEBUG: Updating local variable '{}'", name); // Handle local variables if let Some(stored_var) = self.local_vars.get_mut(name) { - idebug!("🔧 DEBUG: Found local variable '{}', updating from id={} to id={}", - name, stored_var.box_id(), updated_instance.box_id()); + idebug!( + "🔧 DEBUG: Found local variable '{}', updating from id={} to id={}", + name, + stored_var.box_id(), + updated_instance.box_id() + ); *stored_var = Arc::new(updated_instance); } else { idebug!("🔧 DEBUG: Local variable '{}' not found", name); } - }, - ASTNode::FieldAccess { object: field_obj, field, .. } => { + } + ASTNode::FieldAccess { + object: field_obj, + field, + .. + } => { idebug!("🔧 DEBUG: Updating field access '{}'", field); // Handle StaticBox fields like me.server match field_obj.as_ref() { @@ -342,13 +394,22 @@ impl NyashInterpreter { if name == "me" { idebug!("🔧 DEBUG: Updating me.{} (via variable)", field); if let Ok(me_instance) = self.resolve_variable("me") { - idebug!("🔧 DEBUG: Resolved 'me' instance id={}", me_instance.box_id()); - if let Some(instance) = (*me_instance).as_any().downcast_ref::() { + idebug!( + "🔧 DEBUG: Resolved 'me' instance id={}", + me_instance.box_id() + ); + if let Some(instance) = + (*me_instance).as_any().downcast_ref::() + { idebug!("🔧 DEBUG: me is InstanceBox, setting field '{}' to updated instance id={}", field, updated_instance.box_id()); - let result = instance.set_field(field, Arc::new(updated_instance)); + let result = instance + .set_field(field, Arc::new(updated_instance)); idebug!("🔧 DEBUG: set_field result: {:?}", result); } else { - idebug!("🔧 DEBUG: me is not an InstanceBox, type: {}", me_instance.type_name()); + idebug!( + "🔧 DEBUG: me is not an InstanceBox, type: {}", + me_instance.type_name() + ); } } else { idebug!("🔧 DEBUG: Failed to resolve 'me'"); @@ -356,141 +417,188 @@ impl NyashInterpreter { } else { idebug!("🔧 DEBUG: Field object is not 'me', it's '{}'", name); } - }, + } ASTNode::Me { .. } => { idebug!("🔧 DEBUG: Field object is Me node, updating me.{}", field); if let Ok(me_instance) = self.resolve_variable("me") { - idebug!("🔧 DEBUG: Resolved 'me' instance id={}", me_instance.box_id()); - if let Some(instance) = (*me_instance).as_any().downcast_ref::() { + idebug!( + "🔧 DEBUG: Resolved 'me' instance id={}", + me_instance.box_id() + ); + if let Some(instance) = + (*me_instance).as_any().downcast_ref::() + { idebug!("🔧 DEBUG: me is InstanceBox, setting field '{}' to updated instance id={}", field, updated_instance.box_id()); - let result = instance.set_field(field, Arc::new(updated_instance)); + let result = + instance.set_field(field, Arc::new(updated_instance)); idebug!("🔧 DEBUG: set_field result: {:?}", result); } else { - idebug!("🔧 DEBUG: me is not an InstanceBox, type: {}", me_instance.type_name()); + idebug!( + "🔧 DEBUG: me is not an InstanceBox, type: {}", + me_instance.type_name() + ); } } else { idebug!("🔧 DEBUG: Failed to resolve 'me'"); } - }, + } _ => { - idebug!("🔧 DEBUG: Field object is not a variable or me, type: {:?}", field_obj); + idebug!( + "🔧 DEBUG: Field object is not a variable or me, type: {:?}", + field_obj + ); } } - }, + } _ => { idebug!("🔧 DEBUG: Object type not handled: {:?}", object); } } } - + return Ok(result); } - + // HTTPServerBox method calls if let Some(http_server_box) = obj_value.as_any().downcast_ref::() { return self.execute_http_server_method(http_server_box, method, arguments); } - + // HTTPRequestBox method calls if let Some(http_request_box) = obj_value.as_any().downcast_ref::() { return self.execute_http_request_method(http_request_box, method, arguments); } - + // HTTPResponseBox method calls if let Some(http_response_box) = obj_value.as_any().downcast_ref::() { return self.execute_http_response_method(http_response_box, method, arguments); } - + // P2PBox method calls if let Some(p2p_box) = obj_value.as_any().downcast_ref::() { return self.execute_p2p_box_method(p2p_box, method, arguments); } - + // EguiBox method calls (非WASM環境のみ) #[cfg(all(feature = "gui", not(target_arch = "wasm32")))] if let Some(egui_box) = obj_value.as_any().downcast_ref::() { return self.execute_egui_method(egui_box, method, arguments); } - + // WebDisplayBox method calls (WASM環境のみ) #[cfg(target_arch = "wasm32")] - if let Some(web_display_box) = obj_value.as_any().downcast_ref::() { + if let Some(web_display_box) = obj_value + .as_any() + .downcast_ref::() + { return self.execute_web_display_method(web_display_box, method, arguments); } - + // WebConsoleBox method calls (WASM環境のみ) #[cfg(target_arch = "wasm32")] - if let Some(web_console_box) = obj_value.as_any().downcast_ref::() { + if let Some(web_console_box) = obj_value + .as_any() + .downcast_ref::() + { return self.execute_web_console_method(web_console_box, method, arguments); } - + // WebCanvasBox method calls (WASM環境のみ) #[cfg(target_arch = "wasm32")] - if let Some(web_canvas_box) = obj_value.as_any().downcast_ref::() { + if let Some(web_canvas_box) = obj_value + .as_any() + .downcast_ref::() + { return self.execute_web_canvas_method(web_canvas_box, method, arguments); } - + // MethodBox method calls - if let Some(method_box) = obj_value.as_any().downcast_ref::() { + if let Some(method_box) = obj_value + .as_any() + .downcast_ref::() + { return self.execute_method_box_method(method_box, method, arguments); } - - // IntegerBox method calls + + // IntegerBox method calls if let Some(integer_box) = obj_value.as_any().downcast_ref::() { return self.execute_integer_method(integer_box, method, arguments); } - + // FloatBox method calls (将来的に追加予定) - + // RangeBox method calls (将来的に追加予定) - + // PluginBoxV2 method calls #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] - if let Some(plugin_box) = obj_value.as_any().downcast_ref::() { + if let Some(plugin_box) = obj_value + .as_any() + .downcast_ref::() + { return self.execute_plugin_box_v2_method(plugin_box, method, arguments); } - + // InstanceBox dispatch - if let Some(res) = self.dispatch_instance_method(object, &obj_value, method, arguments) { return res; } - idebug!("🔍 DEBUG: Reached non-instance type error for type: {}, method: {}", obj_value.type_name(), method); - Err(RuntimeError::TypeError { message: format!("Cannot call method '{}' on non-instance type", method) }) + if let Some(res) = self.dispatch_instance_method(object, &obj_value, method, arguments) { + return res; + } + idebug!( + "🔍 DEBUG: Reached non-instance type error for type: {}, method: {}", + obj_value.type_name(), + method + ); + Err(RuntimeError::TypeError { + message: format!("Cannot call method '{}' on non-instance type", method), + }) } - + /// 🔥 FromCall実行処理 - from Parent.method(arguments) or from Parent.constructor(arguments) - pub(super) fn execute_from_call(&mut self, parent: &str, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { - + pub(super) fn execute_from_call( + &mut self, + parent: &str, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 1. 現在のコンテキストで'me'変数を取得(現在のインスタンス) - let current_instance_val = self.resolve_variable("me") - .map_err(|_| RuntimeError::InvalidOperation { - message: "'from' can only be used inside methods".to_string(), - })?; - - let current_instance = (*current_instance_val).as_any().downcast_ref::() + let current_instance_val = + self.resolve_variable("me") + .map_err(|_| RuntimeError::InvalidOperation { + message: "'from' can only be used inside methods".to_string(), + })?; + + let current_instance = (*current_instance_val) + .as_any() + .downcast_ref::() .ok_or(RuntimeError::TypeError { message: "'from' requires current instance to be InstanceBox".to_string(), })?; - + // 2. 現在のクラスのデリゲーション関係を検証 let current_class = ¤t_instance.class_name; // ここでは短期ロックで必要な情報だけ抜き出してすぐ解放する let (has_parent_in_ext, has_parent_in_impl) = { let box_declarations = self.shared.box_declarations.read().unwrap(); - let current_box_decl = box_declarations.get(current_class) - .ok_or(RuntimeError::UndefinedClass { name: current_class.clone() })?; - (current_box_decl.extends.contains(&parent.to_string()), - current_box_decl.implements.contains(&parent.to_string())) + let current_box_decl = + box_declarations + .get(current_class) + .ok_or(RuntimeError::UndefinedClass { + name: current_class.clone(), + })?; + ( + current_box_decl.extends.contains(&parent.to_string()), + current_box_decl.implements.contains(&parent.to_string()), + ) }; // extendsまたはimplementsでparentが指定されているか確認 (Multi-delegation) 🚀 let is_valid_delegation = has_parent_in_ext || has_parent_in_impl; - + if !is_valid_delegation { return Err(RuntimeError::InvalidOperation { message: format!("Class '{}' does not delegate to '{}'. Use 'box {} from {}' to establish delegation.", current_class, parent, current_class, parent), }); } - + // 先にプラグイン親のコンストラクタ/メソッドを優先的に処理(v2プラグイン対応) #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] { @@ -502,20 +610,29 @@ impl NyashInterpreter { match self.create_plugin_box(parent, arguments) { Ok(pbox) => { use std::sync::Arc; - let _ = current_instance.set_field_legacy("__plugin_content", Arc::from(pbox)); + let _ = current_instance + .set_field_legacy("__plugin_content", Arc::from(pbox)); return Ok(Box::new(crate::box_trait::VoidBox::new())); } Err(e) => { return Err(RuntimeError::InvalidOperation { - message: format!("Failed to construct plugin parent '{}': {:?}", parent, e), + message: format!( + "Failed to construct plugin parent '{}': {:?}", + parent, e + ), }); } } } else { // 非コンストラクタ: 既存の __plugin_content を通じてメソッド呼び出し - if let Some(plugin_shared) = current_instance.get_field_legacy("__plugin_content") { + if let Some(plugin_shared) = + current_instance.get_field_legacy("__plugin_content") + { let plugin_ref = &*plugin_shared; - if let Some(plugin) = plugin_ref.as_any().downcast_ref::() { + if let Some(plugin) = plugin_ref + .as_any() + .downcast_ref::( + ) { return self.execute_plugin_box_v2_method(plugin, method, arguments); } } @@ -530,82 +647,108 @@ impl NyashInterpreter { let is_builtin = is_builtin_box(parent) || parent == "EguiBox"; #[cfg(not(all(feature = "gui", not(target_arch = "wasm32"))))] let is_builtin = is_builtin_box(parent); - + // 🔥 Phase 8.9: Transparency system removed - all delegation must be explicit // Removed: if is_builtin && method == parent { ... execute_builtin_constructor_call ... } - + if is_builtin { // ビルトインBoxの場合、直接ビルトインメソッドを実行 - return self.execute_builtin_box_method(parent, method, current_instance_val.clone_box(), arguments); + return self.execute_builtin_box_method( + parent, + method, + current_instance_val.clone_box(), + arguments, + ); } - + // プラグイン親(__plugin_content) #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] { if let Some(plugin_shared) = current_instance.get_field_legacy("__plugin_content") { let plugin_ref = &*plugin_shared; - if let Some(plugin) = plugin_ref.as_any().downcast_ref::() { + if let Some(plugin) = plugin_ref + .as_any() + .downcast_ref::( + ) { return self.execute_plugin_box_v2_method(plugin, method, arguments); } } } - + // 3. 親クラスのBox宣言を取得(ユーザー定義Boxの場合) let parent_box_decl = { let box_declarations = self.shared.box_declarations.read().unwrap(); - box_declarations.get(parent) - .ok_or(RuntimeError::UndefinedClass { - name: parent.to_string() - })? - .clone() + box_declarations + .get(parent) + .ok_or(RuntimeError::UndefinedClass { + name: parent.to_string(), + })? + .clone() }; - + // 4. constructorまたはinitまたはpackまたはbirthの場合の特別処理 - if method == "constructor" || method == "init" || method == "pack" || method == "birth" || method == parent { - return self.execute_from_parent_constructor(parent, &parent_box_decl, current_instance_val.clone_box(), arguments); + if method == "constructor" + || method == "init" + || method == "pack" + || method == "birth" + || method == parent + { + return self.execute_from_parent_constructor( + parent, + &parent_box_decl, + current_instance_val.clone_box(), + arguments, + ); } - + // 5. 親クラスのメソッドを取得 - let parent_method = parent_box_decl.methods.get(method) + let parent_method = parent_box_decl + .methods + .get(method) .ok_or(RuntimeError::InvalidOperation { message: format!("Method '{}' not found in parent class '{}'", method, parent), })? .clone(); - + // 6. 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // 7. 親メソッドを実行 if let ASTNode::FunctionDeclaration { params, body, .. } = parent_method { // パラメータ数チェック if arg_values.len() != params.len() { return Err(RuntimeError::InvalidOperation { - message: format!("Parent method {}.{} expects {} arguments, got {}", - parent, method, params.len(), arg_values.len()), + message: format!( + "Parent method {}.{} expects {} arguments, got {}", + parent, + method, + params.len(), + arg_values.len() + ), }); } - + // 🌍 local変数スタックを保存・クリア(親メソッド実行開始) let saved_locals = self.save_local_vars(); self.local_vars.clear(); - + // 'me'を現在のインスタンスに設定(重要:現在のインスタンスを維持) self.declare_local_variable("me", current_instance_val.clone_or_share()); - + // 引数をlocal変数として設定 for (param, value) in params.iter().zip(arg_values.iter()) { self.declare_local_variable(param, value.clone_or_share()); } - + // 親メソッドの本体を実行(TaskGroupスコープ) crate::runtime::global_hooks::push_task_scope(); let mut result: Box = Box::new(VoidBox::new()); for statement in &body { result = self.execute_statement(statement)?; - + // return文チェック if let super::ControlFlow::Return(return_val) = &self.control_flow { result = return_val.clone_box(); @@ -613,76 +756,97 @@ impl NyashInterpreter { break; } } - + // 🔍 DEBUG: FromCall実行結果をログ出力 - idebug!("🔍 DEBUG: FromCall {}.{} result: {}", parent, method, result.to_string_box().value); - + idebug!( + "🔍 DEBUG: FromCall {}.{} result: {}", + parent, + method, + result.to_string_box().value + ); + // local変数スタックを復元 crate::runtime::global_hooks::pop_task_scope(); self.restore_local_vars(saved_locals); - + Ok(result) } else { Err(RuntimeError::InvalidOperation { - message: format!("Parent method '{}' is not a valid function declaration", method), + message: format!( + "Parent method '{}' is not a valid function declaration", + method + ), }) } } - + /// 🔥 fromCall専用親コンストラクタ実行処理 - from Parent.constructor(arguments) - fn execute_from_parent_constructor(&mut self, parent: &str, parent_box_decl: &super::BoxDeclaration, - current_instance: Box, arguments: &[ASTNode]) - -> Result, RuntimeError> { - + fn execute_from_parent_constructor( + &mut self, + parent: &str, + parent_box_decl: &super::BoxDeclaration, + current_instance: Box, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 1. 親クラスのコンストラクタを取得(引数の数でキーを作成) // "birth/引数数"、"pack/引数数"、"init/引数数"、"Box名/引数数" の順で試す let birth_key = format!("birth/{}", arguments.len()); let pack_key = format!("pack/{}", arguments.len()); let init_key = format!("init/{}", arguments.len()); let box_name_key = format!("{}/{}", parent, arguments.len()); - - let parent_constructor = parent_box_decl.constructors.get(&birth_key) + + let parent_constructor = parent_box_decl + .constructors + .get(&birth_key) .or_else(|| parent_box_decl.constructors.get(&pack_key)) .or_else(|| parent_box_decl.constructors.get(&init_key)) .or_else(|| parent_box_decl.constructors.get(&box_name_key)) .ok_or(RuntimeError::InvalidOperation { - message: format!("No constructor found for parent class '{}' with {} arguments", parent, arguments.len()), + message: format!( + "No constructor found for parent class '{}' with {} arguments", + parent, + arguments.len() + ), })? .clone(); - + // 2. 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // 3. 親コンストラクタを実行 if let ASTNode::FunctionDeclaration { params, body, .. } = parent_constructor { // パラメータ数チェック if arg_values.len() != params.len() { return Err(RuntimeError::InvalidOperation { - message: format!("Parent constructor {} expects {} arguments, got {}", - parent, params.len(), arg_values.len()), + message: format!( + "Parent constructor {} expects {} arguments, got {}", + parent, + params.len(), + arg_values.len() + ), }); } - + // 🌍 local変数スタックを保存・クリア(親コンストラクタ実行開始) let saved_locals = self.save_local_vars(); self.local_vars.clear(); - + // 'me'を現在のインスタンスに設定 self.declare_local_variable("me", current_instance.clone_or_share()); - + // 引数をlocal変数として設定 for (param, value) in params.iter().zip(arg_values.iter()) { self.declare_local_variable(param, value.clone_or_share()); } - + // 親コンストラクタの本体を実行 let mut _result: Box = Box::new(VoidBox::new()); for statement in &body { _result = self.execute_statement(statement)?; - + // return文チェック if let super::ControlFlow::Return(return_val) = &self.control_flow { _result = return_val.clone_box(); @@ -690,10 +854,10 @@ impl NyashInterpreter { break; } } - + // local変数スタックを復元 self.restore_local_vars(saved_locals); - + // 親コンストラクタは通常現在のインスタンスを返す Ok(current_instance) } else { @@ -702,7 +866,7 @@ impl NyashInterpreter { }) } } - + /// Execute method call on PluginBoxV2 #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] fn execute_plugin_box_v2_method( diff --git a/src/interpreter/expressions/mod.rs b/src/interpreter/expressions/mod.rs index 0190eb44..0f31d7c1 100644 --- a/src/interpreter/expressions/mod.rs +++ b/src/interpreter/expressions/mod.rs @@ -1,16 +1,16 @@ /*! * Expression Processing Module - * + * * Extracted from core.rs lines 408-787 (~380 lines) * Handles expression evaluation, binary operations, method calls, and field access * Core philosophy: "Everything is Box" with clean expression evaluation */ // Module declarations -mod operators; -mod calls; mod access; mod builtins; +mod calls; +mod operators; use super::*; use std::sync::Arc; @@ -21,69 +21,178 @@ use std::sync::Arc; impl NyashInterpreter { /// Build closure environment by capturing 'me' and free variables by value (P1) - fn build_closure_env(&mut self, params: &Vec, body: &Vec) -> Result { + fn build_closure_env( + &mut self, + params: &Vec, + body: &Vec, + ) -> Result { use std::collections::HashSet; let mut env = crate::boxes::function_box::ClosureEnv::new(); // Capture 'me' if bound - if let Ok(mev) = self.resolve_variable("me") { env.me_value = Some(Arc::downgrade(&mev)); } + if let Ok(mev) = self.resolve_variable("me") { + env.me_value = Some(Arc::downgrade(&mev)); + } // Collect free variables let mut used: HashSet = HashSet::new(); let mut locals: HashSet = HashSet::new(); // params are considered local - for p in params { locals.insert(p.clone()); } + for p in params { + locals.insert(p.clone()); + } // BFS walk statements fn collect(node: &ASTNode, used: &mut HashSet, locals: &mut HashSet) { match node { ASTNode::Variable { name, .. } => { - if !locals.contains(name) && name != "me" && name != "this" { used.insert(name.clone()); } + if !locals.contains(name) && name != "me" && name != "this" { + used.insert(name.clone()); + } } - ASTNode::Local { variables, .. } => { for v in variables { locals.insert(v.clone()); } } - ASTNode::Assignment { target, value, .. } => { collect(target, used, locals); collect(value, used, locals); } - ASTNode::BinaryOp { left, right, .. } => { collect(left, used, locals); collect(right, used, locals); } - ASTNode::UnaryOp { operand, .. } => { collect(operand, used, locals); } - ASTNode::MethodCall { object, arguments, .. } => { collect(object, used, locals); for a in arguments { collect(a, used, locals);} } - ASTNode::FunctionCall { arguments, .. } => { for a in arguments { collect(a, used, locals);} } - ASTNode::Call { callee, arguments, .. } => { collect(callee, used, locals); for a in arguments { collect(a, used, locals);} } - ASTNode::FieldAccess { object, .. } => { collect(object, used, locals); } - ASTNode::New { arguments, .. } => { for a in arguments { collect(a, used, locals);} } - ASTNode::If { condition, then_body, else_body, .. } => { + ASTNode::Local { variables, .. } => { + for v in variables { + locals.insert(v.clone()); + } + } + ASTNode::Assignment { target, value, .. } => { + collect(target, used, locals); + collect(value, used, locals); + } + ASTNode::BinaryOp { left, right, .. } => { + collect(left, used, locals); + collect(right, used, locals); + } + ASTNode::UnaryOp { operand, .. } => { + collect(operand, used, locals); + } + ASTNode::MethodCall { + object, arguments, .. + } => { + collect(object, used, locals); + for a in arguments { + collect(a, used, locals); + } + } + ASTNode::FunctionCall { arguments, .. } => { + for a in arguments { + collect(a, used, locals); + } + } + ASTNode::Call { + callee, arguments, .. + } => { + collect(callee, used, locals); + for a in arguments { + collect(a, used, locals); + } + } + ASTNode::FieldAccess { object, .. } => { + collect(object, used, locals); + } + ASTNode::New { arguments, .. } => { + for a in arguments { + collect(a, used, locals); + } + } + ASTNode::If { + condition, + then_body, + else_body, + .. + } => { collect(condition, used, locals); - for st in then_body { collect(st, used, locals); } - if let Some(eb) = else_body { for st in eb { collect(st, used, locals); } } + for st in then_body { + collect(st, used, locals); + } + if let Some(eb) = else_body { + for st in eb { + collect(st, used, locals); + } + } } - ASTNode::Loop { condition, body, .. } => { collect(condition, used, locals); for st in body { collect(st, used, locals);} } - ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => { - for st in try_body { collect(st, used, locals); } - for c in catch_clauses { for st in &c.body { collect(st, used, locals); } } - if let Some(fb) = finally_body { for st in fb { collect(st, used, locals); } } + ASTNode::Loop { + condition, body, .. + } => { + collect(condition, used, locals); + for st in body { + collect(st, used, locals); + } } - ASTNode::Throw { expression, .. } => { collect(expression, used, locals); } - ASTNode::Print { expression, .. } => { collect(expression, used, locals); } - ASTNode::Return { value, .. } => { if let Some(v) = value { collect(v, used, locals); } } - ASTNode::AwaitExpression { expression, .. } => { collect(expression, used, locals); } - ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => { + ASTNode::TryCatch { + try_body, + catch_clauses, + finally_body, + .. + } => { + for st in try_body { + collect(st, used, locals); + } + for c in catch_clauses { + for st in &c.body { + collect(st, used, locals); + } + } + if let Some(fb) = finally_body { + for st in fb { + collect(st, used, locals); + } + } + } + ASTNode::Throw { expression, .. } => { + collect(expression, used, locals); + } + ASTNode::Print { expression, .. } => { + collect(expression, used, locals); + } + ASTNode::Return { value, .. } => { + if let Some(v) = value { + collect(v, used, locals); + } + } + ASTNode::AwaitExpression { expression, .. } => { + collect(expression, used, locals); + } + ASTNode::PeekExpr { + scrutinee, + arms, + else_expr, + .. + } => { collect(scrutinee, used, locals); - for (_, e) in arms { collect(e, used, locals); } + for (_, e) in arms { + collect(e, used, locals); + } collect(else_expr, used, locals); } - ASTNode::Program { statements, .. } => { for st in statements { collect(st, used, locals); } } + ASTNode::Program { statements, .. } => { + for st in statements { + collect(st, used, locals); + } + } ASTNode::FunctionDeclaration { params, body, .. } => { let mut inner = locals.clone(); - for p in params { inner.insert(p.clone()); } - for st in body { collect(st, used, &mut inner); } + for p in params { + inner.insert(p.clone()); + } + for st in body { + collect(st, used, &mut inner); + } } _ => {} } } - for st in body { collect(st, &mut used, &mut locals); } + for st in body { + collect(st, &mut used, &mut locals); + } // Materialize captures: local by-ref via RefCellBox, others by-value for name in used.into_iter() { if let Some(local_arc) = self.local_vars.get(&name) { let lb: &dyn NyashBox = &**local_arc; // If already RefCellBox, reuse inner; else wrap and replace local binding - if let Some(rc) = lb.as_any().downcast_ref::() { + if let Some(rc) = lb + .as_any() + .downcast_ref::() + { env.captures.insert(name.clone(), rc.share_box()); } else { // wrap existing into RefCell and replace local binding @@ -93,13 +202,18 @@ impl NyashInterpreter { } } else { // non-local (global/static): by-value capture - if let Ok(v) = self.resolve_variable(&name) { env.captures.insert(name, v.clone_or_share()); } + if let Ok(v) = self.resolve_variable(&name) { + env.captures.insert(name, v.clone_or_share()); + } } } Ok(env) } /// 式を実行 - Expression evaluation engine - pub(super) fn execute_expression(&mut self, expression: &ASTNode) -> Result, RuntimeError> { + pub(super) fn execute_expression( + &mut self, + expression: &ASTNode, + ) -> Result, RuntimeError> { match expression { // P1: allow block (Program) as expression; value = last statement's value ASTNode::Program { statements, .. } => { @@ -111,87 +225,106 @@ impl NyashInterpreter { result = self.execute_statement(st)?; self.discard_context = prev; match &self.control_flow { - ControlFlow::Break => { return Err(RuntimeError::BreakOutsideLoop); } - ControlFlow::Continue => { return Err(RuntimeError::BreakOutsideLoop); } - ControlFlow::Return(_) => { return Err(RuntimeError::ReturnOutsideFunction); } - ControlFlow::Throw(_) => { return Err(RuntimeError::UncaughtException); } + ControlFlow::Break => { + return Err(RuntimeError::BreakOutsideLoop); + } + ControlFlow::Continue => { + return Err(RuntimeError::BreakOutsideLoop); + } + ControlFlow::Return(_) => { + return Err(RuntimeError::ReturnOutsideFunction); + } + ControlFlow::Throw(_) => { + return Err(RuntimeError::UncaughtException); + } ControlFlow::None => {} } } Ok(result) } - ASTNode::Literal { value, .. } => { - Ok(value.to_nyash_box()) - } - + ASTNode::Literal { value, .. } => Ok(value.to_nyash_box()), + ASTNode::Variable { name, .. } => { // 🌍 革命的変数解決:local変数 → GlobalBoxフィールド → エラー - let shared_var = self.resolve_variable(name) - .map_err(|_| RuntimeError::UndefinedVariableAt { - name: name.clone(), - span: expression.span() - })?; - Ok((*shared_var).share_box()) // 🎯 State-sharing instead of cloning + let shared_var = + self.resolve_variable(name) + .map_err(|_| RuntimeError::UndefinedVariableAt { + name: name.clone(), + span: expression.span(), + })?; + Ok((*shared_var).share_box()) // 🎯 State-sharing instead of cloning } - - ASTNode::BinaryOp { operator, left, right, .. } => { - self.execute_binary_op(operator, left, right) - } - - ASTNode::UnaryOp { operator, operand, .. } => { - self.execute_unary_op(operator, operand) - } - - ASTNode::AwaitExpression { expression, .. } => { - self.execute_await(expression) - } - - ASTNode::MethodCall { object, method, arguments, .. } => { + + ASTNode::BinaryOp { + operator, + left, + right, + .. + } => self.execute_binary_op(operator, left, right), + + ASTNode::UnaryOp { + operator, operand, .. + } => self.execute_unary_op(operator, operand), + + ASTNode::AwaitExpression { expression, .. } => self.execute_await(expression), + + ASTNode::MethodCall { + object, + method, + arguments, + .. + } => { let result = self.execute_method_call(object, method, arguments); result } - + ASTNode::FieldAccess { object, field, .. } => { let shared_result = self.execute_field_access(object, field)?; Ok((*shared_result).clone_or_share()) } - - ASTNode::New { class, arguments, type_arguments, .. } => { - self.execute_new(class, arguments, type_arguments) - } - + + ASTNode::New { + class, + arguments, + type_arguments, + .. + } => self.execute_new(class, arguments, type_arguments), + ASTNode::This { .. } => { // 🌍 革命的this解決:local変数から取得 - let shared_this = self.resolve_variable("me") - .map_err(|_| RuntimeError::InvalidOperation { - message: "'this' is only available inside methods".to_string(), - })?; + let shared_this = + self.resolve_variable("me") + .map_err(|_| RuntimeError::InvalidOperation { + message: "'this' is only available inside methods".to_string(), + })?; Ok((*shared_this).clone_or_share()) } - + ASTNode::Me { .. } => { - // 🌍 革命的me解決:local変数から取得(thisと同じ) - let shared_me = self.resolve_variable("me") - .map_err(|_| RuntimeError::InvalidOperation { - message: "'me' is only available inside methods".to_string(), - })?; - + let shared_me = + self.resolve_variable("me") + .map_err(|_| RuntimeError::InvalidOperation { + message: "'me' is only available inside methods".to_string(), + })?; + Ok((*shared_me).clone_or_share()) } - + ASTNode::ThisField { field, .. } => { // 🌍 革命的this.fieldアクセス:local変数から取得 - let this_value = self.resolve_variable("me") - .map_err(|_| RuntimeError::InvalidOperation { - message: "'this' is not bound in the current context".to_string(), - })?; - - if let Some(instance) = (*this_value).as_any().downcast_ref::() { - let shared_field = instance.get_field(field) - .ok_or_else(|| RuntimeError::InvalidOperation { - message: format!("Field '{}' not found on this", field) + let this_value = + self.resolve_variable("me") + .map_err(|_| RuntimeError::InvalidOperation { + message: "'this' is not bound in the current context".to_string(), })?; + + if let Some(instance) = (*this_value).as_any().downcast_ref::() { + let shared_field = instance.get_field(field).ok_or_else(|| { + RuntimeError::InvalidOperation { + message: format!("Field '{}' not found on this", field), + } + })?; Ok((*shared_field).clone_or_share()) } else { Err(RuntimeError::TypeError { @@ -199,19 +332,21 @@ impl NyashInterpreter { }) } } - + ASTNode::MeField { field, .. } => { // 🌍 革命的me.fieldアクセス:local変数から取得 - let me_value = self.resolve_variable("me") - .map_err(|_| RuntimeError::InvalidOperation { - message: "'this' is not bound in the current context".to_string(), - })?; - - if let Some(instance) = (*me_value).as_any().downcast_ref::() { - let shared_field = instance.get_field(field) - .ok_or_else(|| RuntimeError::InvalidOperation { - message: format!("Field '{}' not found on me", field) + let me_value = + self.resolve_variable("me") + .map_err(|_| RuntimeError::InvalidOperation { + message: "'this' is not bound in the current context".to_string(), })?; + + if let Some(instance) = (*me_value).as_any().downcast_ref::() { + let shared_field = instance.get_field(field).ok_or_else(|| { + RuntimeError::InvalidOperation { + message: format!("Field '{}' not found on me", field), + } + })?; Ok((*shared_field).clone_or_share()) } else { Err(RuntimeError::TypeError { @@ -219,30 +354,48 @@ impl NyashInterpreter { }) } } - - ASTNode::FunctionCall { name, arguments, .. } => { - self.execute_function_call(name, arguments) - } - ASTNode::Call { callee, arguments, .. } => { + + ASTNode::FunctionCall { + name, arguments, .. + } => self.execute_function_call(name, arguments), + ASTNode::Call { + callee, arguments, .. + } => { // callee を評価して FunctionBox なら本体を実行 let callee_val = self.execute_expression(callee)?; - if let Some(fun) = callee_val.as_any().downcast_ref::() { + if let Some(fun) = callee_val + .as_any() + .downcast_ref::() + { // 引数評価 let mut arg_values: Vec> = Vec::new(); - for a in arguments { arg_values.push(self.execute_expression(a)?); } + for a in arguments { + arg_values.push(self.execute_expression(a)?); + } if arg_values.len() != fun.params.len() { - return Err(RuntimeError::InvalidOperation { message: format!("Function expects {} args, got {}", fun.params.len(), arg_values.len()) }); + return Err(RuntimeError::InvalidOperation { + message: format!( + "Function expects {} args, got {}", + fun.params.len(), + arg_values.len() + ), + }); } // スコープ保存 let saved_locals = self.save_local_vars(); self.local_vars.clear(); // キャプチャ注入(by-value) - for (k, v) in fun.env.captures.iter() { self.declare_local_variable(k, v.clone_or_share()); } + for (k, v) in fun.env.captures.iter() { + self.declare_local_variable(k, v.clone_or_share()); + } if let Some(me_w) = &fun.env.me_value { if let Some(me_arc) = me_w.upgrade() { self.declare_local_variable("me", (*me_arc).clone_or_share()); } else { - self.declare_local_variable("me", Box::new(crate::boxes::null_box::NullBox::new())); + self.declare_local_variable( + "me", + Box::new(crate::boxes::null_box::NullBox::new()), + ); } } for (p, v) in fun.params.iter().zip(arg_values.iter()) { @@ -265,30 +418,52 @@ impl NyashInterpreter { } else if let ASTNode::Lambda { params, body, .. } = callee.as_ref() { // 直書きLambdaは従来通り実行(後方互換) let mut arg_values: Vec> = Vec::new(); - for a in arguments { arg_values.push(self.execute_expression(a)?); } + for a in arguments { + arg_values.push(self.execute_expression(a)?); + } if arg_values.len() != params.len() { - return Err(RuntimeError::InvalidOperation { message: format!("Lambda expects {} args, got {}", params.len(), arg_values.len()) }); + return Err(RuntimeError::InvalidOperation { + message: format!( + "Lambda expects {} args, got {}", + params.len(), + arg_values.len() + ), + }); } let saved_locals = self.save_local_vars(); self.local_vars.clear(); - for (p, v) in params.iter().zip(arg_values.iter()) { self.declare_local_variable(p, v.clone_or_share()); } + for (p, v) in params.iter().zip(arg_values.iter()) { + self.declare_local_variable(p, v.clone_or_share()); + } crate::runtime::global_hooks::push_task_scope(); let mut result: Box = Box::new(VoidBox::new()); - for st in body { result = self.execute_statement(st)?; if let super::ControlFlow::Return(rv) = &self.control_flow { result = rv.clone_box(); self.control_flow = super::ControlFlow::None; break; } } + for st in body { + result = self.execute_statement(st)?; + if let super::ControlFlow::Return(rv) = &self.control_flow { + result = rv.clone_box(); + self.control_flow = super::ControlFlow::None; + break; + } + } crate::runtime::global_hooks::pop_task_scope(); self.restore_local_vars(saved_locals); Ok(result) } else { - Err(RuntimeError::InvalidOperation { message: "Callee is not callable".to_string() }) + Err(RuntimeError::InvalidOperation { + message: "Callee is not callable".to_string(), + }) } } - - ASTNode::Arrow { sender, receiver, .. } => { - self.execute_arrow(sender, receiver) - } + + ASTNode::Arrow { + sender, receiver, .. + } => self.execute_arrow(sender, receiver), ASTNode::QMarkPropagate { expression, .. } => { let v = self.execute_expression(expression)?; - if let Some(res) = v.as_any().downcast_ref::() { + if let Some(res) = v + .as_any() + .downcast_ref::() + { // ok -> unwrap, err -> early return (propagate) if matches!(res, crate::boxes::result::NyashResultBox::Ok(_)) { return Ok(res.get_value()); @@ -301,7 +476,12 @@ impl NyashInterpreter { // Not a Result: pass-through Ok(v) } - ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => { + ASTNode::PeekExpr { + scrutinee, + arms, + else_expr, + .. + } => { let val = self.execute_expression(scrutinee)?; let sval = val.to_string_box().value; for (pat, expr) in arms { @@ -309,7 +489,13 @@ impl NyashInterpreter { crate::ast::LiteralValue::String(s) => s.clone(), crate::ast::LiteralValue::Integer(i) => i.to_string(), crate::ast::LiteralValue::Float(f) => f.to_string(), - crate::ast::LiteralValue::Bool(b) => if *b { "true".to_string() } else { "false".to_string() }, + crate::ast::LiteralValue::Bool(b) => { + if *b { + "true".to_string() + } else { + "false".to_string() + } + } crate::ast::LiteralValue::Null => "null".to_string(), crate::ast::LiteralValue::Void => "void".to_string(), }; @@ -322,27 +508,31 @@ impl NyashInterpreter { ASTNode::Lambda { params, body, .. } => { // 値としての関数ボックスを生成(ClosureEnv: me/by-val captures) let env = self.build_closure_env(¶ms, body)?; - Ok(Box::new(crate::boxes::function_box::FunctionBox::with_env(params.clone(), body.clone(), env))) + Ok(Box::new(crate::boxes::function_box::FunctionBox::with_env( + params.clone(), + body.clone(), + env, + ))) } - + ASTNode::Include { filename, .. } => { // include式: 最初のstatic boxを返す self.execute_include_expr(filename) } - - ASTNode::FromCall { parent, method, arguments, .. } => { - self.execute_from_call(parent, method, arguments) - } - + + ASTNode::FromCall { + parent, + method, + arguments, + .. + } => self.execute_from_call(parent, method, arguments), + _ => Err(RuntimeError::InvalidOperation { message: format!("Cannot execute {:?} as expression", expression.node_type()), }), } } - - - - + /// 🔄 循環参照検出: オブジェクトの一意IDを取得 #[allow(dead_code)] fn get_object_id(&self, node: &ASTNode) -> Option { @@ -353,16 +543,16 @@ impl NyashInterpreter { } ASTNode::Me { .. } => { // 'me'参照の特別なID - Some(usize::MAX) + Some(usize::MAX) } ASTNode::This { .. } => { - // 'this'参照の特別なID + // 'this'参照の特別なID Some(usize::MAX - 1) } _ => None, // 他のノードタイプはID追跡しない } } - + /// 🔄 文字列のシンプルなハッシュ関数 #[allow(dead_code)] fn hash_string(&self, s: &str) -> usize { @@ -372,13 +562,13 @@ impl NyashInterpreter { } hash } - + // fn box_to_nyash_value(&self, box_val: &Box) -> Option { // // Try to convert the box back to NyashValue for weak reference operations // // This is a simplified conversion - in reality we might need more sophisticated logic // use nyash_rust::value::NyashValue; // use crate::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox}; - // + // // if let Some(string_box) = box_val.as_any().downcast_ref::() { // Some(NyashValue::String(string_box.value.clone())) // } else if let Some(int_box) = box_val.as_any().downcast_ref::() { @@ -395,6 +585,4 @@ impl NyashInterpreter { // None // Simplified for now // } // } - - } diff --git a/src/interpreter/expressions/operators.rs b/src/interpreter/expressions/operators.rs index 1d2547ab..c42ef66e 100644 --- a/src/interpreter/expressions/operators.rs +++ b/src/interpreter/expressions/operators.rs @@ -4,18 +4,21 @@ // Removed super::* import - specific imports below use crate::ast::{ASTNode, BinaryOperator, UnaryOperator}; -use crate::box_trait::{NyashBox, BoolBox, CompareBox}; -use crate::box_trait::{IntegerBox, StringBox}; // 🔧 修正: box_trait::*に統一 -use crate::boxes::FloatBox; // FloatBoxはboxesのみに存在 -use crate::interpreter::{NyashInterpreter, RuntimeError}; +use crate::box_trait::{BoolBox, CompareBox, NyashBox}; +use crate::box_trait::{IntegerBox, StringBox}; // 🔧 修正: box_trait::*に統一 +use crate::boxes::FloatBox; // FloatBoxはboxesのみに存在 use crate::instance_v2::InstanceBox; +use crate::interpreter::{NyashInterpreter, RuntimeError}; // Local helper functions to bypass import issues /// InstanceBoxでラップされている場合、内部のBoxを取得する /// シンプルなヘルパー関数で型地獄を回避 fn unwrap_instance(boxed: &dyn NyashBox) -> &dyn NyashBox { - eprintln!("🔍 DEBUG unwrap_instance: input type = {}", boxed.type_name()); + eprintln!( + "🔍 DEBUG unwrap_instance: input type = {}", + boxed.type_name() + ); if let Some(instance) = boxed.as_any().downcast_ref::() { eprintln!(" ✅ Is InstanceBox"); if let Some(ref inner) = instance.inner_content { @@ -28,156 +31,226 @@ fn unwrap_instance(boxed: &dyn NyashBox) -> &dyn NyashBox { } fn best_effort_to_string(val: &dyn NyashBox) -> String { - crate::runtime::semantics::coerce_to_string(val) - .unwrap_or_else(|| val.to_string_box().value) + crate::runtime::semantics::coerce_to_string(val).unwrap_or_else(|| val.to_string_box().value) } fn best_effort_to_i64(val: &dyn NyashBox) -> Option { crate::runtime::semantics::coerce_to_i64(val) } -pub(super) fn try_add_operation(left: &dyn NyashBox, right: &dyn NyashBox) -> Option> { +pub(super) fn try_add_operation( + left: &dyn NyashBox, + right: &dyn NyashBox, +) -> Option> { // 🎯 InstanceBoxのunwrap処理 let left = unwrap_instance(left); let right = unwrap_instance(right); - + // IntegerBox + IntegerBox if let (Some(left_int), Some(right_int)) = ( left.as_any().downcast_ref::(), - right.as_any().downcast_ref::() + right.as_any().downcast_ref::(), ) { return Some(Box::new(IntegerBox::new(left_int.value + right_int.value))); } - + // StringBox + anything -> concatenation if let Some(left_str) = left.as_any().downcast_ref::() { let right_str = right.to_string_box(); - return Some(Box::new(StringBox::new(format!("{}{}", left_str.value, right_str.value)))); + return Some(Box::new(StringBox::new(format!( + "{}{}", + left_str.value, right_str.value + )))); } - - // BoolBox + BoolBox -> IntegerBox + + // BoolBox + BoolBox -> IntegerBox if let (Some(left_bool), Some(right_bool)) = ( left.as_any().downcast_ref::(), - right.as_any().downcast_ref::() + right.as_any().downcast_ref::(), ) { - return Some(Box::new(IntegerBox::new((left_bool.value as i64) + (right_bool.value as i64)))); + return Some(Box::new(IntegerBox::new( + (left_bool.value as i64) + (right_bool.value as i64), + ))); } - + None } -pub(super) fn try_sub_operation(left: &dyn NyashBox, right: &dyn NyashBox) -> Option> { +pub(super) fn try_sub_operation( + left: &dyn NyashBox, + right: &dyn NyashBox, +) -> Option> { // 🎯 InstanceBoxのunwrap処理 let left = unwrap_instance(left); let right = unwrap_instance(right); - + // IntegerBox - IntegerBox if let (Some(left_int), Some(right_int)) = ( left.as_any().downcast_ref::(), - right.as_any().downcast_ref::() + right.as_any().downcast_ref::(), ) { return Some(Box::new(IntegerBox::new(left_int.value - right_int.value))); } None } -pub(super) fn try_mul_operation(left: &dyn NyashBox, right: &dyn NyashBox) -> Option> { +pub(super) fn try_mul_operation( + left: &dyn NyashBox, + right: &dyn NyashBox, +) -> Option> { // 🎯 InstanceBoxのunwrap処理 let left = unwrap_instance(left); let right = unwrap_instance(right); - + // デバッグ出力 - eprintln!("🔍 DEBUG try_mul: left type = {}, right type = {}", left.type_name(), right.type_name()); - + eprintln!( + "🔍 DEBUG try_mul: left type = {}, right type = {}", + left.type_name(), + right.type_name() + ); + // IntegerBox * IntegerBox if let (Some(left_int), Some(right_int)) = ( left.as_any().downcast_ref::(), - right.as_any().downcast_ref::() + right.as_any().downcast_ref::(), ) { - eprintln!("✅ IntegerBox downcast success: {} * {}", left_int.value, right_int.value); + eprintln!( + "✅ IntegerBox downcast success: {} * {}", + left_int.value, right_int.value + ); return Some(Box::new(IntegerBox::new(left_int.value * right_int.value))); } - + // box_trait::IntegerBoxも試す eprintln!("❌ box_trait::IntegerBox downcast failed, trying boxes::integer_box::IntegerBox"); - + // boxes::integer_box::IntegerBoxを試す use crate::boxes::integer_box::IntegerBox as BoxesIntegerBox; if let (Some(left_int), Some(right_int)) = ( left.as_any().downcast_ref::(), - right.as_any().downcast_ref::() + right.as_any().downcast_ref::(), ) { - eprintln!("✅ boxes::IntegerBox downcast success: {} * {}", left_int.value, right_int.value); + eprintln!( + "✅ boxes::IntegerBox downcast success: {} * {}", + left_int.value, right_int.value + ); return Some(Box::new(IntegerBox::new(left_int.value * right_int.value))); } - + // StringBox * IntegerBox -> repetition if let (Some(str_box), Some(count_int)) = ( left.as_any().downcast_ref::(), - right.as_any().downcast_ref::() + right.as_any().downcast_ref::(), ) { - return Some(Box::new(StringBox::new(str_box.value.repeat(count_int.value as usize)))); + return Some(Box::new(StringBox::new( + str_box.value.repeat(count_int.value as usize), + ))); } - + None } -pub(super) fn try_div_operation(left: &dyn NyashBox, right: &dyn NyashBox) -> Result, String> { +pub(super) fn try_div_operation( + left: &dyn NyashBox, + right: &dyn NyashBox, +) -> Result, String> { // 🎯 InstanceBoxのunwrap処理 let left = unwrap_instance(left); let right = unwrap_instance(right); - + // IntegerBox / IntegerBox if let (Some(left_int), Some(right_int)) = ( left.as_any().downcast_ref::(), - right.as_any().downcast_ref::() + right.as_any().downcast_ref::(), ) { if right_int.value == 0 { return Err("Division by zero".to_string()); } return Ok(Box::new(IntegerBox::new(left_int.value / right_int.value))); } - - Err(format!("Division not supported between {} and {}", left.type_name(), right.type_name())) + + Err(format!( + "Division not supported between {} and {}", + left.type_name(), + right.type_name() + )) } -pub(super) fn try_mod_operation(left: &dyn NyashBox, right: &dyn NyashBox) -> Result, String> { +pub(super) fn try_mod_operation( + left: &dyn NyashBox, + right: &dyn NyashBox, +) -> Result, String> { // IntegerBox % IntegerBox if let (Some(left_int), Some(right_int)) = ( left.as_any().downcast_ref::(), - right.as_any().downcast_ref::() + right.as_any().downcast_ref::(), ) { if right_int.value == 0 { return Err("Modulo by zero".to_string()); } return Ok(Box::new(IntegerBox::new(left_int.value % right_int.value))); } - - Err(format!("Modulo not supported between {} and {}", left.type_name(), right.type_name())) + + Err(format!( + "Modulo not supported between {} and {}", + left.type_name(), + right.type_name() + )) } impl NyashInterpreter { /// 二項演算を実行 - Binary operation processing - pub(super) fn execute_binary_op(&mut self, op: &BinaryOperator, left: &ASTNode, right: &ASTNode) - -> Result, RuntimeError> { + pub(super) fn execute_binary_op( + &mut self, + op: &BinaryOperator, + left: &ASTNode, + right: &ASTNode, + ) -> Result, RuntimeError> { let left_val = self.execute_expression(left)?; let right_val = self.execute_expression(right)?; // Binary operation execution - + match op { BinaryOperator::Add => { // Optional: enforce grammar rule for add (behind env) if std::env::var("NYASH_GRAMMAR_ENFORCE_ADD").ok().as_deref() == Some("1") { - let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref()).is_some() { "String" } else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref()).is_some() { "Integer" } else { "Other" }; - let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref()).is_some() { "String" } else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref()).is_some() { "Integer" } else { "Other" }; - if let Some((res, _act)) = crate::grammar::engine::get().decide_add_result(lty, rty) { + let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref()) + .is_some() + { + "String" + } else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref()).is_some() + { + "Integer" + } else { + "Other" + }; + let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref()) + .is_some() + { + "String" + } else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref()).is_some() + { + "Integer" + } else { + "Other" + }; + if let Some((res, _act)) = + crate::grammar::engine::get().decide_add_result(lty, rty) + { match res { "String" => { - let ls = crate::runtime::semantics::coerce_to_string(left_val.as_ref()).unwrap_or_else(|| left_val.to_string_box().value); - let rs = crate::runtime::semantics::coerce_to_string(right_val.as_ref()).unwrap_or_else(|| right_val.to_string_box().value); + let ls = + crate::runtime::semantics::coerce_to_string(left_val.as_ref()) + .unwrap_or_else(|| left_val.to_string_box().value); + let rs = + crate::runtime::semantics::coerce_to_string(right_val.as_ref()) + .unwrap_or_else(|| right_val.to_string_box().value); return Ok(Box::new(StringBox::new(format!("{}{}", ls, rs)))); } "Integer" => { - if let (Some(li), Some(ri)) = (crate::runtime::semantics::coerce_to_i64(left_val.as_ref()), crate::runtime::semantics::coerce_to_i64(right_val.as_ref())) { + if let (Some(li), Some(ri)) = ( + crate::runtime::semantics::coerce_to_i64(left_val.as_ref()), + crate::runtime::semantics::coerce_to_i64(right_val.as_ref()), + ) { return Ok(Box::new(IntegerBox::new(li + ri))); } } @@ -185,17 +258,53 @@ impl NyashInterpreter { } } } - let (strat, lty, rty, expect) = if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { - let strat = crate::grammar::engine::get().add_coercion_strategy(); - let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref()).is_some() { "String" } else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref()).is_some() { "Integer" } else { "Other" }; - let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref()).is_some() { "String" } else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref()).is_some() { "Integer" } else { "Other" }; - let rule = crate::grammar::engine::get().decide_add_result(lty, rty); - (Some(strat.to_string()), Some(lty.to_string()), Some(rty.to_string()), rule.map(|(res, act)| (res.to_string(), act.to_string()))) - } else { (None, None, None, None) }; + let (strat, lty, rty, expect) = + if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { + let strat = crate::grammar::engine::get().add_coercion_strategy(); + let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref()) + .is_some() + { + "String" + } else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref()) + .is_some() + { + "Integer" + } else { + "Other" + }; + let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref()) + .is_some() + { + "String" + } else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref()) + .is_some() + { + "Integer" + } else { + "Other" + }; + let rule = crate::grammar::engine::get().decide_add_result(lty, rty); + ( + Some(strat.to_string()), + Some(lty.to_string()), + Some(rty.to_string()), + rule.map(|(res, act)| (res.to_string(), act.to_string())), + ) + } else { + (None, None, None, None) + }; // 1) Intrinsic fast-paths (Integer+Integer, String+*, Bool+Bool) if let Some(result) = try_add_operation(left_val.as_ref(), right_val.as_ref()) { - if let (Some(s), Some(l), Some(r)) = (strat.as_ref(), lty.as_ref(), rty.as_ref()) { - let actual = if result.as_any().downcast_ref::().is_some() { "String" } else if result.as_any().downcast_ref::().is_some() { "Integer" } else { "Other" }; + if let (Some(s), Some(l), Some(r)) = + (strat.as_ref(), lty.as_ref(), rty.as_ref()) + { + let actual = if result.as_any().downcast_ref::().is_some() { + "String" + } else if result.as_any().downcast_ref::().is_some() { + "Integer" + } else { + "Other" + }; eprintln!("[GRAMMAR-DIFF][Interp] add strat={} lty={} rty={} expect={:?} actual={} match={}", s, l, r, expect, actual, expect.as_ref().map(|(res,_)| res.as_str())==Some(actual)); } return Ok(result); @@ -206,14 +315,21 @@ impl NyashInterpreter { if ls_opt.is_some() || rs_opt.is_some() { let ls = ls_opt.unwrap_or_else(|| left_val.to_string_box().value); let rs = rs_opt.unwrap_or_else(|| right_val.to_string_box().value); - if let (Some(s), Some(l), Some(r)) = (strat.as_ref(), lty.as_ref(), rty.as_ref()) { + if let (Some(s), Some(l), Some(r)) = + (strat.as_ref(), lty.as_ref(), rty.as_ref()) + { eprintln!("[GRAMMAR-DIFF][Interp] add strat={} lty={} rty={} expect={:?} actual=String match={}", s, l, r, expect, expect.as_ref().map(|(res,_)| res=="String").unwrap_or(false)); } return Ok(Box::new(StringBox::new(format!("{}{}", ls, rs)))); } // 3) Numeric fallback via coerce_to_i64 - if let (Some(li), Some(ri)) = (crate::runtime::semantics::coerce_to_i64(left_val.as_ref()), crate::runtime::semantics::coerce_to_i64(right_val.as_ref())) { - if let (Some(s), Some(l), Some(r)) = (strat.as_ref(), lty.as_ref(), rty.as_ref()) { + if let (Some(li), Some(ri)) = ( + crate::runtime::semantics::coerce_to_i64(left_val.as_ref()), + crate::runtime::semantics::coerce_to_i64(right_val.as_ref()), + ) { + if let (Some(s), Some(l), Some(r)) = + (strat.as_ref(), lty.as_ref(), rty.as_ref()) + { eprintln!("[GRAMMAR-DIFF][Interp] add strat={} lty={} rty={} expect={:?} actual=Integer match={}", s, l, r, expect, expect.as_ref().map(|(res,_)| res=="Integer").unwrap_or(false)); } return Ok(Box::new(IntegerBox::new(li + ri))); @@ -222,22 +338,25 @@ impl NyashInterpreter { if let (Some(s), Some(l), Some(r)) = (strat.as_ref(), lty.as_ref(), rty.as_ref()) { eprintln!("[GRAMMAR-DIFF][Interp] add strat={} lty={} rty={} expect={:?} actual=Error", s, l, r, expect); } - Err(RuntimeError::InvalidOperation { - message: format!("Addition not supported between {} and {}", - left_val.type_name(), right_val.type_name()) + Err(RuntimeError::InvalidOperation { + message: format!( + "Addition not supported between {} and {}", + left_val.type_name(), + right_val.type_name() + ), }) } - + BinaryOperator::Equal => { let result = left_val.equals(right_val.as_ref()); Ok(Box::new(result)) } - + BinaryOperator::NotEqual => { let result = left_val.equals(right_val.as_ref()); Ok(Box::new(BoolBox::new(!result.value))) } - + BinaryOperator::And => { let left_bool = self.is_truthy(&left_val); if !left_bool { @@ -247,7 +366,7 @@ impl NyashInterpreter { Ok(Box::new(BoolBox::new(right_bool))) } } - + BinaryOperator::Or => { let left_bool = self.is_truthy(&left_val); if left_bool { @@ -257,72 +376,137 @@ impl NyashInterpreter { Ok(Box::new(BoolBox::new(right_bool))) } } - + BinaryOperator::Subtract => { if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { let strat = crate::grammar::engine::get().sub_coercion_strategy(); - let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref()).is_some() { "String" } else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref()).is_some() { "Integer" } else { "Other" }; - let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref()).is_some() { "String" } else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref()).is_some() { "Integer" } else { "Other" }; + let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref()) + .is_some() + { + "String" + } else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref()).is_some() + { + "Integer" + } else { + "Other" + }; + let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref()) + .is_some() + { + "String" + } else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref()).is_some() + { + "Integer" + } else { + "Other" + }; let rule = crate::grammar::engine::get().decide_sub_result(lty, rty); - eprintln!("[GRAMMAR-DIFF][Interp] sub strat={} lty={} rty={} expect={:?}", strat, lty, rty, rule); + eprintln!( + "[GRAMMAR-DIFF][Interp] sub strat={} lty={} rty={} expect={:?}", + strat, lty, rty, rule + ); } // Use helper function instead of trait methods if let Some(result) = try_sub_operation(left_val.as_ref(), right_val.as_ref()) { return Ok(result); } - - Err(RuntimeError::InvalidOperation { - message: format!("Subtraction not supported between {} and {}", - left_val.type_name(), right_val.type_name()) + + Err(RuntimeError::InvalidOperation { + message: format!( + "Subtraction not supported between {} and {}", + left_val.type_name(), + right_val.type_name() + ), }) } - + BinaryOperator::Multiply => { if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { let strat = crate::grammar::engine::get().mul_coercion_strategy(); - let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref()).is_some() { "String" } else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref()).is_some() { "Integer" } else { "Other" }; - let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref()).is_some() { "String" } else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref()).is_some() { "Integer" } else { "Other" }; + let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref()) + .is_some() + { + "String" + } else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref()).is_some() + { + "Integer" + } else { + "Other" + }; + let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref()) + .is_some() + { + "String" + } else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref()).is_some() + { + "Integer" + } else { + "Other" + }; let rule = crate::grammar::engine::get().decide_mul_result(lty, rty); - eprintln!("[GRAMMAR-DIFF][Interp] mul strat={} lty={} rty={} expect={:?}", strat, lty, rty, rule); + eprintln!( + "[GRAMMAR-DIFF][Interp] mul strat={} lty={} rty={} expect={:?}", + strat, lty, rty, rule + ); } // Use helper function instead of trait methods if let Some(result) = try_mul_operation(left_val.as_ref(), right_val.as_ref()) { return Ok(result); } - - Err(RuntimeError::InvalidOperation { - message: format!("Multiplication not supported between {} and {}", - left_val.type_name(), right_val.type_name()) + + Err(RuntimeError::InvalidOperation { + message: format!( + "Multiplication not supported between {} and {}", + left_val.type_name(), + right_val.type_name() + ), }) } - + BinaryOperator::Divide => { if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { let strat = crate::grammar::engine::get().div_coercion_strategy(); - let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref()).is_some() { "String" } else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref()).is_some() { "Integer" } else { "Other" }; - let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref()).is_some() { "String" } else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref()).is_some() { "Integer" } else { "Other" }; + let lty = if crate::runtime::semantics::coerce_to_string(left_val.as_ref()) + .is_some() + { + "String" + } else if crate::runtime::semantics::coerce_to_i64(left_val.as_ref()).is_some() + { + "Integer" + } else { + "Other" + }; + let rty = if crate::runtime::semantics::coerce_to_string(right_val.as_ref()) + .is_some() + { + "String" + } else if crate::runtime::semantics::coerce_to_i64(right_val.as_ref()).is_some() + { + "Integer" + } else { + "Other" + }; let rule = crate::grammar::engine::get().decide_div_result(lty, rty); - eprintln!("[GRAMMAR-DIFF][Interp] div strat={} lty={} rty={} expect={:?}", strat, lty, rty, rule); + eprintln!( + "[GRAMMAR-DIFF][Interp] div strat={} lty={} rty={} expect={:?}", + strat, lty, rty, rule + ); } // Use helper function instead of trait methods match try_div_operation(left_val.as_ref(), right_val.as_ref()) { Ok(result) => Ok(result), - Err(error_msg) => Err(RuntimeError::InvalidOperation { - message: error_msg - }) + Err(error_msg) => Err(RuntimeError::InvalidOperation { message: error_msg }), } } - + BinaryOperator::Modulo => { // Use helper function for modulo operation match try_mod_operation(left_val.as_ref(), right_val.as_ref()) { Ok(result) => Ok(result), - Err(error_msg) => Err(RuntimeError::InvalidOperation { - message: error_msg - }) + Err(error_msg) => Err(RuntimeError::InvalidOperation { message: error_msg }), } } - + BinaryOperator::Shl => { // Integer-only left shift if let (Some(li), Some(ri)) = ( @@ -332,8 +516,12 @@ impl NyashInterpreter { let sh = (ri as u32) & 63; return Ok(Box::new(IntegerBox::new(li.wrapping_shl(sh)))); } - Err(RuntimeError::TypeError { - message: format!("Shift-left '<<' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) + Err(RuntimeError::TypeError { + message: format!( + "Shift-left '<<' requires integers (got {} and {})", + left_val.type_name(), + right_val.type_name() + ), }) } BinaryOperator::Shr => { @@ -344,59 +532,90 @@ impl NyashInterpreter { let sh = (ri as u32) & 63; return Ok(Box::new(IntegerBox::new(((li as u64) >> sh) as i64))); } - Err(RuntimeError::TypeError { - message: format!("Shift-right '>>' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) + Err(RuntimeError::TypeError { + message: format!( + "Shift-right '>>' requires integers (got {} and {})", + left_val.type_name(), + right_val.type_name() + ), }) } BinaryOperator::BitAnd => { if let (Some(li), Some(ri)) = ( crate::runtime::semantics::coerce_to_i64(left_val.as_ref()), crate::runtime::semantics::coerce_to_i64(right_val.as_ref()), - ) { return Ok(Box::new(IntegerBox::new(li & ri))); } - Err(RuntimeError::TypeError { message: format!("Bitwise '&' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) }) + ) { + return Ok(Box::new(IntegerBox::new(li & ri))); + } + Err(RuntimeError::TypeError { + message: format!( + "Bitwise '&' requires integers (got {} and {})", + left_val.type_name(), + right_val.type_name() + ), + }) } BinaryOperator::BitOr => { if let (Some(li), Some(ri)) = ( crate::runtime::semantics::coerce_to_i64(left_val.as_ref()), crate::runtime::semantics::coerce_to_i64(right_val.as_ref()), - ) { return Ok(Box::new(IntegerBox::new(li | ri))); } - Err(RuntimeError::TypeError { message: format!("Bitwise '|' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) }) + ) { + return Ok(Box::new(IntegerBox::new(li | ri))); + } + Err(RuntimeError::TypeError { + message: format!( + "Bitwise '|' requires integers (got {} and {})", + left_val.type_name(), + right_val.type_name() + ), + }) } BinaryOperator::BitXor => { if let (Some(li), Some(ri)) = ( crate::runtime::semantics::coerce_to_i64(left_val.as_ref()), crate::runtime::semantics::coerce_to_i64(right_val.as_ref()), - ) { return Ok(Box::new(IntegerBox::new(li ^ ri))); } - Err(RuntimeError::TypeError { message: format!("Bitwise '^' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) }) + ) { + return Ok(Box::new(IntegerBox::new(li ^ ri))); + } + Err(RuntimeError::TypeError { + message: format!( + "Bitwise '^' requires integers (got {} and {})", + left_val.type_name(), + right_val.type_name() + ), + }) } - + BinaryOperator::Less => { let result = CompareBox::less(left_val.as_ref(), right_val.as_ref()); Ok(Box::new(result)) } - + BinaryOperator::Greater => { let result = CompareBox::greater(left_val.as_ref(), right_val.as_ref()); Ok(Box::new(result)) } - + BinaryOperator::LessEqual => { let result = CompareBox::less_equal(left_val.as_ref(), right_val.as_ref()); Ok(Box::new(result)) } - + BinaryOperator::GreaterEqual => { let result = CompareBox::greater_equal(left_val.as_ref(), right_val.as_ref()); Ok(Box::new(result)) } } } - + /// 単項演算を実行 - Unary operation processing - pub(super) fn execute_unary_op(&mut self, operator: &UnaryOperator, operand: &ASTNode) - -> Result, RuntimeError> { + pub(super) fn execute_unary_op( + &mut self, + operator: &UnaryOperator, + operand: &ASTNode, + ) -> Result, RuntimeError> { let operand_val = self.execute_expression(operand)?; - + match operator { UnaryOperator::Minus => { // 数値の符号反転 diff --git a/src/interpreter/functions.rs b/src/interpreter/functions.rs index db9a5b81..4719b3b7 100644 --- a/src/interpreter/functions.rs +++ b/src/interpreter/functions.rs @@ -1,6 +1,6 @@ /*! * Function Processing Module - * + * * Extracted from core.rs - function call and definition handling * Handles function declarations, calls, and function-related operations * Core philosophy: "Everything is Box" with structured function processing @@ -10,18 +10,26 @@ use super::*; impl NyashInterpreter { /// 関数呼び出しを実行 - 🌍 革命的実装:GlobalBoxのメソッド呼び出し - pub(super) fn execute_function_call(&mut self, name: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_function_call( + &mut self, + name: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // Fallback: built-in type ops as global functions: isType(value, "Type"), asType(value, "Type") if (name == "isType" || name == "asType") && arguments.len() == 2 { // Evaluate args let val = self.execute_expression(&arguments[0])?; let ty_box = self.execute_expression(&arguments[1])?; // Get type name string - let type_name = if let Some(s) = ty_box.as_any().downcast_ref::() { + let type_name = if let Some(s) = ty_box + .as_any() + .downcast_ref::() + { s.value.clone() } else { - return Err(RuntimeError::InvalidOperation { message: "Type name must be a string".to_string() }); + return Err(RuntimeError::InvalidOperation { + message: "Type name must be a string".to_string(), + }); }; if name == "isType" { @@ -41,14 +49,17 @@ impl NyashInterpreter { } } } - + // 🌍 GlobalBoxのメソッドとして実行 let global_box = self.shared.global_box.lock().unwrap(); - let method_ast = global_box.get_method(name) - .ok_or(RuntimeError::UndefinedFunction { name: name.to_string() })? + let method_ast = global_box + .get_method(name) + .ok_or(RuntimeError::UndefinedFunction { + name: name.to_string(), + })? .clone(); drop(global_box); - + // メソッド呼び出しとして実行(GlobalBoxインスタンス上で) if let ASTNode::FunctionDeclaration { params, body, .. } = method_ast { // 引数を評価 @@ -56,24 +67,28 @@ impl NyashInterpreter { for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // パラメータ数チェック if arg_values.len() != params.len() { return Err(RuntimeError::InvalidOperation { - message: format!("Function {} expects {} arguments, got {}", - name, params.len(), arg_values.len()), + message: format!( + "Function {} expects {} arguments, got {}", + name, + params.len(), + arg_values.len() + ), }); } - + // 🌍 local変数スタックを保存・クリア(関数呼び出し開始) let saved_locals = self.save_local_vars(); self.local_vars.clear(); - + // パラメータをlocal変数として設定 for (param, value) in params.iter().zip(arg_values.iter()) { self.declare_local_variable(param, value.clone_or_share()); } - + // 関数本体を実行(TaskGroupスコープをプッシュ) crate::runtime::global_hooks::push_task_scope(); let mut result: Box = Box::new(VoidBox::new()); @@ -104,22 +119,28 @@ impl NyashInterpreter { }) } } - + /// 関数宣言を登録 - 🌍 革命的実装:GlobalBoxのメソッドとして登録 - pub(super) fn register_function_declaration(&mut self, name: String, params: Vec, body: Vec) { + pub(super) fn register_function_declaration( + &mut self, + name: String, + params: Vec, + body: Vec, + ) { // 🌍 GlobalBoxのメソッドとして登録 let func_ast = ASTNode::FunctionDeclaration { name: name.clone(), params, body, - is_static: false, // 通常の関数は静的でない - is_override: false, // 🔥 通常の関数はオーバーライドでない + is_static: false, // 通常の関数は静的でない + is_override: false, // 🔥 通常の関数はオーバーライドでない span: crate::ast::Span::unknown(), // デフォルトspan }; - - self.register_global_function(name, func_ast).unwrap_or_else(|err| { - eprintln!("Warning: Failed to register global function: {}", err); - }); + + self.register_global_function(name, func_ast) + .unwrap_or_else(|err| { + eprintln!("Warning: Failed to register global function: {}", err); + }); } /// Helper: match a NyashBox value against a simple type name @@ -136,7 +157,10 @@ impl NyashInterpreter { } /// Helper: cast box to a target type name (minimal support) - fn cast_to_type(val: Box, type_name: &str) -> Result, RuntimeError> { + fn cast_to_type( + val: Box, + type_name: &str, + ) -> Result, RuntimeError> { match type_name { "Integer" | "Int" | "I64" => { // Float -> Integer (truncate), Integer -> Integer, else error @@ -151,7 +175,8 @@ impl NyashInterpreter { "Float" | "F64" => { if let Some(f) = val.as_any().downcast_ref::() { Ok(Box::new(crate::boxes::FloatBox::new(f.value))) - } else if let Some(i) = val.as_any().downcast_ref::() { + } else if let Some(i) = val.as_any().downcast_ref::() + { Ok(Box::new(crate::boxes::FloatBox::new(i.value as f64))) } else { Ok(val) diff --git a/src/interpreter/io.rs b/src/interpreter/io.rs index c53bb9a4..6a029b1a 100644 --- a/src/interpreter/io.rs +++ b/src/interpreter/io.rs @@ -1,6 +1,6 @@ /*! * I/O Processing Module - * + * * Extracted from core.rs - file operations and communication * Handles include system, arrow operators, and I/O-related operations * Core philosophy: "Everything is Box" with secure I/O processing @@ -28,7 +28,9 @@ impl NyashInterpreter { if let Some(roots) = include.get("roots").and_then(|v| v.as_table()) { if let Some(root_path_val) = roots.get(root).and_then(|v| v.as_str()) { let mut base = root_path_val.to_string(); - if !base.ends_with('/') && !base.ends_with('\\') { base.push('/'); } + if !base.ends_with('/') && !base.ends_with('\\') { + base.push('/'); + } let joined = format!("{}{}", base, rest); return joined; } @@ -64,47 +66,61 @@ impl NyashInterpreter { // 検出: A -> ... -> B -> A let mut chain: Vec = stack[pos..].to_vec(); chain.push(canonical_path.clone()); - let msg = format!("include cycle detected: {}", - chain.join(" -> ")); + let msg = format!("include cycle detected: {}", chain.join(" -> ")); return Err(RuntimeError::InvalidOperation { message: msg }); } stack.push(canonical_path.clone()); } // 重複読み込みチェック - if self.shared.included_files.lock().unwrap().contains(&canonical_path) { + if self + .shared + .included_files + .lock() + .unwrap() + .contains(&canonical_path) + { // スタックから外して早期終了 self.shared.include_stack.lock().unwrap().pop(); return Ok(()); // 既に読み込み済み } - + // ファイル読み込み - let content = std::fs::read_to_string(&canonical_path) - .map_err(|e| RuntimeError::InvalidOperation { + let content = std::fs::read_to_string(&canonical_path).map_err(|e| { + RuntimeError::InvalidOperation { message: format!("Failed to read file '{}': {}", filename, e), - })?; - + } + })?; + // パース - let ast = NyashParser::parse_from_string(&content) - .map_err(|e| RuntimeError::InvalidOperation { + let ast = NyashParser::parse_from_string(&content).map_err(|e| { + RuntimeError::InvalidOperation { message: format!("Parse error in '{}': {:?}", filename, e), - })?; - + } + })?; + // 重複防止リストに追加 - self.shared.included_files.lock().unwrap().insert(canonical_path.clone()); - + self.shared + .included_files + .lock() + .unwrap() + .insert(canonical_path.clone()); + // 現在の環境で実行 let exec_res = self.execute(ast); // スタックを外す self.shared.include_stack.lock().unwrap().pop(); // 実行結果を伝播 exec_res?; - + Ok(()) } - + /// include式を実行:ファイルを評価し、最初のstatic boxを返す - pub(super) fn execute_include_expr(&mut self, filename: &str) -> Result, RuntimeError> { + pub(super) fn execute_include_expr( + &mut self, + filename: &str, + ) -> Result, RuntimeError> { // パス解決(nyash.toml include.roots + 相対) let mut canonical_path = self.resolve_include_path(filename, None); // 拡張子補完・index対応 @@ -128,31 +144,45 @@ impl NyashInterpreter { } // ファイル読み込み(static box名検出用) - let content = std::fs::read_to_string(&canonical_path) - .map_err(|e| RuntimeError::InvalidOperation { + let content = std::fs::read_to_string(&canonical_path).map_err(|e| { + RuntimeError::InvalidOperation { message: format!("Failed to read file '{}': {}", filename, e), - })?; + } + })?; // パースして最初のstatic box名を特定 - let ast = NyashParser::parse_from_string(&content) - .map_err(|e| RuntimeError::InvalidOperation { + let ast = NyashParser::parse_from_string(&content).map_err(|e| { + RuntimeError::InvalidOperation { message: format!("Parse error in '{}': {:?}", filename, e), - })?; + } + })?; let mut static_names: Vec = Vec::new(); if let crate::ast::ASTNode::Program { statements, .. } = &ast { for st in statements { - if let crate::ast::ASTNode::BoxDeclaration { name, is_static, .. } = st { - if *is_static { static_names.push(name.clone()); } + if let crate::ast::ASTNode::BoxDeclaration { + name, is_static, .. + } = st + { + if *is_static { + static_names.push(name.clone()); + } } } } if static_names.is_empty() { - return Err(RuntimeError::InvalidOperation { message: format!("include target '{}' does not define a static box", filename) }); + return Err(RuntimeError::InvalidOperation { + message: format!("include target '{}' does not define a static box", filename), + }); } if static_names.len() > 1 { - return Err(RuntimeError::InvalidOperation { message: format!("include target '{}' defines multiple static boxes; exactly one is required", filename) }); + return Err(RuntimeError::InvalidOperation { + message: format!( + "include target '{}' defines multiple static boxes; exactly one is required", + filename + ), + }); } let box_name = static_names.remove(0); @@ -162,7 +192,11 @@ impl NyashInterpreter { set.contains(&canonical_path) }; if !already { - self.shared.included_files.lock().unwrap().insert(canonical_path.clone()); + self.shared + .included_files + .lock() + .unwrap() + .insert(canonical_path.clone()); let exec_res = self.execute(ast); // スタックを外す self.shared.include_stack.lock().unwrap().pop(); @@ -176,23 +210,42 @@ impl NyashInterpreter { self.ensure_static_box_initialized(&box_name)?; // statics名前空間からインスタンスを取り出す - let global_box = self.shared.global_box.lock() - .map_err(|_| RuntimeError::RuntimeFailure { message: "Failed to acquire global box lock".to_string() })?; - let statics = global_box.get_field("statics").ok_or(RuntimeError::TypeError { message: "statics namespace not found in GlobalBox".to_string() })?; - let statics_inst = statics.as_any().downcast_ref::() - .ok_or(RuntimeError::TypeError { message: "statics field is not an InstanceBox".to_string() })?; - let value = statics_inst.get_field(&box_name) - .ok_or(RuntimeError::InvalidOperation { message: format!("Static box '{}' not found after include", box_name) })?; + let global_box = + self.shared + .global_box + .lock() + .map_err(|_| RuntimeError::RuntimeFailure { + message: "Failed to acquire global box lock".to_string(), + })?; + let statics = global_box + .get_field("statics") + .ok_or(RuntimeError::TypeError { + message: "statics namespace not found in GlobalBox".to_string(), + })?; + let statics_inst = statics + .as_any() + .downcast_ref::() + .ok_or(RuntimeError::TypeError { + message: "statics field is not an InstanceBox".to_string(), + })?; + let value = statics_inst + .get_field(&box_name) + .ok_or(RuntimeError::InvalidOperation { + message: format!("Static box '{}' not found after include", box_name), + })?; Ok((*value).clone_or_share()) } - + /// Arrow演算子を実行: sender >> receiver - Channel communication - pub(super) fn execute_arrow(&mut self, sender: &ASTNode, receiver: &ASTNode) - -> Result, RuntimeError> { + pub(super) fn execute_arrow( + &mut self, + sender: &ASTNode, + receiver: &ASTNode, + ) -> Result, RuntimeError> { // 送信者を評価 let sender_value = self.execute_expression(sender)?; - + // 受信者を評価 let receiver_str = match receiver { ASTNode::Variable { name, .. } => name.clone(), @@ -206,26 +259,31 @@ impl NyashInterpreter { receiver_value.to_string_box().value } }; - + // 送信者の名前を取得 let sender_name = sender_value.to_string_box().value; - + // ChannelBoxを作成して返す - let channel_box = Box::new(ChannelBox::new(&sender_name, &receiver_str)) as Box; + let channel_box = + Box::new(ChannelBox::new(&sender_name, &receiver_str)) as Box; // 🌍 革命的実装:Environment tracking廃止 Ok(channel_box) } - + /// nowait文を実行 - 非同期実行(真の非同期実装) - Async execution - pub(super) fn execute_nowait(&mut self, variable: &str, expression: &ASTNode) -> Result, RuntimeError> { + pub(super) fn execute_nowait( + &mut self, + variable: &str, + expression: &ASTNode, + ) -> Result, RuntimeError> { use crate::boxes::FutureBox; - + // FutureBoxを作成 let future_box = FutureBox::new(); // 個別のクローンを用意(スケジュール経路とフォールバック経路で別々に使う) let future_for_sched = future_box.clone(); let future_for_thread = future_box.clone(); - + // 式をクローンしてスケジューラ(なければフォールバック)で実行 // それぞれの経路で独立に所有させるためクローンを分けておく let expr_for_sched = expression.clone(); @@ -242,28 +300,34 @@ impl NyashInterpreter { let mut async_interpreter = NyashInterpreter::with_shared(shared_for_sched); // 式を評価 match async_interpreter.execute_expression(&expr_for_sched) { - Ok(result) => { future_for_sched.set_result(result); } + Ok(result) => { + future_for_sched.set_result(result); + } Err(e) => { // エラーをErrorBoxとして設定 - let error_box = Box::new(ErrorBox::new("RuntimeError", &format!("{:?}", e))); + let error_box = + Box::new(ErrorBox::new("RuntimeError", &format!("{:?}", e))); future_for_sched.set_result(error_box); } } - }) + }), ); if !scheduled { std::thread::spawn(move || { let mut async_interpreter = NyashInterpreter::with_shared(shared_for_thread); match async_interpreter.execute_expression(&expr_for_thread) { - Ok(result) => { future_for_thread.set_result(result); } + Ok(result) => { + future_for_thread.set_result(result); + } Err(e) => { - let error_box = Box::new(ErrorBox::new("RuntimeError", &format!("{:?}", e))); + let error_box = + Box::new(ErrorBox::new("RuntimeError", &format!("{:?}", e))); future_for_thread.set_result(error_box); } } }); } - + // FutureBoxを現在のTaskGroupに登録(暗黙グループ best-effort) crate::runtime::global_hooks::register_future_to_current_group(&future_box); // FutureBoxを変数に保存 diff --git a/src/interpreter/math_methods.rs b/src/interpreter/math_methods.rs index 9c48d39f..2cfd3878 100644 --- a/src/interpreter/math_methods.rs +++ b/src/interpreter/math_methods.rs @@ -1,21 +1,21 @@ /*! * Math and Random Box Method Handlers Module - * + * * Extracted from box_methods.rs lines 148-632 * Contains mathematical computation and random number generation method implementations: - * + * * MathBox methods: * - abs, max, min, pow, sqrt - Basic mathematical operations * - sin, cos, tan - Trigonometric functions * - log, log10, exp - Logarithmic and exponential functions * - floor, ceil, round - Rounding operations * - getPi, getE - Mathematical constants - * + * * RandomBox methods: * - seed, random, randInt, randBool - Basic random generation * - choice, shuffle, randString - Advanced random operations * - probability - Probability-based operations - * + * * All methods include comprehensive argument validation and error handling. */ @@ -24,14 +24,18 @@ use super::*; impl NyashInterpreter { /// MathBoxのメソッド呼び出しを実行 /// 包括的な数学計算機能を提供 - pub(super) fn execute_math_method(&mut self, math_box: &MathBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_math_method( + &mut self, + math_box: &MathBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドを実行 match method { // 基本数学演算 @@ -75,7 +79,7 @@ impl NyashInterpreter { } Ok(math_box.sqrt(arg_values[0].clone_box())) } - + // 数学定数 "getPi" => { if !arg_values.is_empty() { @@ -93,7 +97,7 @@ impl NyashInterpreter { } Ok(math_box.getE()) } - + // 三角関数 "sin" => { if arg_values.len() != 1 { @@ -119,7 +123,7 @@ impl NyashInterpreter { } Ok(math_box.tan(arg_values[0].clone_box())) } - + // 対数・指数関数 "log" => { if arg_values.len() != 1 { @@ -145,7 +149,7 @@ impl NyashInterpreter { } Ok(math_box.exp(arg_values[0].clone_box())) } - + // 丸め関数 "floor" => { if arg_values.len() != 1 { @@ -171,25 +175,27 @@ impl NyashInterpreter { } Ok(math_box.round(arg_values[0].clone_box())) } - - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown MathBox method: {}", method), - }) - } + + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown MathBox method: {}", method), + }), } } /// RandomBoxのメソッド呼び出しを実行 /// 乱数生成と確率的操作を提供 - pub(super) fn execute_random_method(&mut self, random_box: &RandomBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_random_method( + &mut self, + random_box: &RandomBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドを実行 match method { // 乱数シード設定 @@ -201,7 +207,7 @@ impl NyashInterpreter { } Ok(random_box.seed(arg_values[0].clone_box())) } - + // 基本乱数生成 "random" => { if !arg_values.is_empty() { @@ -222,12 +228,15 @@ impl NyashInterpreter { "randBool" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("randBool() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "randBool() expects 0 arguments, got {}", + arg_values.len() + ), }); } Ok(random_box.randBool()) } - + // 配列・コレクション操作 "choice" => { if arg_values.len() != 1 { @@ -245,12 +254,15 @@ impl NyashInterpreter { } Ok(random_box.shuffle(arg_values[0].clone_box())) } - + // 文字列・確率操作 "randString" => { if arg_values.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("randString() expects 1 argument, got {}", arg_values.len()), + message: format!( + "randString() expects 1 argument, got {}", + arg_values.len() + ), }); } Ok(random_box.randString(arg_values[0].clone_box())) @@ -258,17 +270,18 @@ impl NyashInterpreter { "probability" => { if arg_values.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("probability() expects 1 argument, got {}", arg_values.len()), + message: format!( + "probability() expects 1 argument, got {}", + arg_values.len() + ), }); } Ok(random_box.probability(arg_values[0].clone_box())) } - - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown RandomBox method: {}", method), - }) - } + + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown RandomBox method: {}", method), + }), } } -} \ No newline at end of file +} diff --git a/src/interpreter/methods/basic_methods.rs b/src/interpreter/methods/basic_methods.rs index 65cf2169..4c57dc09 100644 --- a/src/interpreter/methods/basic_methods.rs +++ b/src/interpreter/methods/basic_methods.rs @@ -1,22 +1,26 @@ /*! * Basic Box Methods Module - * + * * Extracted from box_methods.rs * Contains method implementations for: * - StringBox (execute_string_method) - * - IntegerBox (execute_integer_method) + * - IntegerBox (execute_integer_method) * - BoolBox (execute_bool_method) * - FloatBox (execute_float_method) */ use super::super::*; -use crate::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox}; +use crate::box_trait::{BoolBox, IntegerBox, StringBox, VoidBox}; use crate::boxes::FloatBox; impl NyashInterpreter { /// StringBoxのメソッド呼び出しを実行 - pub(in crate::interpreter) fn execute_string_method(&mut self, string_box: &StringBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(in crate::interpreter) fn execute_string_method( + &mut self, + string_box: &StringBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "split" => { if arguments.len() != 1 { @@ -93,7 +97,7 @@ impl NyashInterpreter { let new_value = self.execute_expression(&arguments[1])?; if let (Some(old_str), Some(new_str)) = ( old_value.as_any().downcast_ref::(), - new_value.as_any().downcast_ref::() + new_value.as_any().downcast_ref::(), ) { Ok(string_box.replace(&old_str.value, &new_str.value)) } else { @@ -129,7 +133,10 @@ impl NyashInterpreter { "toInteger" => { if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("toInteger() expects 0 arguments, got {}", arguments.len()), + message: format!( + "toInteger() expects 0 arguments, got {}", + arguments.len() + ), }); } Ok(string_box.to_integer()) @@ -137,12 +144,15 @@ impl NyashInterpreter { "substring" => { if arguments.len() != 2 { return Err(RuntimeError::InvalidOperation { - message: format!("substring() expects 2 arguments, got {}", arguments.len()), + message: format!( + "substring() expects 2 arguments, got {}", + arguments.len() + ), }); } let start = self.execute_expression(&arguments[0])?; let end = self.execute_expression(&arguments[1])?; - + // Convert arguments to integers let start_int = if let Some(int_box) = start.as_any().downcast_ref::() { int_box.value as usize @@ -151,7 +161,7 @@ impl NyashInterpreter { message: "substring() expects integer arguments".to_string(), }); }; - + let end_int = if let Some(int_box) = end.as_any().downcast_ref::() { int_box.value as usize } else { @@ -159,20 +169,22 @@ impl NyashInterpreter { message: "substring() expects integer arguments".to_string(), }); }; - + Ok(string_box.substring(start_int, end_int)) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown method '{}' for StringBox", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for StringBox", method), + }), } } /// IntegerBoxのメソッド呼び出しを実行 - pub(in crate::interpreter) fn execute_integer_method(&mut self, integer_box: &IntegerBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(in crate::interpreter) fn execute_integer_method( + &mut self, + integer_box: &IntegerBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "toString" => { if !arguments.is_empty() { @@ -198,7 +210,9 @@ impl NyashInterpreter { } let other_value = self.execute_expression(&arguments[0])?; if let Some(other_int) = other_value.as_any().downcast_ref::() { - Ok(Box::new(IntegerBox::new(integer_box.value.max(other_int.value)))) + Ok(Box::new(IntegerBox::new( + integer_box.value.max(other_int.value), + ))) } else { Err(RuntimeError::TypeError { message: "max() requires integer argument".to_string(), @@ -213,7 +227,9 @@ impl NyashInterpreter { } let other_value = self.execute_expression(&arguments[0])?; if let Some(other_int) = other_value.as_any().downcast_ref::() { - Ok(Box::new(IntegerBox::new(integer_box.value.min(other_int.value)))) + Ok(Box::new(IntegerBox::new( + integer_box.value.min(other_int.value), + ))) } else { Err(RuntimeError::TypeError { message: "min() requires integer argument".to_string(), @@ -249,17 +265,19 @@ impl NyashInterpreter { }) } } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown method '{}' for IntegerBox", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for IntegerBox", method), + }), } } /// BoolBoxのメソッド呼び出しを実行 - pub(in crate::interpreter) fn execute_bool_method(&mut self, bool_box: &BoolBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(in crate::interpreter) fn execute_bool_method( + &mut self, + bool_box: &BoolBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "toString" => { if !arguments.is_empty() { @@ -316,17 +334,19 @@ impl NyashInterpreter { let other_value = self.execute_expression(&arguments[0])?; Ok(Box::new(bool_box.equals(&*other_value))) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown method '{}' for BoolBox", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for BoolBox", method), + }), } } /// FloatBoxのメソッド呼び出しを実行 - pub(in crate::interpreter) fn execute_float_method(&mut self, float_box: &FloatBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(in crate::interpreter) fn execute_float_method( + &mut self, + float_box: &FloatBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "toString" => { if !arguments.is_empty() { @@ -371,7 +391,10 @@ impl NyashInterpreter { "toInteger" => { if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("toInteger() expects 0 arguments, got {}", arguments.len()), + message: format!( + "toInteger() expects 0 arguments, got {}", + arguments.len() + ), }); } Ok(Box::new(IntegerBox::new(float_box.value as i64))) @@ -384,9 +407,13 @@ impl NyashInterpreter { } let other_value = self.execute_expression(&arguments[0])?; if let Some(other_float) = other_value.as_any().downcast_ref::() { - Ok(Box::new(FloatBox::new(float_box.value.max(other_float.value)))) + Ok(Box::new(FloatBox::new( + float_box.value.max(other_float.value), + ))) } else if let Some(other_int) = other_value.as_any().downcast_ref::() { - Ok(Box::new(FloatBox::new(float_box.value.max(other_int.value as f64)))) + Ok(Box::new(FloatBox::new( + float_box.value.max(other_int.value as f64), + ))) } else { Err(RuntimeError::TypeError { message: "max() requires numeric argument".to_string(), @@ -401,9 +428,13 @@ impl NyashInterpreter { } let other_value = self.execute_expression(&arguments[0])?; if let Some(other_float) = other_value.as_any().downcast_ref::() { - Ok(Box::new(FloatBox::new(float_box.value.min(other_float.value)))) + Ok(Box::new(FloatBox::new( + float_box.value.min(other_float.value), + ))) } else if let Some(other_int) = other_value.as_any().downcast_ref::() { - Ok(Box::new(FloatBox::new(float_box.value.min(other_int.value as f64)))) + Ok(Box::new(FloatBox::new( + float_box.value.min(other_int.value as f64), + ))) } else { Err(RuntimeError::TypeError { message: "min() requires numeric argument".to_string(), @@ -418,9 +449,15 @@ impl NyashInterpreter { } let exponent_value = self.execute_expression(&arguments[0])?; if let Some(exponent_float) = exponent_value.as_any().downcast_ref::() { - Ok(Box::new(FloatBox::new(float_box.value.powf(exponent_float.value)))) - } else if let Some(exponent_int) = exponent_value.as_any().downcast_ref::() { - Ok(Box::new(FloatBox::new(float_box.value.powf(exponent_int.value as f64)))) + Ok(Box::new(FloatBox::new( + float_box.value.powf(exponent_float.value), + ))) + } else if let Some(exponent_int) = + exponent_value.as_any().downcast_ref::() + { + Ok(Box::new(FloatBox::new( + float_box.value.powf(exponent_int.value as f64), + ))) } else { Err(RuntimeError::TypeError { message: "pow() requires numeric exponent".to_string(), @@ -512,7 +549,10 @@ impl NyashInterpreter { "isInfinite" => { if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("isInfinite() expects 0 arguments, got {}", arguments.len()), + message: format!( + "isInfinite() expects 0 arguments, got {}", + arguments.len() + ), }); } Ok(Box::new(BoolBox::new(float_box.value.is_infinite()))) @@ -534,11 +574,9 @@ impl NyashInterpreter { let other_value = self.execute_expression(&arguments[0])?; Ok(Box::new(float_box.equals(&*other_value))) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown method '{}' for FloatBox", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for FloatBox", method), + }), } } -} \ No newline at end of file +} diff --git a/src/interpreter/methods/collection_methods.rs b/src/interpreter/methods/collection_methods.rs index 627aa92f..00352cb3 100644 --- a/src/interpreter/methods/collection_methods.rs +++ b/src/interpreter/methods/collection_methods.rs @@ -1,6 +1,6 @@ /*! * Collection Methods Module - * + * * Extracted from box_methods.rs * Contains method implementations for collection types: * - ArrayBox (execute_array_method) @@ -8,13 +8,17 @@ */ use super::super::*; -use crate::box_trait::{IntegerBox, NyashBox, BoolBox}; +use crate::box_trait::{BoolBox, IntegerBox, NyashBox}; use crate::boxes::{ArrayBox, MapBox}; impl NyashInterpreter { /// ArrayBoxのメソッド呼び出しを実行 - pub(in crate::interpreter) fn execute_array_method(&mut self, array_box: &ArrayBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(in crate::interpreter) fn execute_array_method( + &mut self, + array_box: &ArrayBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "of" => { // Build a new ArrayBox from provided arguments @@ -154,25 +158,29 @@ impl NyashInterpreter { "slice" => { if arguments.len() != 2 { return Err(RuntimeError::InvalidOperation { - message: format!("slice() expects 2 arguments (start, end), got {}", arguments.len()), + message: format!( + "slice() expects 2 arguments (start, end), got {}", + arguments.len() + ), }); } let start_value = self.execute_expression(&arguments[0])?; let end_value = self.execute_expression(&arguments[1])?; Ok(array_box.slice(start_value, end_value)) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown method '{}' for ArrayBox", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for ArrayBox", method), + }), } } /// MapBoxのメソッド呼び出しを実行 - pub(in crate::interpreter) fn execute_map_method(&mut self, map_box: &MapBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { - + pub(in crate::interpreter) fn execute_map_method( + &mut self, + map_box: &MapBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // メソッドを実行(必要時評価方式) match method { "set" => { @@ -260,7 +268,10 @@ impl NyashInterpreter { "containsKey" => { if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("containsKey() expects 1 argument, got {}", arguments.len()), + message: format!( + "containsKey() expects 1 argument, got {}", + arguments.len() + ), }); } let key_value = self.execute_expression(&arguments[0])?; @@ -269,7 +280,10 @@ impl NyashInterpreter { "containsValue" => { if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("containsValue() expects 1 argument, got {}", arguments.len()), + message: format!( + "containsValue() expects 1 argument, got {}", + arguments.len() + ), }); } let _value = self.execute_expression(&arguments[0])?; @@ -303,11 +317,9 @@ impl NyashInterpreter { } Ok(Box::new(map_box.to_string_box())) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown MapBox method: {}", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown MapBox method: {}", method), + }), } } } diff --git a/src/interpreter/methods/data_methods.rs b/src/interpreter/methods/data_methods.rs index c95dbf49..066db3f0 100644 --- a/src/interpreter/methods/data_methods.rs +++ b/src/interpreter/methods/data_methods.rs @@ -1,6 +1,6 @@ /*! * Data Processing Box Methods Module - * + * * Contains method implementations for data processing Box types: * - BufferBox (execute_buffer_method) - Binary data operations * - JSONBox (execute_json_method) - JSON parsing and manipulation @@ -13,8 +13,12 @@ use crate::boxes::{buffer::BufferBox, JSONBox, RegexBox}; impl NyashInterpreter { /// BufferBoxのメソッド呼び出しを実行 - pub(in crate::interpreter) fn execute_buffer_method(&mut self, buffer_box: &BufferBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(in crate::interpreter) fn execute_buffer_method( + &mut self, + buffer_box: &BufferBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "write" => { if arguments.len() != 1 { @@ -81,7 +85,10 @@ impl NyashInterpreter { "is_shared_with" => { if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("is_shared_with() expects 1 argument, got {}", arguments.len()), + message: format!( + "is_shared_with() expects 1 argument, got {}", + arguments.len() + ), }); } let other = self.execute_expression(&arguments[0])?; @@ -90,7 +97,10 @@ impl NyashInterpreter { "share_reference" => { if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("share_reference() expects 1 argument, got {}", arguments.len()), + message: format!( + "share_reference() expects 1 argument, got {}", + arguments.len() + ), }); } let data = self.execute_expression(&arguments[0])?; @@ -99,20 +109,27 @@ impl NyashInterpreter { "memory_footprint" => { if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("memory_footprint() expects 0 arguments, got {}", arguments.len()), + message: format!( + "memory_footprint() expects 0 arguments, got {}", + arguments.len() + ), }); } Ok(buffer_box.memory_footprint()) } _ => Err(RuntimeError::InvalidOperation { message: format!("Unknown method '{}' for BufferBox", method), - }) + }), } } /// JSONBoxのメソッド呼び出しを実行 - pub(in crate::interpreter) fn execute_json_method(&mut self, json_box: &JSONBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(in crate::interpreter) fn execute_json_method( + &mut self, + json_box: &JSONBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "parse" => { if arguments.len() != 1 { @@ -126,7 +143,10 @@ impl NyashInterpreter { "stringify" => { if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("stringify() expects 0 arguments, got {}", arguments.len()), + message: format!( + "stringify() expects 0 arguments, got {}", + arguments.len() + ), }); } Ok(json_box.stringify()) @@ -169,13 +189,17 @@ impl NyashInterpreter { } _ => Err(RuntimeError::InvalidOperation { message: format!("Unknown method '{}' for JSONBox", method), - }) + }), } } /// RegexBoxのメソッド呼び出しを実行 - pub(in crate::interpreter) fn execute_regex_method(&mut self, regex_box: &RegexBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(in crate::interpreter) fn execute_regex_method( + &mut self, + regex_box: &RegexBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "test" => { if arguments.len() != 1 { @@ -225,7 +249,7 @@ impl NyashInterpreter { } _ => Err(RuntimeError::InvalidOperation { message: format!("Unknown method '{}' for RegexBox", method), - }) + }), } } -} \ No newline at end of file +} diff --git a/src/interpreter/methods/http_methods.rs b/src/interpreter/methods/http_methods.rs index 278d190f..8b7e3e79 100644 --- a/src/interpreter/methods/http_methods.rs +++ b/src/interpreter/methods/http_methods.rs @@ -1,19 +1,19 @@ /*! 🌐 HTTP Method Implementations - * + * * HTTP関連Boxのメソッド実行を実装 * SocketBox, HTTPServerBox, HTTPRequestBox, HTTPResponseBox */ use super::super::*; -use crate::boxes::{SocketBox, HTTPServerBox, HTTPRequestBox, HTTPResponseBox}; +use crate::boxes::{HTTPRequestBox, HTTPResponseBox, HTTPServerBox, SocketBox}; impl NyashInterpreter { /// SocketBox methods pub(in crate::interpreter) fn execute_socket_method( - &mut self, - socket_box: &SocketBox, - method: &str, - arguments: &[ASTNode] + &mut self, + socket_box: &SocketBox, + method: &str, + arguments: &[ASTNode], ) -> Result, RuntimeError> { match method { "bind" => { @@ -22,7 +22,7 @@ impl NyashInterpreter { message: format!("bind() expects 2 arguments, got {}", arguments.len()), }); } - + let address = self.execute_expression(&arguments[0])?; let port = self.execute_expression(&arguments[1])?; let result = socket_box.bind(address, port); @@ -34,7 +34,7 @@ impl NyashInterpreter { message: format!("listen() expects 1 argument, got {}", arguments.len()), }); } - + let backlog = self.execute_expression(&arguments[0])?; Ok(socket_box.listen(backlog)) } @@ -44,13 +44,16 @@ impl NyashInterpreter { message: format!("accept() expects 0 arguments, got {}", arguments.len()), }); } - + Ok(socket_box.accept()) } "acceptTimeout" | "accept_timeout" => { if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("acceptTimeout(ms) expects 1 argument, got {}", arguments.len()), + message: format!( + "acceptTimeout(ms) expects 1 argument, got {}", + arguments.len() + ), }); } let ms = self.execute_expression(&arguments[0])?; @@ -62,7 +65,7 @@ impl NyashInterpreter { message: format!("connect() expects 2 arguments, got {}", arguments.len()), }); } - + let address = self.execute_expression(&arguments[0])?; let port = self.execute_expression(&arguments[1])?; Ok(socket_box.connect(address, port)) @@ -73,13 +76,16 @@ impl NyashInterpreter { message: format!("read() expects 0 arguments, got {}", arguments.len()), }); } - + Ok(socket_box.read()) } "recvTimeout" | "recv_timeout" => { if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("recvTimeout(ms) expects 1 argument, got {}", arguments.len()), + message: format!( + "recvTimeout(ms) expects 1 argument, got {}", + arguments.len() + ), }); } let ms = self.execute_expression(&arguments[0])?; @@ -88,10 +94,13 @@ impl NyashInterpreter { "readHttpRequest" => { if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("readHttpRequest() expects 0 arguments, got {}", arguments.len()), + message: format!( + "readHttpRequest() expects 0 arguments, got {}", + arguments.len() + ), }); } - + Ok(socket_box.read_http_request()) } "write" => { @@ -100,7 +109,7 @@ impl NyashInterpreter { message: format!("write() expects 1 argument, got {}", arguments.len()), }); } - + let data = self.execute_expression(&arguments[0])?; Ok(socket_box.write(data)) } @@ -110,16 +119,19 @@ impl NyashInterpreter { message: format!("close() expects 0 arguments, got {}", arguments.len()), }); } - + Ok(socket_box.close()) } "isConnected" => { if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("isConnected() expects 0 arguments, got {}", arguments.len()), + message: format!( + "isConnected() expects 0 arguments, got {}", + arguments.len() + ), }); } - + Ok(socket_box.is_connected()) } "isServer" => { @@ -128,7 +140,7 @@ impl NyashInterpreter { message: format!("isServer() expects 0 arguments, got {}", arguments.len()), }); } - + Ok(socket_box.is_server()) } "toString" => { @@ -137,7 +149,7 @@ impl NyashInterpreter { message: format!("toString() expects 0 arguments, got {}", arguments.len()), }); } - + Ok(Box::new(socket_box.to_string_box())) } _ => Err(RuntimeError::UndefinedVariable { @@ -148,10 +160,10 @@ impl NyashInterpreter { /// HTTPServerBox methods pub(in crate::interpreter) fn execute_http_server_method( - &mut self, - server_box: &HTTPServerBox, - method: &str, - arguments: &[ASTNode] + &mut self, + server_box: &HTTPServerBox, + method: &str, + arguments: &[ASTNode], ) -> Result, RuntimeError> { match method { "bind" => { @@ -160,7 +172,7 @@ impl NyashInterpreter { message: format!("bind() expects 2 arguments, got {}", arguments.len()), }); } - + let address = self.execute_expression(&arguments[0])?; let port = self.execute_expression(&arguments[1])?; Ok(server_box.bind(address, port)) @@ -171,7 +183,7 @@ impl NyashInterpreter { message: format!("listen() expects 1 argument, got {}", arguments.len()), }); } - + let backlog = self.execute_expression(&arguments[0])?; Ok(server_box.listen(backlog)) } @@ -181,7 +193,7 @@ impl NyashInterpreter { message: format!("start() expects 0 arguments, got {}", arguments.len()), }); } - + Ok(server_box.start()) } "stop" => { @@ -190,7 +202,7 @@ impl NyashInterpreter { message: format!("stop() expects 0 arguments, got {}", arguments.len()), }); } - + Ok(server_box.stop()) } "get" => { @@ -199,7 +211,7 @@ impl NyashInterpreter { message: format!("get() expects 2 arguments, got {}", arguments.len()), }); } - + let path = self.execute_expression(&arguments[0])?; let handler = self.execute_expression(&arguments[1])?; Ok(server_box.get(path, handler)) @@ -210,7 +222,7 @@ impl NyashInterpreter { message: format!("toString() expects 0 arguments, got {}", arguments.len()), }); } - + Ok(Box::new(server_box.to_string_box())) } _ => Err(RuntimeError::UndefinedVariable { @@ -221,19 +233,22 @@ impl NyashInterpreter { /// HTTPRequestBox methods pub(in crate::interpreter) fn execute_http_request_method( - &mut self, - request_box: &HTTPRequestBox, - method: &str, - arguments: &[ASTNode] + &mut self, + request_box: &HTTPRequestBox, + method: &str, + arguments: &[ASTNode], ) -> Result, RuntimeError> { match method { "getMethod" => { if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("getMethod() expects 0 arguments, got {}", arguments.len()), + message: format!( + "getMethod() expects 0 arguments, got {}", + arguments.len() + ), }); } - + Ok(request_box.get_method()) } "getPath" => { @@ -242,7 +257,7 @@ impl NyashInterpreter { message: format!("getPath() expects 0 arguments, got {}", arguments.len()), }); } - + Ok(request_box.get_path()) } "toString" => { @@ -251,7 +266,7 @@ impl NyashInterpreter { message: format!("toString() expects 0 arguments, got {}", arguments.len()), }); } - + Ok(Box::new(request_box.to_string_box())) } _ => Err(RuntimeError::UndefinedVariable { @@ -262,19 +277,22 @@ impl NyashInterpreter { /// HTTPResponseBox methods pub(in crate::interpreter) fn execute_http_response_method( - &mut self, - response_box: &HTTPResponseBox, - method: &str, - arguments: &[ASTNode] + &mut self, + response_box: &HTTPResponseBox, + method: &str, + arguments: &[ASTNode], ) -> Result, RuntimeError> { match method { "setStatus" => { if arguments.len() != 2 { return Err(RuntimeError::InvalidOperation { - message: format!("setStatus() expects 2 arguments, got {}", arguments.len()), + message: format!( + "setStatus() expects 2 arguments, got {}", + arguments.len() + ), }); } - + let code = self.execute_expression(&arguments[0])?; let message = self.execute_expression(&arguments[1])?; Ok(response_box.set_status(code, message)) @@ -282,10 +300,13 @@ impl NyashInterpreter { "toHttpString" => { if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("toHttpString() expects 0 arguments, got {}", arguments.len()), + message: format!( + "toHttpString() expects 0 arguments, got {}", + arguments.len() + ), }); } - + Ok(response_box.to_http_string()) } "toString" => { @@ -294,7 +315,7 @@ impl NyashInterpreter { message: format!("toString() expects 0 arguments, got {}", arguments.len()), }); } - + Ok(Box::new(response_box.to_string_box())) } _ => Err(RuntimeError::UndefinedVariable { diff --git a/src/interpreter/methods/io_methods.rs b/src/interpreter/methods/io_methods.rs index aa332775..633ad384 100644 --- a/src/interpreter/methods/io_methods.rs +++ b/src/interpreter/methods/io_methods.rs @@ -1,6 +1,6 @@ /*! * I/O Operations Box Methods Module - * + * * Extracted from box_methods.rs * Contains method implementations for I/O and error handling operations: * - FileBox (execute_file_method) - File I/O operations @@ -8,17 +8,21 @@ */ use super::super::*; -use crate::boxes::ResultBox; -use crate::box_trait::{StringBox, NyashBox}; -use crate::boxes::FileBox; +use crate::box_trait::{NyashBox, StringBox}; use crate::boxes::ref_cell_box::RefCellBox; +use crate::boxes::FileBox; +use crate::boxes::ResultBox; // use crate::bid::plugin_box::PluginFileBox; // legacy - FileBox専用 impl NyashInterpreter { /// FileBoxのメソッド呼び出しを実行 /// Handles file I/O operations including read, write, exists, delete, and copy - pub(in crate::interpreter) fn execute_file_method(&mut self, file_box: &FileBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(in crate::interpreter) fn execute_file_method( + &mut self, + file_box: &FileBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "read" => { if !arguments.is_empty() { @@ -70,14 +74,18 @@ impl NyashInterpreter { } _ => Err(RuntimeError::InvalidOperation { message: format!("Unknown method '{}' for FileBox", method), - }) + }), } } /// ResultBoxのメソッド呼び出しを実行 /// Handles result/error checking operations for error handling patterns - pub(in crate::interpreter) fn execute_result_method(&mut self, result_box: &ResultBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(in crate::interpreter) fn execute_result_method( + &mut self, + result_box: &ResultBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "isOk" | "is_ok" => { if !arguments.is_empty() { @@ -105,38 +113,50 @@ impl NyashInterpreter { } _ => Err(RuntimeError::InvalidOperation { message: format!("Unknown method '{}' for ResultBox", method), - }) + }), } } /// RefCellBox のメソッド: get()/set(value) - pub(in crate::interpreter) fn execute_refcell_method(&mut self, cell: &RefCellBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(in crate::interpreter) fn execute_refcell_method( + &mut self, + cell: &RefCellBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "get" => { if !arguments.is_empty() { - return Err(RuntimeError::InvalidOperation { message: format!("get() expects 0 arguments, got {}", arguments.len()) }); + return Err(RuntimeError::InvalidOperation { + message: format!("get() expects 0 arguments, got {}", arguments.len()), + }); } Ok(cell.borrow()) } "set" => { - if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { message: format!("set() expects 1 argument, got {}", arguments.len()) }); } + if arguments.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("set() expects 1 argument, got {}", arguments.len()), + }); + } let v = self.execute_expression(&arguments[0])?; cell.replace(v); Ok(Box::new(crate::box_trait::VoidBox::new())) } - _ => Err(RuntimeError::InvalidOperation { message: format!("Unknown method '{}' for RefCellBox", method) }) + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for RefCellBox", method), + }), } } /* legacy - PluginFileBox専用 /// 汎用プラグインメソッド呼び出し実行 (BID-FFI system) /// Handles generic plugin method calls via dynamic method discovery - pub(in crate::interpreter) fn execute_plugin_method_generic(&mut self, plugin_box: &PluginFileBox, method: &str, arguments: &[ASTNode]) + pub(in crate::interpreter) fn execute_plugin_method_generic(&mut self, plugin_box: &PluginFileBox, method: &str, arguments: &[ASTNode]) -> Result, RuntimeError> { - + eprintln!("🔍 execute_plugin_method_generic: method='{}', args_count={}", method, arguments.len()); - + // まず利用可能なメソッドを確認 match plugin_box.get_available_methods() { Ok(methods) => { @@ -147,11 +167,11 @@ impl NyashInterpreter { } Err(e) => eprintln!("⚠️ Failed to get plugin methods: {:?}", e), } - + // 引数をTLVエンコード(メソッド名も渡す) let encoded_args = self.encode_arguments_to_tlv(arguments, method)?; eprintln!("🔍 Encoded args length: {} bytes", encoded_args.len()); - + // プラグインのメソッドを動的呼び出し match plugin_box.call_method(method, &encoded_args) { Ok(response_bytes) => { @@ -172,25 +192,25 @@ impl NyashInterpreter { fn encode_arguments_to_tlv(&mut self, arguments: &[ASTNode], method_name: &str) -> Result, RuntimeError> { use crate::bid::tlv::TlvEncoder; use crate::bid::registry; - + let mut encoder = TlvEncoder::new(); - + // 型情報を取得(FileBoxのみ対応、後で拡張) let type_info = registry::global() .and_then(|reg| reg.get_method_type_info("FileBox", method_name)); - + // 型情報がある場合は、それに従って変換 if let Some(type_info) = type_info { eprintln!("✨ Using type info for method '{}'", method_name); - + // 引数の数をチェック if arguments.len() != type_info.args.len() { return Err(RuntimeError::InvalidOperation { - message: format!("{} expects {} arguments, got {}", + message: format!("{} expects {} arguments, got {}", method_name, type_info.args.len(), arguments.len()), }); } - + // 各引数を型情報に従ってエンコード for (i, (arg, mapping)) in arguments.iter().zip(&type_info.args).enumerate() { eprintln!(" 🔄 Arg[{}]: {} -> {} conversion", i, mapping.from, mapping.to); @@ -205,15 +225,15 @@ impl NyashInterpreter { self.encode_value_default(&mut encoder, value)?; } } - + Ok(encoder.finish()) } - + /// 型マッピングに基づいて値をエンコード(美しい!) fn encode_value_with_mapping( - &self, - encoder: &mut crate::bid::tlv::TlvEncoder, - value: Box, + &self, + encoder: &mut crate::bid::tlv::TlvEncoder, + value: Box, mapping: &crate::bid::ArgTypeMapping ) -> Result<(), RuntimeError> { // determine_bid_tag()を使って適切なタグを決定 @@ -221,7 +241,7 @@ impl NyashInterpreter { .ok_or_else(|| RuntimeError::InvalidOperation { message: format!("Unsupported type mapping: {} -> {}", mapping.from, mapping.to), })?; - + // タグに応じてエンコード match tag { crate::bid::BidTag::String => { @@ -267,7 +287,7 @@ impl NyashInterpreter { }) } } - + /// デフォルトエンコード(型情報がない場合のフォールバック) fn encode_value_default( &self, @@ -297,26 +317,26 @@ impl NyashInterpreter { }) } } - + /// TLVレスポンスをNyashBoxに変換 fn decode_tlv_to_nyash_box(&self, response_bytes: &[u8], method_name: &str) -> Result, RuntimeError> { use crate::bid::tlv::TlvDecoder; use crate::bid::types::BidTag; - + if response_bytes.is_empty() { return Ok(Box::new(StringBox::new("".to_string()))); } - + let mut decoder = TlvDecoder::new(response_bytes) .map_err(|e| RuntimeError::InvalidOperation { message: format!("TLV decoder creation failed: {:?}", e), })?; - + if let Some((tag, payload)) = decoder.decode_next() .map_err(|e| RuntimeError::InvalidOperation { message: format!("TLV decoding failed: {:?}", e), })? { - + match tag { BidTag::String => { let text = String::from_utf8_lossy(payload).to_string(); @@ -359,7 +379,7 @@ impl NyashInterpreter { /// Handles plugin-backed file I/O operations via FFI interface /// 🚨 DEPRECATED: This method has hardcoded method names and violates BID-FFI principles /// Use execute_plugin_method_generic instead for true dynamic method calling - pub(in crate::interpreter) fn execute_plugin_file_method(&mut self, plugin_file_box: &PluginFileBox, method: &str, arguments: &[ASTNode]) + pub(in crate::interpreter) fn execute_plugin_file_method(&mut self, plugin_file_box: &PluginFileBox, method: &str, arguments: &[ASTNode]) -> Result, RuntimeError> { // 🎯 新しい汎用システムにリダイレクト self.execute_plugin_method_generic(plugin_file_box, method, arguments) diff --git a/src/interpreter/methods/mod.rs b/src/interpreter/methods/mod.rs index 71bb7823..01aeec88 100644 --- a/src/interpreter/methods/mod.rs +++ b/src/interpreter/methods/mod.rs @@ -1,11 +1,11 @@ /*! * Box Methods Module Organization - * + * * 旧box_methods.rsを機能別に分割したモジュール群 * 保守性と可読性の向上を目的とした再構成 - * + * * Current implementation: - * - basic_methods: StringBox, IntegerBox, BoolBox, FloatBox + * - basic_methods: StringBox, IntegerBox, BoolBox, FloatBox * - collection_methods: ArrayBox, MapBox * - io_methods: FileBox, ResultBox ✅ IMPLEMENTED * Future modules (planned): @@ -16,13 +16,13 @@ * - special_methods: MethodBox, SoundBox */ -pub mod basic_methods; // StringBox, IntegerBox, BoolBox, FloatBox +pub mod basic_methods; // StringBox, IntegerBox, BoolBox, FloatBox pub mod collection_methods; // ArrayBox, MapBox -pub mod io_methods; // FileBox, ResultBox -pub mod data_methods; // BufferBox, JSONBox, RegexBox -pub mod network_methods; // HttpClientBox, StreamBox -pub mod p2p_methods; // IntentBox, P2PBox -pub mod http_methods; // SocketBox, HTTPServerBox, HTTPRequestBox, HTTPResponseBox -pub mod system_methods; // GcConfigBox, DebugConfigBox +pub mod data_methods; // BufferBox, JSONBox, RegexBox +pub mod http_methods; // SocketBox, HTTPServerBox, HTTPRequestBox, HTTPResponseBox +pub mod io_methods; // FileBox, ResultBox +pub mod network_methods; // HttpClientBox, StreamBox +pub mod p2p_methods; // IntentBox, P2PBox +pub mod system_methods; // GcConfigBox, DebugConfigBox // Re-export methods for easy access diff --git a/src/interpreter/methods/network_methods.rs b/src/interpreter/methods/network_methods.rs index 9eb9e283..87ab2088 100644 --- a/src/interpreter/methods/network_methods.rs +++ b/src/interpreter/methods/network_methods.rs @@ -1,6 +1,6 @@ /*! * Network and Communication Box Methods Module - * + * * Contains method implementations for network-related Box types: * - HttpClientBox (execute_http_method) - HTTP client operations * - StreamBox (execute_stream_method) - Stream processing operations @@ -12,8 +12,12 @@ use crate::boxes::{HttpClientBox, StreamBox}; impl NyashInterpreter { /// HttpClientBoxのメソッド呼び出しを実行 - pub(in crate::interpreter) fn execute_http_method(&mut self, http_box: &HttpClientBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(in crate::interpreter) fn execute_http_method( + &mut self, + http_box: &HttpClientBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "get" => { if arguments.len() != 1 { @@ -66,13 +70,17 @@ impl NyashInterpreter { } _ => Err(RuntimeError::InvalidOperation { message: format!("Unknown method '{}' for HttpClientBox", method), - }) + }), } } /// StreamBoxのメソッド呼び出しを実行 - pub(in crate::interpreter) fn execute_stream_method(&mut self, stream_box: &StreamBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(in crate::interpreter) fn execute_stream_method( + &mut self, + stream_box: &StreamBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "write" => { if arguments.len() != 1 { @@ -118,7 +126,7 @@ impl NyashInterpreter { } _ => Err(RuntimeError::InvalidOperation { message: format!("Unknown method '{}' for StreamBox", method), - }) + }), } } -} \ No newline at end of file +} diff --git a/src/interpreter/methods/p2p_methods.rs b/src/interpreter/methods/p2p_methods.rs index baa4392f..8601fbc3 100644 --- a/src/interpreter/methods/p2p_methods.rs +++ b/src/interpreter/methods/p2p_methods.rs @@ -3,11 +3,11 @@ * Arcパターン対応版 */ -use crate::interpreter::NyashInterpreter; -use crate::interpreter::RuntimeError; use crate::ast::ASTNode; use crate::box_trait::{NyashBox, StringBox}; use crate::boxes::{IntentBox, P2PBox}; +use crate::interpreter::NyashInterpreter; +use crate::interpreter::RuntimeError; impl NyashInterpreter { /// IntentBoxのメソッド実行 (RwLock版) @@ -16,29 +16,23 @@ impl NyashInterpreter { intent_box: &IntentBox, method: &str, _arguments: &[ASTNode], - ) -> Result, RuntimeError> { + ) -> Result, RuntimeError> { match method { // メッセージ名取得 - "getName" | "name" => { - Ok(intent_box.get_name()) - } - + "getName" | "name" => Ok(intent_box.get_name()), + // ペイロード取得(JSON文字列として) - "getPayload" | "payload" => { - Ok(intent_box.get_payload()) - } - + "getPayload" | "payload" => Ok(intent_box.get_payload()), + // 型情報取得 - "getType" | "type" => { - Ok(Box::new(StringBox::new("IntentBox"))) - } - + "getType" | "type" => Ok(Box::new(StringBox::new("IntentBox"))), + _ => Err(RuntimeError::UndefinedVariable { name: format!("IntentBox method '{}' not found", method), - }) + }), } } - + // P2PBoxのメソッド実装(RwLockベース) pub(in crate::interpreter) fn execute_p2p_box_method( &mut self, @@ -46,8 +40,14 @@ impl NyashInterpreter { method: &str, arguments: &[ASTNode], ) -> Result, RuntimeError> { - if crate::interpreter::utils::debug_on() || std::env::var("NYASH_DEBUG_P2P").unwrap_or_default() == "1" { - eprintln!("[Interp:P2P] {}(..) called with {} args", method, arguments.len()); + if crate::interpreter::utils::debug_on() + || std::env::var("NYASH_DEBUG_P2P").unwrap_or_default() == "1" + { + eprintln!( + "[Interp:P2P] {}(..) called with {} args", + method, + arguments.len() + ); } match method { // ノードID取得 @@ -59,7 +59,9 @@ impl NyashInterpreter { // ノード到達可能性確認 "isReachable" => { if arguments.is_empty() { - return Err(RuntimeError::InvalidOperation { message: "isReachable requires node_id argument".to_string() }); + return Err(RuntimeError::InvalidOperation { + message: "isReachable requires node_id argument".to_string(), + }); } let node_id_result = self.execute_expression(&arguments[0])?; Ok(p2p_box.is_reachable(node_id_result)) @@ -68,7 +70,9 @@ impl NyashInterpreter { // send メソッド実装(ResultBox返却) "send" => { if arguments.len() < 2 { - return Err(RuntimeError::InvalidOperation { message: "send requires (to, intent) arguments".to_string() }); + return Err(RuntimeError::InvalidOperation { + message: "send requires (to, intent) arguments".to_string(), + }); } let to_result = self.execute_expression(&arguments[0])?; let intent_result = self.execute_expression(&arguments[1])?; @@ -78,7 +82,9 @@ impl NyashInterpreter { // ping: health check using sys.ping/sys.pong "ping" => { if arguments.is_empty() { - return Err(RuntimeError::InvalidOperation { message: "ping requires (to [, timeout_ms]) arguments".to_string() }); + return Err(RuntimeError::InvalidOperation { + message: "ping requires (to [, timeout_ms]) arguments".to_string(), + }); } let to_result = self.execute_expression(&arguments[0])?; if arguments.len() >= 2 { @@ -93,7 +99,9 @@ impl NyashInterpreter { // on メソッド実装(ResultBox返却) "on" => { if arguments.len() < 2 { - return Err(RuntimeError::InvalidOperation { message: "on requires (intentName, handler) arguments".to_string() }); + return Err(RuntimeError::InvalidOperation { + message: "on requires (intentName, handler) arguments".to_string(), + }); } let name_val = self.execute_expression(&arguments[0])?; let handler_val = self.execute_expression(&arguments[1])?; @@ -109,7 +117,9 @@ impl NyashInterpreter { // onOnce / off "onOnce" | "on_once" => { if arguments.len() < 2 { - return Err(RuntimeError::InvalidOperation { message: "onOnce requires (intentName, handler) arguments".to_string() }); + return Err(RuntimeError::InvalidOperation { + message: "onOnce requires (intentName, handler) arguments".to_string(), + }); } let name_val = self.execute_expression(&arguments[0])?; let handler_val = self.execute_expression(&arguments[1])?; @@ -117,13 +127,17 @@ impl NyashInterpreter { } "off" => { if arguments.len() < 1 { - return Err(RuntimeError::InvalidOperation { message: "off requires (intentName) argument".to_string() }); + return Err(RuntimeError::InvalidOperation { + message: "off requires (intentName) argument".to_string(), + }); } let name_val = self.execute_expression(&arguments[0])?; Ok(p2p_box.off(name_val)) } - _ => Err(RuntimeError::UndefinedVariable { name: format!("P2PBox method '{}' not found", method) }), + _ => Err(RuntimeError::UndefinedVariable { + name: format!("P2PBox method '{}' not found", method), + }), } } } diff --git a/src/interpreter/methods/system_methods.rs b/src/interpreter/methods/system_methods.rs index d5692bdf..36ba38cd 100644 --- a/src/interpreter/methods/system_methods.rs +++ b/src/interpreter/methods/system_methods.rs @@ -1,15 +1,15 @@ /*! * System Methods Module - * + * * Contains system-level Box type method implementations: * - GcConfigBox: Garbage collector configuration * - DebugConfigBox: Debug and observability configuration */ use crate::ast::ASTNode; -use crate::box_trait::{NyashBox, BoolBox}; -use crate::boxes::gc_config_box::GcConfigBox; +use crate::box_trait::{BoolBox, NyashBox}; use crate::boxes::debug_config_box::DebugConfigBox; +use crate::boxes::gc_config_box::GcConfigBox; use crate::interpreter::{NyashInterpreter, RuntimeError}; impl NyashInterpreter { @@ -24,59 +24,71 @@ impl NyashInterpreter { "setFlag" => { if arguments.len() != 2 { return Err(RuntimeError::InvalidOperation { - message: format!("GcConfigBox.setFlag expects 2 arguments, got {}", arguments.len()), + message: format!( + "GcConfigBox.setFlag expects 2 arguments, got {}", + arguments.len() + ), }); } let name = self.execute_expression(&arguments[0])?; let on = self.execute_expression(&arguments[1])?; - + let name_str = name.to_string_box().value; let on_bool = if let Some(b) = on.as_any().downcast_ref::() { b.value } else { on.to_string_box().value.to_lowercase() == "true" }; - + let mut gc_clone = gc_box.clone(); gc_clone.set_flag(&name_str, on_bool); Ok(Box::new(gc_clone)) } - + "getFlag" => { if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("GcConfigBox.getFlag expects 1 argument, got {}", arguments.len()), + message: format!( + "GcConfigBox.getFlag expects 1 argument, got {}", + arguments.len() + ), }); } let name = self.execute_expression(&arguments[0])?; let name_str = name.to_string_box().value; Ok(gc_box.get_flag(&name_str)) } - + "apply" => { if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("GcConfigBox.apply expects 0 arguments, got {}", arguments.len()), + message: format!( + "GcConfigBox.apply expects 0 arguments, got {}", + arguments.len() + ), }); } Ok(gc_box.apply()) } - + "summary" => { if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("GcConfigBox.summary expects 0 arguments, got {}", arguments.len()), + message: format!( + "GcConfigBox.summary expects 0 arguments, got {}", + arguments.len() + ), }); } Ok(gc_box.summary()) } - + _ => Err(RuntimeError::InvalidOperation { message: format!("GcConfigBox has no method '{}'", method), }), } } - + /// Execute DebugConfigBox methods pub(crate) fn execute_debug_config_method( &mut self, @@ -88,81 +100,99 @@ impl NyashInterpreter { "setFlag" => { if arguments.len() != 2 { return Err(RuntimeError::InvalidOperation { - message: format!("DebugConfigBox.setFlag expects 2 arguments, got {}", arguments.len()), + message: format!( + "DebugConfigBox.setFlag expects 2 arguments, got {}", + arguments.len() + ), }); } let name = self.execute_expression(&arguments[0])?; let on = self.execute_expression(&arguments[1])?; - + let name_str = name.to_string_box().value; let on_bool = if let Some(b) = on.as_any().downcast_ref::() { b.value } else { on.to_string_box().value.to_lowercase() == "true" }; - + let mut debug_clone = debug_box.clone(); debug_clone.set_flag(&name_str, on_bool); Ok(Box::new(debug_clone)) } - + "setPath" => { if arguments.len() != 2 { return Err(RuntimeError::InvalidOperation { - message: format!("DebugConfigBox.setPath expects 2 arguments, got {}", arguments.len()), + message: format!( + "DebugConfigBox.setPath expects 2 arguments, got {}", + arguments.len() + ), }); } let name = self.execute_expression(&arguments[0])?; let path = self.execute_expression(&arguments[1])?; - + let name_str = name.to_string_box().value; let path_str = path.to_string_box().value; - + let mut debug_clone = debug_box.clone(); debug_clone.set_path(&name_str, &path_str); Ok(Box::new(debug_clone)) } - + "getFlag" => { if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("DebugConfigBox.getFlag expects 1 argument, got {}", arguments.len()), + message: format!( + "DebugConfigBox.getFlag expects 1 argument, got {}", + arguments.len() + ), }); } let name = self.execute_expression(&arguments[0])?; let name_str = name.to_string_box().value; Ok(debug_box.get_flag(&name_str)) } - + "getPath" => { if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("DebugConfigBox.getPath expects 1 argument, got {}", arguments.len()), + message: format!( + "DebugConfigBox.getPath expects 1 argument, got {}", + arguments.len() + ), }); } let name = self.execute_expression(&arguments[0])?; let name_str = name.to_string_box().value; Ok(debug_box.get_path(&name_str)) } - + "apply" => { if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("DebugConfigBox.apply expects 0 arguments, got {}", arguments.len()), + message: format!( + "DebugConfigBox.apply expects 0 arguments, got {}", + arguments.len() + ), }); } Ok(debug_box.apply()) } - + "summary" => { if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("DebugConfigBox.summary expects 0 arguments, got {}", arguments.len()), + message: format!( + "DebugConfigBox.summary expects 0 arguments, got {}", + arguments.len() + ), }); } Ok(debug_box.summary()) } - + _ => Err(RuntimeError::InvalidOperation { message: format!("DebugConfigBox has no method '{}'", method), }), diff --git a/src/interpreter/methods_dispatch.rs b/src/interpreter/methods_dispatch.rs index 4239eab2..20111446 100644 --- a/src/interpreter/methods_dispatch.rs +++ b/src/interpreter/methods_dispatch.rs @@ -1,14 +1,17 @@ //! Central builtin method dispatcher (thin wrapper) -use crate::ast::ASTNode; -use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore}; -use crate::boxes::{ArrayBox, FloatBox, BufferBox, ResultBox, FutureBox, JSONBox, HttpClientBox, StreamBox, RegexBox, MathBox}; -use crate::boxes::{null_box, time_box, map_box, random_box, sound_box, debug_box, console_box}; -use crate::boxes::{gc_config_box::GcConfigBox, debug_config_box::DebugConfigBox}; -use crate::boxes::ref_cell_box::RefCellBox as RcCell; -use crate::boxes::file; -use crate::channel_box::ChannelBox; use super::{NyashInterpreter, RuntimeError}; +use crate::ast::ASTNode; +use crate::box_trait::{BoolBox, BoxCore, IntegerBox, NyashBox, StringBox}; +use crate::boxes::file; +use crate::boxes::ref_cell_box::RefCellBox as RcCell; +use crate::boxes::{console_box, debug_box, map_box, null_box, random_box, sound_box, time_box}; +use crate::boxes::{debug_config_box::DebugConfigBox, gc_config_box::GcConfigBox}; +use crate::boxes::{ + ArrayBox, BufferBox, FloatBox, FutureBox, HttpClientBox, JSONBox, MathBox, RegexBox, ResultBox, + StreamBox, +}; +use crate::channel_box::ChannelBox; impl NyashInterpreter { /// Try dispatching a builtin method based on dynamic type. @@ -136,10 +139,10 @@ impl NyashInterpreter { method: &str, arguments: &[ASTNode], ) -> Option, RuntimeError>> { - use crate::box_trait::{StringBox, IntegerBox}; + use crate::box_trait::{IntegerBox, StringBox}; use crate::boxes::MathBox; - use crate::instance_v2::InstanceBox; use crate::finalization; + use crate::instance_v2::InstanceBox; let instance = match obj_value.as_any().downcast_ref::() { Some(i) => i, @@ -149,11 +152,18 @@ impl NyashInterpreter { // fini() special handling (idempotent, weak prohibition) if method == "fini" { // weak-fini prohibition check: me..fini() - if let ASTNode::FieldAccess { object: field_object, field, .. } = object_ast { + if let ASTNode::FieldAccess { + object: field_object, + field, + .. + } = object_ast + { if let ASTNode::Variable { name, .. } = field_object.as_ref() { if name == "me" { if let Ok(current_me) = self.resolve_variable("me") { - if let Some(current_instance) = (*current_me).as_any().downcast_ref::() { + if let Some(current_instance) = + (*current_me).as_any().downcast_ref::() + { if current_instance.is_weak_field(field) { return Some(Err(RuntimeError::InvalidOperation { message: format!( @@ -175,11 +185,17 @@ impl NyashInterpreter { let saved = self.save_local_vars(); self.local_vars.clear(); self.declare_local_variable("me", obj_value.clone_or_share()); - let mut _result = Box::new(crate::box_trait::VoidBox::new()) as Box; + let mut _result = + Box::new(crate::box_trait::VoidBox::new()) as Box; for statement in &body { match self.execute_statement(statement) { - Ok(v) => { _result = v; }, - Err(e) => { self.restore_local_vars(saved); return Some(Err(e)); } + Ok(v) => { + _result = v; + } + Err(e) => { + self.restore_local_vars(saved); + return Some(Err(e)); + } } if let super::ControlFlow::Return(_) = &self.control_flow { self.control_flow = super::ControlFlow::None; @@ -191,7 +207,9 @@ impl NyashInterpreter { } let target_info = obj_value.to_string_box().value; self.trigger_weak_reference_invalidation(&target_info); - if let Err(e) = instance.fini() { return Some(Err(RuntimeError::InvalidOperation { message: e })); } + if let Err(e) = instance.fini() { + return Some(Err(RuntimeError::InvalidOperation { message: e })); + } finalization::mark_as_finalized(instance.box_id()); return Some(Ok(Box::new(crate::box_trait::VoidBox::new()))); } @@ -199,7 +217,10 @@ impl NyashInterpreter { // Local method on instance if let Some(method_ast) = instance.get_method(method) { if let ASTNode::FunctionDeclaration { params, body, .. } = method_ast.clone() { - eprintln!("[dbg] enter instance method {}.{}", instance.class_name, method); + eprintln!( + "[dbg] enter instance method {}.{}", + instance.class_name, method + ); // Evaluate args in current context let mut arg_values = Vec::new(); for a in arguments { @@ -210,7 +231,12 @@ impl NyashInterpreter { } if arg_values.len() != params.len() { return Some(Err(RuntimeError::InvalidOperation { - message: format!("Method {} expects {} arguments, got {}", method, params.len(), arg_values.len()), + message: format!( + "Method {} expects {} arguments, got {}", + method, + params.len(), + arg_values.len() + ), })); } let saved = self.save_local_vars(); @@ -222,7 +248,9 @@ impl NyashInterpreter { let mut result: Box = Box::new(crate::box_trait::VoidBox::new()); for stmt in &body { match self.execute_statement(stmt) { - Ok(v) => { result = v; }, + Ok(v) => { + result = v; + } Err(e) => return Some(Err(e)), } if let super::ControlFlow::Return(ret) = &self.control_flow { @@ -232,17 +260,25 @@ impl NyashInterpreter { } } self.restore_local_vars(saved); - eprintln!("[dbg] exit instance method {}.{}", instance.class_name, method); + eprintln!( + "[dbg] exit instance method {}.{}", + instance.class_name, method + ); return Some(Ok(result)); } else { - return Some(Err(RuntimeError::InvalidOperation { message: format!("Method '{}' is not a valid function declaration", method) })); + return Some(Err(RuntimeError::InvalidOperation { + message: format!("Method '{}' is not a valid function declaration", method), + })); } } // Builtin parent method promotion (StringBox/IntegerBox/MathBox) let parent_names = { let decls = self.shared.box_declarations.read().unwrap(); - decls.get(&instance.class_name).map(|d| d.extends.clone()).unwrap_or_default() + decls + .get(&instance.class_name) + .map(|d| d.extends.clone()) + .unwrap_or_default() }; for parent_name in &parent_names { if crate::box_trait::is_builtin_box(parent_name) { @@ -280,13 +316,18 @@ impl NyashInterpreter { { if let Some(plugin_shared) = instance.get_field_legacy("__plugin_content") { let plugin_ref = &*plugin_shared; - if let Some(plugin) = plugin_ref.as_any().downcast_ref::() { + if let Some(plugin) = plugin_ref + .as_any() + .downcast_ref::( + ) { return Some(self.call_plugin_method(plugin, method, arguments)); } } } // Not handled here - Some(Err(RuntimeError::InvalidOperation { message: format!("Method '{}' not found in {}", method, instance.class_name) })) + Some(Err(RuntimeError::InvalidOperation { + message: format!("Method '{}' not found in {}", method, instance.class_name), + })) } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index fd5809d5..e89f603c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,54 +1,53 @@ /*! * Nyash Interpreter - Modular Rust Implementation - * + * * Refactored from massive 2,633-line interpreter.rs into logical modules * Everything is Box philosophy with clean separation of concerns */ // Import all necessary dependencies use crate::ast::{ASTNode, CatchClause}; -use crate::box_trait::{NyashBox, StringBox, BoolBox, VoidBox, ErrorBox, BoxCore}; -use crate::boxes::FutureBox; -use crate::instance_v2::InstanceBox; -use crate::channel_box::ChannelBox; -use crate::boxes::math_box::MathBox; -use crate::boxes::time_box::TimerBox; -use crate::boxes::random_box::RandomBox; +use crate::box_trait::{BoolBox, BoxCore, ErrorBox, NyashBox, StringBox, VoidBox}; use crate::boxes::debug_box::DebugBox; +use crate::boxes::math_box::MathBox; +use crate::boxes::random_box::RandomBox; +use crate::boxes::time_box::TimerBox; +use crate::boxes::FutureBox; +use crate::channel_box::ChannelBox; +use crate::instance_v2::InstanceBox; // WASM-specific Box types (conditionally included) #[cfg(target_arch = "wasm32")] -use crate::boxes::web::{WebDisplayBox, WebConsoleBox, WebCanvasBox}; +use crate::boxes::web::{WebCanvasBox, WebConsoleBox, WebDisplayBox}; use crate::exception_box; use std::collections::HashMap; -// Module declarations +// Module declarations mod async_methods; mod box_methods; -mod core; -mod eval; mod calls; -mod methods_dispatch; -pub mod utils; -pub mod state; +mod core; pub mod errors; +mod eval; mod expressions; -mod statements; mod functions; +mod io; +mod math_methods; +mod methods; +mod methods_dispatch; pub mod objects; mod objects_basic_constructors; -mod io; -mod methods; -mod math_methods; -mod system_methods; -mod web_methods; mod special_methods; +pub mod state; +mod statements; +mod system_methods; +pub mod utils; +mod web_methods; // Main interpreter implementation - will be moved from interpreter.rs pub use core::NyashInterpreter; -pub use state::SharedState; pub use errors::RuntimeError; - +pub use state::SharedState; /// 実行制御フロー #[derive(Debug)] @@ -77,9 +76,9 @@ pub struct StaticBoxDefinition { pub fields: Vec, pub methods: HashMap, pub init_fields: Vec, - pub weak_fields: Vec, // 🔗 weak修飾子が付いたフィールドのリスト - pub static_init: Option>, // static { } ブロック - pub extends: Vec, // 🚀 Multi-delegation: Changed from Option to Vec + pub weak_fields: Vec, // 🔗 weak修飾子が付いたフィールドのリスト + pub static_init: Option>, // static { } ブロック + pub extends: Vec, // 🚀 Multi-delegation: Changed from Option to Vec pub implements: Vec, pub type_parameters: Vec, /// 初期化状態 @@ -89,9 +88,9 @@ pub struct StaticBoxDefinition { /// 🔥 Static Box初期化状態 #[derive(Debug, Clone, PartialEq)] pub enum StaticBoxState { - NotInitialized, // 未初期化 - Initializing, // 初期化中(循環参照検出用) - Initialized, // 初期化完了 + NotInitialized, // 未初期化 + Initializing, // 初期化中(循環参照検出用) + Initialized, // 初期化完了 } /// 関数宣言を保持する構造体 @@ -105,5 +104,5 @@ pub struct FunctionDeclaration { // Re-export core interpreter types pub use core::*; -// Import and re-export stdlib for interpreter modules +// Import and re-export stdlib for interpreter modules pub use crate::stdlib::BuiltinStdlib; diff --git a/src/interpreter/objects/fields.rs b/src/interpreter/objects/fields.rs index ada1e896..f7e0fc2a 100644 --- a/src/interpreter/objects/fields.rs +++ b/src/interpreter/objects/fields.rs @@ -15,10 +15,14 @@ impl NyashInterpreter { is_interface: bool, extends: Vec, implements: Vec, - type_parameters: Vec + type_parameters: Vec, ) -> Result<(), RuntimeError> { if !constructors.is_empty() { - eprintln!("🐛 DEBUG: Registering Box '{}' with constructors: {:?}", name, constructors.keys().collect::>()); + eprintln!( + "🐛 DEBUG: Registering Box '{}' with constructors: {:?}", + name, + constructors.keys().collect::>() + ); } if constructors.len() > 1 { let constructor_names: Vec = constructors.keys().cloned().collect(); @@ -31,7 +35,7 @@ impl NyashInterpreter { name, constructors.len(), constructor_names.join(", ") - ) + ), }); } let box_decl = super::BoxDeclaration { @@ -56,8 +60,11 @@ impl NyashInterpreter { } /// 🔥 ジェネリクス型引数の検証 - pub(super) fn validate_generic_arguments(&self, box_decl: &BoxDeclaration, type_arguments: &[String]) - -> Result<(), RuntimeError> { + pub(super) fn validate_generic_arguments( + &self, + box_decl: &BoxDeclaration, + type_arguments: &[String], + ) -> Result<(), RuntimeError> { if box_decl.type_parameters.len() != type_arguments.len() { return Err(RuntimeError::TypeError { message: format!( @@ -81,7 +88,9 @@ impl NyashInterpreter { } for type_arg in type_arguments { if !self.is_valid_type(type_arg) { - return Err(RuntimeError::TypeError { message: format!("Unknown type '{}'", type_arg) }); + return Err(RuntimeError::TypeError { + message: format!("Unknown type '{}'", type_arg), + }); } } Ok(()) @@ -90,14 +99,22 @@ impl NyashInterpreter { /// 型が有効かどうかをチェック fn is_valid_type(&self, type_name: &str) -> bool { if let Ok(reg) = self.runtime.box_registry.lock() { - if reg.has_type(type_name) { return true; } + if reg.has_type(type_name) { + return true; + } } - self.shared.box_declarations.read().unwrap().contains_key(type_name) + self.shared + .box_declarations + .read() + .unwrap() + .contains_key(type_name) } /// 継承チェーンを解決してフィールドとメソッドを収集 - Inheritance resolution - pub(crate) fn resolve_inheritance(&self, box_decl: &BoxDeclaration) - -> Result<(Vec, HashMap), RuntimeError> { + pub(crate) fn resolve_inheritance( + &self, + box_decl: &BoxDeclaration, + ) -> Result<(Vec, HashMap), RuntimeError> { let mut all_fields = Vec::new(); let mut all_methods = HashMap::new(); for parent_name in &box_decl.extends { @@ -105,20 +122,28 @@ impl NyashInterpreter { let is_builtin = is_builtin_box(parent_name); #[cfg(all(feature = "gui", not(target_arch = "wasm32")))] { - if parent_name == "EguiBox" { is_builtin = true; } + if parent_name == "EguiBox" { + is_builtin = true; + } } if is_builtin { // skip builtin inheritance } else { let parent_decl = { let box_decls = self.shared.box_declarations.read().unwrap(); - box_decls.get(parent_name) - .ok_or(RuntimeError::UndefinedClass { name: parent_name.to_string() })? + box_decls + .get(parent_name) + .ok_or(RuntimeError::UndefinedClass { + name: parent_name.to_string(), + })? .clone() }; if parent_decl.is_interface { return Err(RuntimeError::InvalidOperation { - message: format!("Cannot extend interface '{}'. Use 'implements' instead.", parent_name), + message: format!( + "Cannot extend interface '{}'. Use 'implements' instead.", + parent_name + ), }); } let (parent_fields, parent_methods) = self.resolve_inheritance(&parent_decl)?; @@ -128,7 +153,9 @@ impl NyashInterpreter { } all_fields.extend(box_decl.fields.clone()); for init_field in &box_decl.init_fields { - if !all_fields.contains(init_field) { all_fields.push(init_field.clone()); } + if !all_fields.contains(init_field) { + all_fields.push(init_field.clone()); + } } for (method_name, method_ast) in &box_decl.methods { all_methods.insert(method_name.clone(), method_ast.clone()); @@ -136,18 +163,25 @@ impl NyashInterpreter { for interface_name in &box_decl.implements { let interface_decl = { let box_decls = self.shared.box_declarations.read().unwrap(); - box_decls.get(interface_name) - .ok_or(RuntimeError::UndefinedClass { name: interface_name.clone() })? + box_decls + .get(interface_name) + .ok_or(RuntimeError::UndefinedClass { + name: interface_name.clone(), + })? .clone() }; if !interface_decl.is_interface { - return Err(RuntimeError::InvalidOperation { message: format!("'{}' is not an interface", interface_name) }); + return Err(RuntimeError::InvalidOperation { + message: format!("'{}' is not an interface", interface_name), + }); } for (required_method, _) in &interface_decl.methods { if !all_methods.contains_key(required_method) { return Err(RuntimeError::InvalidOperation { - message: format!("Class '{}' must implement method '{}' from interface '{}'", - box_decl.name, required_method, interface_name), + message: format!( + "Class '{}' must implement method '{}' from interface '{}'", + box_decl.name, required_method, interface_name + ), }); } } @@ -159,7 +193,7 @@ impl NyashInterpreter { pub(super) fn specialize_generic_class( &self, generic_decl: &BoxDeclaration, - type_arguments: &[String] + type_arguments: &[String], ) -> Result { use std::collections::HashMap; let specialized_name = format!("{}_{}", generic_decl.name, type_arguments.join("_")); @@ -170,7 +204,8 @@ impl NyashInterpreter { let mut specialized = generic_decl.clone(); specialized.name = specialized_name.clone(); specialized.type_parameters.clear(); - specialized.init_fields = self.substitute_types_in_fields(&specialized.init_fields, &type_mapping); + specialized.init_fields = + self.substitute_types_in_fields(&specialized.init_fields, &type_mapping); let mut updated_constructors = HashMap::new(); for (old_key, constructor_node) in &generic_decl.constructors { if let Some(args_count) = old_key.split('/').nth(1) { @@ -186,7 +221,7 @@ impl NyashInterpreter { pub(super) fn substitute_types_in_fields( &self, fields: &[String], - _type_mapping: &HashMap + _type_mapping: &HashMap, ) -> Vec { fields.to_vec() } diff --git a/src/interpreter/objects/methods.rs b/src/interpreter/objects/methods.rs index 5e59cb17..7ef22954 100644 --- a/src/interpreter/objects/methods.rs +++ b/src/interpreter/objects/methods.rs @@ -8,14 +8,26 @@ impl NyashInterpreter { instance: &SharedNyashBox, constructor: &ASTNode, arguments: &[ASTNode], - box_decl: &BoxDeclaration + box_decl: &BoxDeclaration, ) -> Result<(), RuntimeError> { - if let ASTNode::FunctionDeclaration { name: _, params, body, .. } = constructor { + if let ASTNode::FunctionDeclaration { + name: _, + params, + body, + .. + } = constructor + { let mut arg_values = Vec::new(); - for arg in arguments { arg_values.push(self.execute_expression(arg)?); } + for arg in arguments { + arg_values.push(self.execute_expression(arg)?); + } if params.len() != arg_values.len() { return Err(RuntimeError::InvalidOperation { - message: format!("Constructor expects {} arguments, got {}", params.len(), arg_values.len()), + message: format!( + "Constructor expects {} arguments, got {}", + params.len(), + arg_values.len() + ), }); } let saved_locals = self.save_local_vars(); @@ -31,35 +43,52 @@ impl NyashInterpreter { }); let mut result = Ok(()); for statement in body.iter() { - if let Err(e) = self.execute_statement(statement) { result = Err(e); break; } + if let Err(e) = self.execute_statement(statement) { + result = Err(e); + break; + } } self.restore_local_vars(saved_locals); self.current_constructor_context = old_context; result } else { - Err(RuntimeError::InvalidOperation { message: "Invalid constructor node".to_string() }) + Err(RuntimeError::InvalidOperation { + message: "Invalid constructor node".to_string(), + }) } } /// 親コンストラクタを実行 - Parent constructor execution - pub(crate) fn execute_parent_constructor(&mut self, parent_class: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(crate) fn execute_parent_constructor( + &mut self, + parent_class: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { let parent_decl = { let box_decls = self.shared.box_declarations.read().unwrap(); - box_decls.get(parent_class) - .ok_or(RuntimeError::UndefinedClass { name: parent_class.to_string() })? + box_decls + .get(parent_class) + .ok_or(RuntimeError::UndefinedClass { + name: parent_class.to_string(), + })? .clone() }; let birth_key = format!("birth/{}", arguments.len()); if let Some(parent_constructor) = parent_decl.constructors.get(&birth_key) { - let this_instance = self.resolve_variable("me").map_err(|_| RuntimeError::InvalidOperation { - message: "'this' not available in parent constructor call".to_string(), - })?; + let this_instance = + self.resolve_variable("me") + .map_err(|_| RuntimeError::InvalidOperation { + message: "'this' not available in parent constructor call".to_string(), + })?; self.execute_constructor(&this_instance, parent_constructor, arguments, &parent_decl)?; Ok(Box::new(VoidBox::new())) } else { Err(RuntimeError::InvalidOperation { - message: format!("No constructor found for parent class {} with {} arguments", parent_class, arguments.len()), + message: format!( + "No constructor found for parent class {} with {} arguments", + parent_class, + arguments.len() + ), }) } } diff --git a/src/interpreter/objects/mod.rs b/src/interpreter/objects/mod.rs index 935a24a6..f521aadf 100644 --- a/src/interpreter/objects/mod.rs +++ b/src/interpreter/objects/mod.rs @@ -9,7 +9,6 @@ use super::*; -mod ops; -mod methods; mod fields; - +mod methods; +mod ops; diff --git a/src/interpreter/objects/ops.rs b/src/interpreter/objects/ops.rs index b7663e13..870b9171 100644 --- a/src/interpreter/objects/ops.rs +++ b/src/interpreter/objects/ops.rs @@ -4,12 +4,22 @@ use std::sync::Arc; impl NyashInterpreter { /// Evaluate `new` expression arguments to NyashBox values - pub(super) fn new_eval_args(&mut self, arguments: &[ASTNode]) -> Result>, RuntimeError> { - arguments.iter().map(|arg| self.execute_expression(arg)).collect() + pub(super) fn new_eval_args( + &mut self, + arguments: &[ASTNode], + ) -> Result>, RuntimeError> { + arguments + .iter() + .map(|arg| self.execute_expression(arg)) + .collect() } /// If user-defined and type args provided, validate/specialize and register declaration - pub(super) fn new_specialize_if_needed(&self, class: &str, type_arguments: &[String]) -> Result { + pub(super) fn new_specialize_if_needed( + &self, + class: &str, + type_arguments: &[String], + ) -> Result { let mut target_class = class.to_string(); let user_defined_exists = { let box_decls = self.shared.box_declarations.read().unwrap(); @@ -45,7 +55,10 @@ impl NyashInterpreter { match registry_lock.create_box(target_class, &args) { Ok(box_instance) => { // Check if this is a user-defined box that needs constructor execution - if let Some(_instance_box) = box_instance.as_any().downcast_ref::() { + if let Some(_instance_box) = box_instance + .as_any() + .downcast_ref::() + { // Check if we have a box declaration for this class let (box_decl_opt, constructor_opt) = { let box_decls = self.shared.box_declarations.read().unwrap(); @@ -54,28 +67,39 @@ impl NyashInterpreter { let birth_key = format!("birth/{}", arguments.len()); let constructor = box_decl.constructors.get(&birth_key).cloned(); (Some(box_decl.clone()), constructor) - } else { (None, None) } + } else { + (None, None) + } }; if let Some(box_decl) = box_decl_opt { if let Some(constructor) = constructor_opt { // Execute the constructor let instance_arc: SharedNyashBox = Arc::from(box_instance); drop(registry_lock); // Release lock before executing constructor - self.execute_constructor(&instance_arc, &constructor, arguments, &box_decl)?; + self.execute_constructor( + &instance_arc, + &constructor, + arguments, + &box_decl, + )?; return Ok((*instance_arc).clone_box()); } else if arguments.is_empty() { // No constructor needed for zero arguments return Ok(box_instance); } else { return Err(RuntimeError::InvalidOperation { - message: format!("No constructor found for {} with {} arguments", target_class, arguments.len()), + message: format!( + "No constructor found for {} with {} arguments", + target_class, + arguments.len() + ), }); } } } // Not a user-defined box or no constructor needed Ok(box_instance) - }, + } Err(e) => { // Fallback: handle basic built-in boxes directly (e.g., FutureBox) // This keeps interpreter usability when registry has no provider. @@ -84,13 +108,17 @@ impl NyashInterpreter { Ok(b) => Ok(b), Err(_) => Err(e), } - }, + } } } /// new式を実行 - Object creation engine - pub(crate) fn execute_new(&mut self, class: &str, arguments: &[ASTNode], type_arguments: &[String]) - -> Result, RuntimeError> { + pub(crate) fn execute_new( + &mut self, + class: &str, + arguments: &[ASTNode], + type_arguments: &[String], + ) -> Result, RuntimeError> { // 80/20 path: unified registry + constructor let args = self.new_eval_args(arguments)?; let target_class = self.new_specialize_if_needed(class, type_arguments)?; diff --git a/src/interpreter/objects_basic_constructors.rs b/src/interpreter/objects_basic_constructors.rs index b359802d..a1c57ae4 100644 --- a/src/interpreter/objects_basic_constructors.rs +++ b/src/interpreter/objects_basic_constructors.rs @@ -3,27 +3,30 @@ use crate::ast::ASTNode; use crate::box_trait::*; -use crate::interpreter::{NyashInterpreter as Interpreter, RuntimeError}; -use crate::boxes::FloatBox; -use crate::boxes::null_box::NullBox; use crate::boxes::map_box::MapBox; +use crate::boxes::null_box::NullBox; +use crate::boxes::FloatBox; +use crate::interpreter::{NyashInterpreter as Interpreter, RuntimeError}; impl Interpreter { /// Create basic type boxes (StringBox, IntegerBox, BoolBox, etc.) pub(super) fn create_basic_box( - &mut self, - class: &str, - arguments: &[ASTNode] + &mut self, + class: &str, + arguments: &[ASTNode], ) -> Result, RuntimeError> { match class { "StringBox" => { // StringBoxは引数1個(文字列値)で作成 if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("StringBox constructor expects 1 argument, got {}", arguments.len()), + message: format!( + "StringBox constructor expects 1 argument, got {}", + arguments.len() + ), }); } - + let value = self.execute_expression(&arguments[0])?; if let Some(s) = value.as_any().downcast_ref::() { return Ok(Box::new(StringBox::new(s.value.clone()))); @@ -35,40 +38,49 @@ impl Interpreter { return Ok(Box::new(StringBox::new(value.to_string_box().value))); } } - + "IntegerBox" => { // IntegerBoxは引数1個(整数値)で作成 if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("IntegerBox constructor expects 1 argument, got {}", arguments.len()), + message: format!( + "IntegerBox constructor expects 1 argument, got {}", + arguments.len() + ), }); } - + let value = self.execute_expression(&arguments[0])?; if let Some(i) = value.as_any().downcast_ref::() { return Ok(Box::new(IntegerBox::new(i.value))); } else if let Some(s) = value.as_any().downcast_ref::() { match s.value.parse::() { Ok(n) => return Ok(Box::new(IntegerBox::new(n))), - Err(_) => return Err(RuntimeError::TypeError { - message: format!("Cannot convert '{}' to integer", s.value), - }), + Err(_) => { + return Err(RuntimeError::TypeError { + message: format!("Cannot convert '{}' to integer", s.value), + }) + } } } else { return Err(RuntimeError::TypeError { - message: "IntegerBox constructor requires integer or string argument".to_string(), + message: "IntegerBox constructor requires integer or string argument" + .to_string(), }); } } - + "BoolBox" => { // BoolBoxは引数1個(ブール値)で作成 if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("BoolBox constructor expects 1 argument, got {}", arguments.len()), + message: format!( + "BoolBox constructor expects 1 argument, got {}", + arguments.len() + ), }); } - + let value = self.execute_expression(&arguments[0])?; if let Some(b) = value.as_any().downcast_ref::() { return Ok(Box::new(BoolBox::new(b.value))); @@ -76,57 +88,72 @@ impl Interpreter { let val = match s.value.as_str() { "true" => true, "false" => false, - _ => return Err(RuntimeError::TypeError { - message: format!("Cannot convert '{}' to boolean", s.value), - }), + _ => { + return Err(RuntimeError::TypeError { + message: format!("Cannot convert '{}' to boolean", s.value), + }) + } }; return Ok(Box::new(BoolBox::new(val))); } else { return Err(RuntimeError::TypeError { - message: "BoolBox constructor requires boolean or string argument".to_string(), + message: "BoolBox constructor requires boolean or string argument" + .to_string(), }); } } - + "ArrayBox" => { // ArrayBoxは引数なしで作成 if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("ArrayBox constructor expects 0 arguments, got {}", arguments.len()), + message: format!( + "ArrayBox constructor expects 0 arguments, got {}", + arguments.len() + ), }); } return Ok(Box::new(ArrayBox::new())); } - + "NullBox" => { // NullBoxは引数なしで作成 if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("NullBox constructor expects 0 arguments, got {}", arguments.len()), + message: format!( + "NullBox constructor expects 0 arguments, got {}", + arguments.len() + ), }); } return Ok(Box::new(NullBox::new())); } - + "MapBox" => { // MapBoxは引数なしで作成 if !arguments.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("MapBox constructor expects 0 arguments, got {}", arguments.len()), + message: format!( + "MapBox constructor expects 0 arguments, got {}", + arguments.len() + ), }); } let map_box = Box::new(MapBox::new()) as Box; return Ok(map_box); } - + "FloatBox" => { // FloatBoxは引数1個(浮動小数点数値)で作成 if arguments.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("FloatBox constructor expects 1 argument, got {}", arguments.len()), + message: format!( + "FloatBox constructor expects 1 argument, got {}", + arguments.len() + ), }); } - + let value = self.execute_expression(&arguments[0])?; if let Some(f) = value.as_any().downcast_ref::() { return Ok(Box::new(FloatBox::new(f.value))); @@ -135,22 +162,28 @@ impl Interpreter { } else if let Some(s) = value.as_any().downcast_ref::() { match s.value.parse::() { Ok(n) => return Ok(Box::new(FloatBox::new(n))), - Err(_) => return Err(RuntimeError::TypeError { - message: format!("Cannot convert '{}' to float", s.value), - }), + Err(_) => { + return Err(RuntimeError::TypeError { + message: format!("Cannot convert '{}' to float", s.value), + }) + } } } else { return Err(RuntimeError::TypeError { - message: "FloatBox constructor requires float, integer, or string argument".to_string(), + message: "FloatBox constructor requires float, integer, or string argument" + .to_string(), }); } } - + "FutureBox" => { // FutureBox([value]) — optional initial value if arguments.len() > 1 { return Err(RuntimeError::InvalidOperation { - message: format!("FutureBox constructor expects 0 or 1 argument, got {}", arguments.len()), + message: format!( + "FutureBox constructor expects 0 or 1 argument, got {}", + arguments.len() + ), }); } let fut = crate::boxes::future::NyashFutureBox::new(); @@ -160,7 +193,7 @@ impl Interpreter { } return Ok(Box::new(fut)); } - + _ => { // Not a basic type Err(RuntimeError::TypeError { diff --git a/src/interpreter/special_methods.rs b/src/interpreter/special_methods.rs index f0bfbac3..233232df 100644 --- a/src/interpreter/special_methods.rs +++ b/src/interpreter/special_methods.rs @@ -1,12 +1,12 @@ /*! * Special Methods Module - * + * * Extracted from box_methods.rs * Contains specialized Box method implementations: - * + * * - execute_method_box_method (MethodBox) - イベントハンドラー/関数ポインタ機能 * - execute_sound_method (SoundBox) - オーディオ機能 - * + * * These are critical special-purpose Box implementations: * - MethodBox: Essential for event handling and callback functionality * - SoundBox: Essential for audio feedback and game sound effects @@ -14,12 +14,12 @@ use super::*; use crate::boxes::SoundBox; -use crate::method_box::MethodBox; use crate::instance_v2::InstanceBox; +use crate::method_box::MethodBox; impl NyashInterpreter { /// SoundBoxのメソッド呼び出しを実行 - /// + /// /// SoundBoxはオーディオ機能を提供する重要なBox: /// - beep(), beeps() - 基本的なビープ音 /// - tone() - カスタム周波数/期間の音 @@ -27,14 +27,18 @@ impl NyashInterpreter { /// - pattern() - 音パターン再生 /// - volumeTest() - 音量テスト /// - interval() - 間隔付き音再生 - pub(super) fn execute_sound_method(&mut self, sound_box: &SoundBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_sound_method( + &mut self, + sound_box: &SoundBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドを実行 match method { "beep" => { @@ -96,7 +100,10 @@ impl NyashInterpreter { "volumeTest" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("volumeTest() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "volumeTest() expects 0 arguments, got {}", + arg_values.len() + ), }); } Ok(sound_box.volumeTest()) @@ -104,27 +111,32 @@ impl NyashInterpreter { "interval" => { if arg_values.len() != 2 { return Err(RuntimeError::InvalidOperation { - message: format!("interval() expects 2 arguments, got {}", arg_values.len()), + message: format!( + "interval() expects 2 arguments, got {}", + arg_values.len() + ), }); } Ok(sound_box.interval(arg_values[0].clone_box(), arg_values[1].clone_box())) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown SoundBox method: {}", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown SoundBox method: {}", method), + }), } } /// MethodBoxのメソッド呼び出しを実行 - /// + /// /// MethodBoxはイベントハンドラー機能の核心: /// - invoke() - メソッド参照を実際に呼び出し /// - 関数ポインタ相当の機能を提供 /// - GUI/イベント駆動プログラミングに必須 - pub(super) fn execute_method_box_method(&mut self, method_box: &MethodBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_method_box_method( + &mut self, + method_box: &MethodBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { match method { "invoke" => { // 引数を評価 @@ -132,20 +144,18 @@ impl NyashInterpreter { for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // MethodBoxのinvokeを呼び出す self.invoke_method_box(method_box, arg_values) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown MethodBox method: {}", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown MethodBox method: {}", method), + }), } } /// MethodBoxでメソッドを実際に呼び出す - /// + /// /// この関数はMethodBoxの中核機能: /// 1. インスタンスとメソッド名からメソッドを取得 /// 2. 引数数の検証 @@ -153,48 +163,56 @@ impl NyashInterpreter { /// 4. 'me' 変数の設定 /// 5. メソッド実行 /// 6. 戻り値処理 - fn invoke_method_box(&mut self, method_box: &MethodBox, args: Vec>) - -> Result, RuntimeError> { + fn invoke_method_box( + &mut self, + method_box: &MethodBox, + args: Vec>, + ) -> Result, RuntimeError> { // インスタンスを取得 let instance_arc = method_box.get_instance(); let instance = instance_arc.lock().unwrap(); - + // InstanceBoxにダウンキャスト if let Some(instance_box) = instance.as_any().downcast_ref::() { // メソッドを取得 - let method_ast = instance_box.get_method(&method_box.method_name) + let method_ast = instance_box + .get_method(&method_box.method_name) .ok_or(RuntimeError::InvalidOperation { message: format!("Method '{}' not found", method_box.method_name), })? .clone(); - + // メソッド呼び出しを実行 if let ASTNode::FunctionDeclaration { params, body, .. } = method_ast { // パラメータ数チェック if args.len() != params.len() { return Err(RuntimeError::InvalidOperation { - message: format!("Method {} expects {} arguments, got {}", - method_box.method_name, params.len(), args.len()), + message: format!( + "Method {} expects {} arguments, got {}", + method_box.method_name, + params.len(), + args.len() + ), }); } - + // local変数スタックを保存 let saved_locals = self.save_local_vars(); self.local_vars.clear(); - + // meをlocal変数として設定(インスタンス自体) self.declare_local_variable("me", instance.clone_or_share()); - + // パラメータをlocal変数として設定 for (param, arg) in params.iter().zip(args.iter()) { self.declare_local_variable(param, arg.clone_or_share()); } - + // メソッド本体を実行 let mut result = Box::new(crate::box_trait::VoidBox::new()) as Box; for statement in &body { result = self.execute_statement(statement)?; - + // return文チェック if let super::ControlFlow::Return(ret_val) = &self.control_flow { result = ret_val.clone_box(); @@ -202,14 +220,17 @@ impl NyashInterpreter { break; } } - + // local変数スタックを復元 self.restore_local_vars(saved_locals); - + Ok(result) } else { Err(RuntimeError::InvalidOperation { - message: format!("Method '{}' is not a valid function declaration", method_box.method_name), + message: format!( + "Method '{}' is not a valid function declaration", + method_box.method_name + ), }) } } else { diff --git a/src/interpreter/state.rs b/src/interpreter/state.rs index 2243e31a..a92c6378 100644 --- a/src/interpreter/state.rs +++ b/src/interpreter/state.rs @@ -1,7 +1,7 @@ +use super::{BoxDeclaration, StaticBoxDefinition}; use crate::instance_v2::InstanceBox; use std::collections::{HashMap, HashSet}; use std::sync::{Arc, Mutex, RwLock}; -use super::{BoxDeclaration, StaticBoxDefinition}; /// スレッド間で共有される状態 #[derive(Clone)] @@ -30,8 +30,8 @@ impl SharedState { pub fn new() -> Self { let global_box = InstanceBox::new( "Global".to_string(), - vec![], // フィールド名(空から始める) - HashMap::new(), // メソッド(グローバル関数) + vec![], // フィールド名(空から始める) + HashMap::new(), // メソッド(グローバル関数) ); Self { diff --git a/src/interpreter/statements.rs b/src/interpreter/statements.rs index d5715c97..a66a9982 100644 --- a/src/interpreter/statements.rs +++ b/src/interpreter/statements.rs @@ -1,14 +1,14 @@ /*! * Statement Processing Module - * + * * Extracted from core.rs - statement execution engine * Handles all statement types: assignments, if/else, loops, control flow * Core philosophy: "Everything is Box" with structured statement processing */ +use super::BuiltinStdlib; use super::*; use crate::boxes::ref_cell_box::RefCellBox; -use super::BuiltinStdlib; use std::sync::Arc; // Conditional debug macro - unified with utils::debug_on() @@ -27,46 +27,69 @@ macro_rules! idebug { impl NyashInterpreter { fn warn_if_must_use(&self, value: &Box) { - if std::env::var("NYASH_LINT_MUSTUSE").unwrap_or_default() != "1" { return; } - if !self.discard_context { return; } + if std::env::var("NYASH_LINT_MUSTUSE").unwrap_or_default() != "1" { + return; + } + if !self.discard_context { + return; + } // 重資源のヒューリスティクス: プラグインBox、またはHTTP/Socket/File系の型名 #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] { - if value.as_any().downcast_ref::().is_some() { + if value + .as_any() + .downcast_ref::() + .is_some() + { eprintln!("[lint:must_use] Discarded resource value (plugin box). Consider assigning it or calling fini()."); return; } } let ty = value.type_name(); - let heavy = matches!(ty, - "FileBox" | "SocketBox" | "SocketServerBox" | "SocketClientBox" | "SocketConnBox" | - "HTTPServerBox" | "HTTPRequestBox" | "HTTPResponseBox" | "HttpClientBox" + let heavy = matches!( + ty, + "FileBox" + | "SocketBox" + | "SocketServerBox" + | "SocketClientBox" + | "SocketConnBox" + | "HTTPServerBox" + | "HTTPRequestBox" + | "HTTPResponseBox" + | "HttpClientBox" ); if heavy { - eprintln!("[lint:must_use] Discarded {} value. Consider assigning it or calling fini().", ty); + eprintln!( + "[lint:must_use] Discarded {} value. Consider assigning it or calling fini().", + ty + ); } } /// 文を実行 - Core statement execution engine - pub(crate) fn execute_statement(&mut self, statement: &ASTNode) -> Result, RuntimeError> { + pub(crate) fn execute_statement( + &mut self, + statement: &ASTNode, + ) -> Result, RuntimeError> { match statement { - ASTNode::Assignment { target, value, .. } => { - self.execute_assignment(target, value) - } - + ASTNode::Assignment { target, value, .. } => self.execute_assignment(target, value), + ASTNode::Print { expression, .. } => { let value = self.execute_expression(expression)?; println!("{}", value.to_string_box()); Ok(Box::new(VoidBox::new())) } - - ASTNode::If { condition, then_body, else_body, .. } => { - self.execute_if(condition, then_body, else_body) - } - - ASTNode::Loop { condition, body, .. } => { - self.execute_loop(condition, body) - } - + + ASTNode::If { + condition, + then_body, + else_body, + .. + } => self.execute_if(condition, then_body, else_body), + + ASTNode::Loop { + condition, body, .. + } => self.execute_loop(condition, body), + ASTNode::Return { value, .. } => { let return_value = if let Some(val) = value { self.execute_expression(val)? @@ -82,7 +105,7 @@ impl NyashInterpreter { self.control_flow = super::ControlFlow::Return(return_value); Ok(Box::new(VoidBox::new())) } - + ASTNode::Break { .. } => { self.control_flow = super::ControlFlow::Break; Ok(Box::new(VoidBox::new())) @@ -91,24 +114,46 @@ impl NyashInterpreter { self.control_flow = super::ControlFlow::Continue; Ok(Box::new(VoidBox::new())) } - - ASTNode::Nowait { variable, expression, .. } => { - self.execute_nowait(variable, expression) - } - + + ASTNode::Nowait { + variable, + expression, + .. + } => self.execute_nowait(variable, expression), + ASTNode::UsingStatement { namespace_name, .. } => { self.execute_using_statement(namespace_name) } - + ASTNode::ImportStatement { path, alias, .. } => { // Stage-0 import: no-op (record/log only) if std::env::var("NYASH_IMPORT_TRACE").ok().as_deref() == Some("1") { - if let Some(a) = alias { eprintln!("[import] {} as {}", path, a); } else { eprintln!("[import] {}", path); } + if let Some(a) = alias { + eprintln!("[import] {} as {}", path, a); + } else { + eprintln!("[import] {}", path); + } } Ok(Box::new(VoidBox::new())) } - - ASTNode::BoxDeclaration { name, fields, public_fields, private_fields, methods, constructors, init_fields, weak_fields, is_interface, extends, implements, type_parameters, is_static, static_init, .. } => { + + ASTNode::BoxDeclaration { + name, + fields, + public_fields, + private_fields, + methods, + constructors, + init_fields, + weak_fields, + is_interface, + extends, + implements, + type_parameters, + is_static, + static_init, + .. + } => { if *is_static { // 🔥 Static Box宣言の処理 self.register_static_box_declaration( @@ -116,39 +161,45 @@ impl NyashInterpreter { fields.clone(), methods.clone(), init_fields.clone(), - weak_fields.clone(), // 🔗 Add weak_fields parameter + weak_fields.clone(), // 🔗 Add weak_fields parameter static_init.clone(), extends.clone(), implements.clone(), - type_parameters.clone() + type_parameters.clone(), )?; } else { // 通常のBox宣言の処理 - 🔥 コンストラクタオーバーロード禁止対応 self.register_box_declaration( - name.clone(), - fields.clone(), + name.clone(), + fields.clone(), public_fields.clone(), private_fields.clone(), methods.clone(), constructors.clone(), init_fields.clone(), - weak_fields.clone(), // 🔗 Add weak_fields parameter + weak_fields.clone(), // 🔗 Add weak_fields parameter *is_interface, extends.clone(), implements.clone(), - type_parameters.clone() // 🔥 ジェネリクス型パラメータ追加 + type_parameters.clone(), // 🔥 ジェネリクス型パラメータ追加 )?; // 🔥 エラーハンドリング追加 } Ok(Box::new(VoidBox::new())) } - - ASTNode::FunctionDeclaration { name, params, body, is_static, .. } => { + + ASTNode::FunctionDeclaration { + name, + params, + body, + is_static, + .. + } => { if *is_static { // 🔥 静的関数:box名.関数名の形式で解析 if let Some(dot_pos) = name.find('.') { let box_name = name[..dot_pos].to_string(); let func_name = name[dot_pos + 1..].to_string(); - + // boxのstaticメソッドとして登録 let func_ast = ASTNode::FunctionDeclaration { name: func_name.clone(), @@ -158,7 +209,7 @@ impl NyashInterpreter { is_override: false, span: crate::ast::Span::unknown(), }; - + { let mut static_funcs = self.shared.static_functions.write().unwrap(); static_funcs @@ -166,11 +217,14 @@ impl NyashInterpreter { .or_insert_with(HashMap::new) .insert(func_name.clone(), func_ast); } - + idebug!("🔥 Static function '{}.{}' registered", box_name, func_name); } else { // box名なしのstatic関数(将来的にはエラーにする) - idebug!("⚠️ Static function '{}' needs box prefix (e.g., Math.min)", name); + idebug!( + "⚠️ Static function '{}' needs box prefix (e.g., Math.min)", + name + ); } } else { // 通常の関数:従来通りGlobalBoxメソッドとして登録 @@ -178,23 +232,28 @@ impl NyashInterpreter { } Ok(Box::new(VoidBox::new())) } - + ASTNode::GlobalVar { name, value, .. } => { let val = self.execute_expression(value)?; // 🌍 革命的グローバル変数:GlobalBoxのフィールドとして設定 self.set_variable(name, val.clone_or_share())?; Ok(Box::new(VoidBox::new())) } - - ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => { - self.execute_try_catch(try_body, catch_clauses, finally_body) - } - - ASTNode::Throw { expression, .. } => { - self.execute_throw(expression) - } - - ASTNode::Local { variables, initial_values, .. } => { + + ASTNode::TryCatch { + try_body, + catch_clauses, + finally_body, + .. + } => self.execute_try_catch(try_body, catch_clauses, finally_body), + + ASTNode::Throw { expression, .. } => self.execute_throw(expression), + + ASTNode::Local { + variables, + initial_values, + .. + } => { // 🌍 革命的local変数宣言:local変数スタックに追加(初期化対応) for (i, var_name) in variables.iter().enumerate() { if let Some(Some(init_expr)) = initial_values.get(i) { @@ -208,8 +267,12 @@ impl NyashInterpreter { } Ok(Box::new(VoidBox::new())) } - - ASTNode::Outbox { variables, initial_values, .. } => { + + ASTNode::Outbox { + variables, + initial_values, + .. + } => { // 📤 革命的outbox変数宣言:static関数内で所有権移転(初期化対応) for (i, var_name) in variables.iter().enumerate() { if let Some(Some(init_expr)) = initial_values.get(i) { @@ -223,24 +286,28 @@ impl NyashInterpreter { } Ok(Box::new(VoidBox::new())) } - + // 式文(結果は多くの場合破棄されるため、must_use警告を出力) _ => { let v = self.execute_expression(statement)?; self.warn_if_must_use(&v); Ok(v) - }, + } } } - + /// 条件分岐を実行 - If/else statement processing - pub(super) fn execute_if(&mut self, condition: &ASTNode, then_body: &[ASTNode], else_body: &Option>) - -> Result, RuntimeError> { + pub(super) fn execute_if( + &mut self, + condition: &ASTNode, + then_body: &[ASTNode], + else_body: &Option>, + ) -> Result, RuntimeError> { let condition_value = self.execute_expression(condition)?; - + // 条件を真偉値として評価 let is_true = self.is_truthy(&condition_value); - + if is_true { eprintln!("[dbg] if-then enter"); for statement in then_body { @@ -260,12 +327,16 @@ impl NyashInterpreter { } eprintln!("[dbg] if-else exit"); } - + Ok(Box::new(VoidBox::new())) } - + /// ループを実行 - Loop processing: loop(condition) { body } のみ - pub(super) fn execute_loop(&mut self, condition: &Box, body: &[ASTNode]) -> Result, RuntimeError> { + pub(super) fn execute_loop( + &mut self, + condition: &Box, + body: &[ASTNode], + ) -> Result, RuntimeError> { loop { // 常に条件をチェック let condition_result = self.execute_expression(condition)?; @@ -279,11 +350,11 @@ impl NyashInterpreter { break; } } - + // ループ本体を実行 for statement in body { self.execute_statement(statement)?; - + match &self.control_flow { super::ControlFlow::Break => { self.control_flow = super::ControlFlow::None; @@ -305,44 +376,58 @@ impl NyashInterpreter { } } } - + Ok(Box::new(VoidBox::new())) } - + /// 代入処理を実行 - Assignment processing - pub(super) fn execute_assignment(&mut self, target: &ASTNode, value: &ASTNode) -> Result, RuntimeError> { + pub(super) fn execute_assignment( + &mut self, + target: &ASTNode, + value: &ASTNode, + ) -> Result, RuntimeError> { let val = self.execute_expression(value)?; - + match target { ASTNode::Variable { name, .. } => { // 🌍 革命的代入:local変数 → GlobalBoxフィールド - + // 🔗 DEMO: Weak Reference Invalidation Simulation // If we're setting a variable to 0, simulate "dropping" the previous value if val.to_string_box().value == "0" { - debug_trace!("🔗 DEBUG: Variable '{}' set to 0 - simulating object drop", name); - + debug_trace!( + "🔗 DEBUG: Variable '{}' set to 0 - simulating object drop", + name + ); + // Get the current value before dropping it if let Ok(old_value) = self.resolve_variable(name) { let old_value_str = old_value.to_string_box().value; debug_trace!("🔗 DEBUG: Old value being dropped: {}", old_value_str); - + // For demo purposes, if we're dropping a "parent" variable, // manually invalidate weak references to Parent instances if name.contains("parent") && old_value_str.contains("instance #") { - debug_trace!("🔗 DEBUG: Triggering weak reference invalidation for: {}", old_value_str); - + debug_trace!( + "🔗 DEBUG: Triggering weak reference invalidation for: {}", + old_value_str + ); + // Call the interpreter method with actual object info self.trigger_weak_reference_invalidation(&old_value_str); } } } - + // Assign-by-share for plugin handle types; clone for others let assigned = { #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] { - if val.as_any().downcast_ref::().is_some() { + if val + .as_any() + .downcast_ref::() + .is_some() + { val.share_box() } else { val.clone_box() @@ -363,7 +448,7 @@ impl NyashInterpreter { self.set_variable(name, assigned)?; Ok(val) } - + ASTNode::FieldAccess { object, field, .. } => { // フィールドへの代入 // 内部(me/this)からの代入かどうか @@ -374,50 +459,69 @@ impl NyashInterpreter { }; let obj_value = self.execute_expression(object)?; - + if let Some(instance) = obj_value.as_any().downcast_ref::() { // 可視性チェック(外部アクセスの場合のみ) if !is_internal { let box_decls = self.shared.box_declarations.read().unwrap(); if let Some(box_decl) = box_decls.get(&instance.class_name) { - let has_visibility = !box_decl.public_fields.is_empty() || !box_decl.private_fields.is_empty(); - if has_visibility && !box_decl.public_fields.contains(&field.to_string()) { + let has_visibility = !box_decl.public_fields.is_empty() + || !box_decl.private_fields.is_empty(); + if has_visibility + && !box_decl.public_fields.contains(&field.to_string()) + { return Err(RuntimeError::InvalidOperation { - message: format!("Field '{}' is private in {}", field, instance.class_name), + message: format!( + "Field '{}' is private in {}", + field, instance.class_name + ), }); } } } // 🔥 finiは何回呼ばれてもエラーにしない(ユーザー要求) // is_finalized()チェックを削除 - + // 🔗 Weak Reference Assignment Check let box_decls = self.shared.box_declarations.read().unwrap(); if let Some(box_decl) = box_decls.get(&instance.class_name) { if box_decl.weak_fields.contains(&field.to_string()) { - debug_trace!("🔗 DEBUG: Assigning to weak field '{}' in class '{}'", field, instance.class_name); - + debug_trace!( + "🔗 DEBUG: Assigning to weak field '{}' in class '{}'", + field, + instance.class_name + ); + // 🎯 PHASE 2: Use the new legacy conversion helper - instance.set_weak_field_from_legacy(field.to_string(), val.clone_box()) + instance + .set_weak_field_from_legacy(field.to_string(), val.clone_box()) .map_err(|e| RuntimeError::InvalidOperation { message: e })?; - + return Ok(val); } } - + // 🚨 フィールド差し替え時の自動finiは削除(Nyashの明示的哲学) // プログラマーが必要なら明示的にfini()を呼ぶべき - + // Store-by-share for plugin handle types; clone for others let stored = { #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] { - if val.as_any().downcast_ref::().is_some() { + if val + .as_any() + .downcast_ref::() + .is_some() + { val.share_box() - } else { val.clone_box() } + } else { + val.clone_box() + } } #[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))] - { val.clone_box() } + { + val.clone_box() + } }; // セル反映: 既存フィールドが RefCellBox なら中身を置換 if let Some(cur) = instance.get_field(field) { @@ -426,7 +530,8 @@ impl NyashInterpreter { return Ok(val); } } - instance.set_field(field, Arc::from(stored)) + instance + .set_field(field, Arc::from(stored)) .map_err(|e| RuntimeError::InvalidOperation { message: e })?; Ok(val) } else { @@ -435,30 +540,39 @@ impl NyashInterpreter { }) } } - + ASTNode::ThisField { field, .. } => { // 🌍 革命的this.field代入:local変数から取得 - let this_value = self.resolve_variable("me") - .map_err(|_| RuntimeError::InvalidOperation { - message: "'this' is not bound in the current context".to_string(), - })?; - + let this_value = + self.resolve_variable("me") + .map_err(|_| RuntimeError::InvalidOperation { + message: "'this' is not bound in the current context".to_string(), + })?; + if let Some(instance) = (*this_value).as_any().downcast_ref::() { // 🔥 finiは何回呼ばれてもエラーにしない(ユーザー要求) // is_finalized()チェックを削除 - + // 🚨 フィールド差し替え時の自動finiは削除(Nyashの明示的哲学) // プログラマーが必要なら明示的にfini()を呼ぶべき - + let stored = { #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] { - if val.as_any().downcast_ref::().is_some() { + if val + .as_any() + .downcast_ref::() + .is_some() + { val.share_box() - } else { val.clone_box() } + } else { + val.clone_box() + } } #[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))] - { val.clone_box() } + { + val.clone_box() + } }; // セル反映: 既存フィールドが RefCellBox なら中身を置換 if let Some(cur) = instance.get_field(field) { @@ -467,7 +581,8 @@ impl NyashInterpreter { return Ok(val); } } - instance.set_field(field, Arc::from(stored)) + instance + .set_field(field, Arc::from(stored)) .map_err(|e| RuntimeError::InvalidOperation { message: e })?; Ok(val) } else { @@ -476,32 +591,42 @@ impl NyashInterpreter { }) } } - + ASTNode::MeField { field, .. } => { // 🌍 革命的me.field代入:local変数から取得 - let me_value = self.resolve_variable("me") - .map_err(|_| RuntimeError::InvalidOperation { - message: "'this' is not bound in the current context".to_string(), - })?; - + let me_value = + self.resolve_variable("me") + .map_err(|_| RuntimeError::InvalidOperation { + message: "'this' is not bound in the current context".to_string(), + })?; + if let Some(instance) = (*me_value).as_any().downcast_ref::() { // 🔥 finiは何回呼ばれてもエラーにしない(ユーザー要求) // is_finalized()チェックを削除 - + // 🚨 フィールド差し替え時の自動finiは削除(Nyashの明示的哲学) // プログラマーが必要なら明示的にfini()を呼ぶべき - + let stored = { #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] { - if val.as_any().downcast_ref::().is_some() { + if val + .as_any() + .downcast_ref::() + .is_some() + { val.share_box() - } else { val.clone_box() } + } else { + val.clone_box() + } } #[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))] - { val.clone_box() } + { + val.clone_box() + } }; - instance.set_field(field, Arc::from(stored)) + instance + .set_field(field, Arc::from(stored)) .map_err(|e| RuntimeError::InvalidOperation { message: e })?; Ok(val) } else { @@ -510,18 +635,22 @@ impl NyashInterpreter { }) } } - + _ => Err(RuntimeError::InvalidOperation { message: "Invalid assignment target".to_string(), }), } } - + /// try/catch/finally文を実行 - Exception handling - pub(super) fn execute_try_catch(&mut self, try_body: &[ASTNode], catch_clauses: &[super::CatchClause], finally_body: &Option>) - -> Result, RuntimeError> { + pub(super) fn execute_try_catch( + &mut self, + try_body: &[ASTNode], + catch_clauses: &[super::CatchClause], + finally_body: &Option>, + ) -> Result, RuntimeError> { let mut thrown_exception: Option> = None; - + // Try block execution let mut try_result = Ok(Box::new(VoidBox::new())); for statement in try_body { @@ -540,13 +669,14 @@ impl NyashInterpreter { } Err(e) => { // RuntimeErrorを例外として扱う - thrown_exception = Some(Box::new(exception_box::ErrorBox::new(&format!("{:?}", e)))); + thrown_exception = + Some(Box::new(exception_box::ErrorBox::new(&format!("{:?}", e)))); try_result = Err(e); break; } } } - + // Catch clause processing if let Some(exception) = &thrown_exception { for catch_clause in catch_clauses { @@ -556,12 +686,12 @@ impl NyashInterpreter { continue; // 型が合わない場合は次のcatch句へ } } - + // 🌍 革命的例外変数束縛:local変数として設定 if let Some(var_name) = &catch_clause.variable_name { self.declare_local_variable(var_name, exception.clone_box()); } - + // Catch body execution for statement in &catch_clause.body { self.execute_statement(statement)?; @@ -569,17 +699,17 @@ impl NyashInterpreter { break; } } - + // 🌍 革命的例外変数クリーンアップ:local変数から削除 if let Some(var_name) = &catch_clause.variable_name { self.local_vars.remove(var_name); } - + thrown_exception = None; // 例外が処理された break; } } - + // Finally block execution (always executed) if let Some(ref finally_statements) = finally_body { for statement in finally_statements { @@ -589,38 +719,48 @@ impl NyashInterpreter { } } } - + // 未処理の例外があれば再スロー if let Some(exception) = thrown_exception { self.control_flow = super::ControlFlow::Throw(exception); } - + match try_result { Ok(result) => Ok(result), Err(_) => Ok(Box::new(VoidBox::new()) as Box), } } - + /// throw文を実行 - Throw exception - pub(super) fn execute_throw(&mut self, expression: &ASTNode) -> Result, RuntimeError> { + pub(super) fn execute_throw( + &mut self, + expression: &ASTNode, + ) -> Result, RuntimeError> { let value = self.execute_expression(expression)?; - + // 値を例外として扱う - let exception = if let Some(error_box) = value.as_any().downcast_ref::() { - Box::new(error_box.clone()) as Box - } else { - // 文字列や他の値はErrorBoxに変換 - Box::new(exception_box::ErrorBox::new(&value.to_string_box().value)) - }; - + let exception = + if let Some(error_box) = value.as_any().downcast_ref::() { + Box::new(error_box.clone()) as Box + } else { + // 文字列や他の値はErrorBoxに変換 + Box::new(exception_box::ErrorBox::new(&value.to_string_box().value)) + }; + self.control_flow = super::ControlFlow::Throw(exception); Ok(Box::new(VoidBox::new())) } - + /// using文を実行 - Import namespace - pub(super) fn execute_using_statement(&mut self, namespace_name: &str) -> Result, RuntimeError> { - idebug!("🌟 DEBUG: execute_using_statement called with namespace: {}", namespace_name); - + pub(super) fn execute_using_statement( + &mut self, + namespace_name: &str, + ) -> Result, RuntimeError> { + idebug!( + "🌟 DEBUG: execute_using_statement called with namespace: {}", + namespace_name + ); + // First, handle the builtin stdlib namespace if namespace_name == "nyashstd" { idebug!("🌟 DEBUG: About to call ensure_stdlib_initialized"); @@ -635,14 +775,19 @@ impl NyashInterpreter { } let strict = std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1"); if strict { - return Err(RuntimeError::InvalidOperation { message: format!("Unresolved namespace '{}' (strict)", namespace_name) }); + return Err(RuntimeError::InvalidOperation { + message: format!("Unresolved namespace '{}' (strict)", namespace_name), + }); } if crate::interpreter::utils::debug_on() { - eprintln!("[using] unresolved '{}' (non-strict, continuing)", namespace_name); + eprintln!( + "[using] unresolved '{}' (non-strict, continuing)", + namespace_name + ); } Ok(Box::new(VoidBox::new())) } - + /// 標準ライブラリの初期化を確保 fn ensure_stdlib_initialized(&mut self) -> Result<(), RuntimeError> { if self.stdlib.is_none() { diff --git a/src/interpreter/system_methods.rs b/src/interpreter/system_methods.rs index c05e65d2..33614f6b 100644 --- a/src/interpreter/system_methods.rs +++ b/src/interpreter/system_methods.rs @@ -1,6 +1,6 @@ /*! * System Methods Module - * + * * Extracted from box_methods.rs * Contains system-level Box method implementations: * - TimeBox methods (now, fromTimestamp, parse, sleep, format) @@ -11,18 +11,22 @@ use super::*; use crate::box_trait::StringBox; -use crate::boxes::{TimeBox, DateTimeBox}; +use crate::boxes::{DateTimeBox, TimeBox}; impl NyashInterpreter { /// TimeBoxのメソッド呼び出しを実行 - pub(super) fn execute_time_method(&mut self, time_box: &TimeBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_time_method( + &mut self, + time_box: &TimeBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドを実行 match method { "now" => { @@ -36,7 +40,10 @@ impl NyashInterpreter { "fromTimestamp" => { if arg_values.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("fromTimestamp() expects 1 argument, got {}", arg_values.len()), + message: format!( + "fromTimestamp() expects 1 argument, got {}", + arg_values.len() + ), }); } Ok(time_box.fromTimestamp(arg_values[0].clone_box())) @@ -65,23 +72,25 @@ impl NyashInterpreter { } Ok(time_box.format(arg_values[0].clone_box())) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown TimeBox method: {}", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown TimeBox method: {}", method), + }), } } /// DateTimeBoxのメソッド呼び出しを実行 - pub(super) fn execute_datetime_method(&mut self, datetime_box: &DateTimeBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_datetime_method( + &mut self, + datetime_box: &DateTimeBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドを実行 match method { "year" => { @@ -135,7 +144,10 @@ impl NyashInterpreter { "timestamp" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("timestamp() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "timestamp() expects 0 arguments, got {}", + arg_values.len() + ), }); } Ok(datetime_box.timestamp()) @@ -143,7 +155,10 @@ impl NyashInterpreter { "toISOString" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("toISOString() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "toISOString() expects 0 arguments, got {}", + arg_values.len() + ), }); } Ok(datetime_box.toISOString()) @@ -175,28 +190,33 @@ impl NyashInterpreter { "toString" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("toString() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "toString() expects 0 arguments, got {}", + arg_values.len() + ), }); } Ok(Box::new(datetime_box.to_string_box())) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown DateTimeBox method: {}", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown DateTimeBox method: {}", method), + }), } } /// TimerBoxのメソッド呼び出しを実行 - pub(super) fn execute_timer_method(&mut self, timer_box: &TimerBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_timer_method( + &mut self, + timer_box: &TimerBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドを実行 match method { "elapsed" => { @@ -218,29 +238,34 @@ impl NyashInterpreter { // 🌍 革命的実装:Environment tracking廃止 Ok(timer_box) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown TimerBox method: {}", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown TimerBox method: {}", method), + }), } } /// DebugBoxのメソッド呼び出しを実行 - pub(super) fn execute_debug_method(&mut self, debug_box: &DebugBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_debug_method( + &mut self, + debug_box: &DebugBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドを実行 match method { "startTracking" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("startTracking() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "startTracking() expects 0 arguments, got {}", + arg_values.len() + ), }); } debug_box.start_tracking() @@ -248,7 +273,10 @@ impl NyashInterpreter { "stopTracking" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("stopTracking() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "stopTracking() expects 0 arguments, got {}", + arg_values.len() + ), }); } debug_box.stop_tracking() @@ -256,11 +284,15 @@ impl NyashInterpreter { "trackBox" => { if arg_values.len() != 2 { return Err(RuntimeError::InvalidOperation { - message: format!("trackBox() expects 2 arguments, got {}", arg_values.len()), + message: format!( + "trackBox() expects 2 arguments, got {}", + arg_values.len() + ), }); } // 第2引数をStringBoxとして取得 - let name = if let Some(str_box) = arg_values[1].as_any().downcast_ref::() { + let name = if let Some(str_box) = arg_values[1].as_any().downcast_ref::() + { str_box.value.clone() } else { return Err(RuntimeError::InvalidOperation { @@ -280,16 +312,20 @@ impl NyashInterpreter { "saveToFile" => { if arg_values.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("saveToFile() expects 1 argument, got {}", arg_values.len()), + message: format!( + "saveToFile() expects 1 argument, got {}", + arg_values.len() + ), }); } - let filename = if let Some(str_box) = arg_values[0].as_any().downcast_ref::() { - str_box.value.clone() - } else { - return Err(RuntimeError::InvalidOperation { - message: "saveToFile() argument must be a string".to_string(), - }); - }; + let filename = + if let Some(str_box) = arg_values[0].as_any().downcast_ref::() { + str_box.value.clone() + } else { + return Err(RuntimeError::InvalidOperation { + message: "saveToFile() argument must be a string".to_string(), + }); + }; debug_box.save_to_file(&filename) } "watch" => { @@ -298,7 +334,8 @@ impl NyashInterpreter { message: format!("watch() expects 2 arguments, got {}", arg_values.len()), }); } - let name = if let Some(str_box) = arg_values[1].as_any().downcast_ref::() { + let name = if let Some(str_box) = arg_values[1].as_any().downcast_ref::() + { str_box.value.clone() } else { return Err(RuntimeError::InvalidOperation { @@ -310,7 +347,10 @@ impl NyashInterpreter { "memoryReport" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("memoryReport() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "memoryReport() expects 0 arguments, got {}", + arg_values.len() + ), }); } debug_box.memory_report() @@ -318,33 +358,42 @@ impl NyashInterpreter { "setBreakpoint" => { if arg_values.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("setBreakpoint() expects 1 argument, got {}", arg_values.len()), + message: format!( + "setBreakpoint() expects 1 argument, got {}", + arg_values.len() + ), }); } - let func_name = if let Some(str_box) = arg_values[0].as_any().downcast_ref::() { - str_box.value.clone() - } else { - return Err(RuntimeError::InvalidOperation { - message: "setBreakpoint() argument must be a string".to_string(), - }); - }; + let func_name = + if let Some(str_box) = arg_values[0].as_any().downcast_ref::() { + str_box.value.clone() + } else { + return Err(RuntimeError::InvalidOperation { + message: "setBreakpoint() argument must be a string".to_string(), + }); + }; debug_box.set_breakpoint(&func_name) } "traceCall" => { if arg_values.len() < 1 { return Err(RuntimeError::InvalidOperation { - message: format!("traceCall() expects at least 1 argument, got {}", arg_values.len()), + message: format!( + "traceCall() expects at least 1 argument, got {}", + arg_values.len() + ), }); } - let func_name = if let Some(str_box) = arg_values[0].as_any().downcast_ref::() { - str_box.value.clone() - } else { - return Err(RuntimeError::InvalidOperation { - message: "traceCall() first argument must be a string".to_string(), - }); - }; + let func_name = + if let Some(str_box) = arg_values[0].as_any().downcast_ref::() { + str_box.value.clone() + } else { + return Err(RuntimeError::InvalidOperation { + message: "traceCall() first argument must be a string".to_string(), + }); + }; // 残りの引数を文字列として収集 - let args: Vec = arg_values[1..].iter() + let args: Vec = arg_values[1..] + .iter() .map(|v| v.to_string_box().value) .collect(); debug_box.trace_call(&func_name, args) @@ -352,7 +401,10 @@ impl NyashInterpreter { "showCallStack" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("showCallStack() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "showCallStack() expects 0 arguments, got {}", + arg_values.len() + ), }); } debug_box.show_call_stack() @@ -360,16 +412,29 @@ impl NyashInterpreter { "tracePluginCalls" => { if arg_values.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("tracePluginCalls(on:bool) expects 1 argument, got {}", arg_values.len()), + message: format!( + "tracePluginCalls(on:bool) expects 1 argument, got {}", + arg_values.len() + ), }); } - let on = if let Some(b) = arg_values[0].as_any().downcast_ref::() { b.value } else { false }; + let on = if let Some(b) = arg_values[0] + .as_any() + .downcast_ref::() + { + b.value + } else { + false + }; debug_box.trace_plugin_calls(on) } "getJitEvents" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("getJitEvents() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "getJitEvents() expects 0 arguments, got {}", + arg_values.len() + ), }); } debug_box.get_jit_events() @@ -385,7 +450,10 @@ impl NyashInterpreter { "isTracking" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("isTracking() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "isTracking() expects 0 arguments, got {}", + arg_values.len() + ), }); } debug_box.is_tracking() @@ -393,16 +461,17 @@ impl NyashInterpreter { "getTrackedCount" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("getTrackedCount() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "getTrackedCount() expects 0 arguments, got {}", + arg_values.len() + ), }); } debug_box.get_tracked_count() } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown DebugBox method: {}", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown DebugBox method: {}", method), + }), } } } diff --git a/src/interpreter/utils.rs b/src/interpreter/utils.rs index e0af4364..d0ee43a3 100644 --- a/src/interpreter/utils.rs +++ b/src/interpreter/utils.rs @@ -4,4 +4,3 @@ pub fn debug_on() -> bool { std::env::var("NYASH_DEBUG").unwrap_or_default() == "1" } - diff --git a/src/interpreter/web_methods.rs b/src/interpreter/web_methods.rs index 964a4529..d531367b 100644 --- a/src/interpreter/web_methods.rs +++ b/src/interpreter/web_methods.rs @@ -1,20 +1,20 @@ /*! * Web Box Methods Module - * + * * Extracted from box_methods.rs * Contains WASM/browser-specific Box type method implementations: - * + * * - execute_web_display_method (WebDisplayBox) - HTML DOM manipulation * - execute_web_console_method (WebConsoleBox) - Browser console logging * - execute_web_canvas_method (WebCanvasBox) - Canvas drawing operations - * + * * All methods are conditionally compiled for WASM target architecture only. */ #[cfg(target_arch = "wasm32")] use super::*; #[cfg(target_arch = "wasm32")] -use crate::boxes::web::{WebDisplayBox, WebConsoleBox, WebCanvasBox}; +use crate::boxes::web::{WebCanvasBox, WebConsoleBox, WebDisplayBox}; #[cfg(target_arch = "wasm32")] use crate::boxes::FloatBox; @@ -22,14 +22,18 @@ use crate::boxes::FloatBox; impl NyashInterpreter { /// WebDisplayBoxメソッド実行 (WASM環境のみ) /// HTML DOM操作、CSS スタイル設定、クラス管理などの包括的なWeb表示機能 - pub(super) fn execute_web_display_method(&mut self, web_display_box: &WebDisplayBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_web_display_method( + &mut self, + web_display_box: &WebDisplayBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドを実行 match method { "print" => { @@ -65,7 +69,10 @@ impl NyashInterpreter { "appendHTML" => { if arg_values.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("appendHTML() expects 1 argument, got {}", arg_values.len()), + message: format!( + "appendHTML() expects 1 argument, got {}", + arg_values.len() + ), }); } let html_content = arg_values[0].to_string_box().value; @@ -75,7 +82,10 @@ impl NyashInterpreter { "setCSS" => { if arg_values.len() != 2 { return Err(RuntimeError::InvalidOperation { - message: format!("setCSS() expects 2 arguments (property, value), got {}", arg_values.len()), + message: format!( + "setCSS() expects 2 arguments (property, value), got {}", + arg_values.len() + ), }); } let property = arg_values[0].to_string_box().value; @@ -96,7 +106,10 @@ impl NyashInterpreter { "removeClass" => { if arg_values.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: format!("removeClass() expects 1 argument, got {}", arg_values.len()), + message: format!( + "removeClass() expects 1 argument, got {}", + arg_values.len() + ), }); } let class_name = arg_values[0].to_string_box().value; @@ -133,30 +146,35 @@ impl NyashInterpreter { "scrollToBottom" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("scrollToBottom() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "scrollToBottom() expects 0 arguments, got {}", + arg_values.len() + ), }); } web_display_box.scroll_to_bottom(); Ok(Box::new(VoidBox::new())) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown method '{}' for WebDisplayBox", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for WebDisplayBox", method), + }), } } - + /// WebConsoleBoxメソッド実行 (WASM環境のみ) /// ブラウザーコンソールへの多彩なログ出力、グループ化、区切り表示機能 - pub(super) fn execute_web_console_method(&mut self, web_console_box: &WebConsoleBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_web_console_method( + &mut self, + web_console_box: &WebConsoleBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドを実行 match method { "log" => { @@ -221,7 +239,10 @@ impl NyashInterpreter { "separator" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("separator() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "separator() expects 0 arguments, got {}", + arg_values.len() + ), }); } web_console_box.separator(); @@ -240,30 +261,35 @@ impl NyashInterpreter { "groupEnd" => { if !arg_values.is_empty() { return Err(RuntimeError::InvalidOperation { - message: format!("groupEnd() expects 0 arguments, got {}", arg_values.len()), + message: format!( + "groupEnd() expects 0 arguments, got {}", + arg_values.len() + ), }); } web_console_box.group_end(); Ok(Box::new(VoidBox::new())) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown method '{}' for WebConsoleBox", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for WebConsoleBox", method), + }), } } - + /// WebCanvasBoxメソッド実行 (WASM環境のみ) /// HTML5 Canvas描画操作 - 矩形、円、テキスト描画の包括的な2D描画機能 - pub(super) fn execute_web_canvas_method(&mut self, web_canvas_box: &WebCanvasBox, method: &str, arguments: &[ASTNode]) - -> Result, RuntimeError> { + pub(super) fn execute_web_canvas_method( + &mut self, + web_canvas_box: &WebCanvasBox, + method: &str, + arguments: &[ASTNode], + ) -> Result, RuntimeError> { // 引数を評価 let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.execute_expression(arg)?); } - + // メソッドを実行 match method { "clear" => { @@ -278,7 +304,10 @@ impl NyashInterpreter { "fillRect" => { if arg_values.len() != 5 { return Err(RuntimeError::InvalidOperation { - message: format!("fillRect() expects 5 arguments (x, y, width, height, color), got {}", arg_values.len()), + message: format!( + "fillRect() expects 5 arguments (x, y, width, height, color), got {}", + arg_values.len() + ), }); } let x = if let Some(n) = arg_values[0].as_any().downcast_ref::() { @@ -364,22 +393,26 @@ impl NyashInterpreter { }); }; let color = arg_values[4].to_string_box().value; - let line_width = if let Some(n) = arg_values[5].as_any().downcast_ref::() { - n.value as f64 - } else if let Some(n) = arg_values[5].as_any().downcast_ref::() { - n.value - } else { - return Err(RuntimeError::TypeError { - message: "strokeRect() lineWidth must be a number".to_string(), - }); - }; + let line_width = + if let Some(n) = arg_values[5].as_any().downcast_ref::() { + n.value as f64 + } else if let Some(n) = arg_values[5].as_any().downcast_ref::() { + n.value + } else { + return Err(RuntimeError::TypeError { + message: "strokeRect() lineWidth must be a number".to_string(), + }); + }; web_canvas_box.stroke_rect(x, y, width, height, &color, line_width); Ok(Box::new(VoidBox::new())) } "fillCircle" => { if arg_values.len() != 4 { return Err(RuntimeError::InvalidOperation { - message: format!("fillCircle() expects 4 arguments (x, y, radius, color), got {}", arg_values.len()), + message: format!( + "fillCircle() expects 4 arguments (x, y, radius, color), got {}", + arg_values.len() + ), }); } let x = if let Some(n) = arg_values[0].as_any().downcast_ref::() { @@ -416,7 +449,10 @@ impl NyashInterpreter { "fillText" => { if arg_values.len() != 5 { return Err(RuntimeError::InvalidOperation { - message: format!("fillText() expects 5 arguments (text, x, y, font, color), got {}", arg_values.len()), + message: format!( + "fillText() expects 5 arguments (text, x, y, font, color), got {}", + arg_values.len() + ), }); } let text = arg_values[0].to_string_box().value; @@ -443,11 +479,9 @@ impl NyashInterpreter { web_canvas_box.fill_text(&text, x, y, &font, &color); Ok(Box::new(VoidBox::new())) } - _ => { - Err(RuntimeError::InvalidOperation { - message: format!("Unknown method '{}' for WebCanvasBox", method), - }) - } + _ => Err(RuntimeError::InvalidOperation { + message: format!("Unknown method '{}' for WebCanvasBox", method), + }), } } -} \ No newline at end of file +} diff --git a/src/jit/abi.rs b/src/jit/abi.rs index a400d9b2..e15c4a25 100644 --- a/src/jit/abi.rs +++ b/src/jit/abi.rs @@ -10,7 +10,13 @@ pub enum JitValue { } impl JitValue { - pub fn as_i64(&self) -> Option { if let JitValue::I64(v) = self { Some(*v) } else { None } } + pub fn as_i64(&self) -> Option { + if let JitValue::I64(v) = self { + Some(*v) + } else { + None + } + } } /// Adapter between VMValue and JitValue — keeps JIT decoupled from VM internals @@ -19,30 +25,34 @@ pub mod adapter { use crate::backend::vm::VMValue; pub fn to_jit_values(args: &[VMValue]) -> Vec { - args.iter().map(|v| match v { - VMValue::Integer(i) => JitValue::I64(*i), - VMValue::Float(f) => JitValue::F64(*f), - VMValue::Bool(b) => JitValue::Bool(*b), - VMValue::BoxRef(arc) => { - let h = crate::jit::rt::handles::to_handle(arc.clone()); - JitValue::Handle(h) - } - // For now, map others to handle via boxing where reasonable - VMValue::String(s) => { - let bx = Box::new(crate::box_trait::StringBox::new(s)); - let bx_dyn: Box = bx; - let arc: std::sync::Arc = std::sync::Arc::from(bx_dyn); - let h = crate::jit::rt::handles::to_handle(arc); - JitValue::Handle(h) - } - VMValue::Void => JitValue::Handle(0), - VMValue::Future(f) => { - let bx_dyn: Box = Box::new(f.clone()); - let arc: std::sync::Arc = std::sync::Arc::from(bx_dyn); - let h = crate::jit::rt::handles::to_handle(arc); - JitValue::Handle(h) - } - }).collect() + args.iter() + .map(|v| match v { + VMValue::Integer(i) => JitValue::I64(*i), + VMValue::Float(f) => JitValue::F64(*f), + VMValue::Bool(b) => JitValue::Bool(*b), + VMValue::BoxRef(arc) => { + let h = crate::jit::rt::handles::to_handle(arc.clone()); + JitValue::Handle(h) + } + // For now, map others to handle via boxing where reasonable + VMValue::String(s) => { + let bx = Box::new(crate::box_trait::StringBox::new(s)); + let bx_dyn: Box = bx; + let arc: std::sync::Arc = + std::sync::Arc::from(bx_dyn); + let h = crate::jit::rt::handles::to_handle(arc); + JitValue::Handle(h) + } + VMValue::Void => JitValue::Handle(0), + VMValue::Future(f) => { + let bx_dyn: Box = Box::new(f.clone()); + let arc: std::sync::Arc = + std::sync::Arc::from(bx_dyn); + let h = crate::jit::rt::handles::to_handle(arc); + JitValue::Handle(h) + } + }) + .collect() } pub fn from_jit_value(v: JitValue) -> VMValue { diff --git a/src/jit/boundary.rs b/src/jit/boundary.rs index 578cb6f8..7fd21970 100644 --- a/src/jit/boundary.rs +++ b/src/jit/boundary.rs @@ -1,6 +1,6 @@ //! CallBoundaryBox: unify JIT→VM return conversion in one place -use crate::backend::vm::VMValue; use super::abi::JitValue; +use crate::backend::vm::VMValue; pub struct CallBoundaryBox; @@ -10,17 +10,25 @@ impl CallBoundaryBox { crate::mir::MirType::Float => match v { JitValue::F64(f) => VMValue::Float(f), JitValue::I64(i) => VMValue::Float(i as f64), - JitValue::Bool(b) => VMValue::Float(if b {1.0} else {0.0}), + JitValue::Bool(b) => VMValue::Float(if b { 1.0 } else { 0.0 }), JitValue::Handle(h) => { - if let Some(_) = crate::jit::rt::handles::get(h) { VMValue::Float(0.0) } else { VMValue::Float(0.0) } + if let Some(_) = crate::jit::rt::handles::get(h) { + VMValue::Float(0.0) + } else { + VMValue::Float(0.0) + } } }, crate::mir::MirType::Integer => match v { JitValue::I64(i) => VMValue::Integer(i), JitValue::F64(f) => VMValue::Integer(f as i64), - JitValue::Bool(b) => VMValue::Integer(if b {1} else {0}), + JitValue::Bool(b) => VMValue::Integer(if b { 1 } else { 0 }), JitValue::Handle(h) => { - if let Some(_) = crate::jit::rt::handles::get(h) { VMValue::Integer(0) } else { VMValue::Integer(0) } + if let Some(_) = crate::jit::rt::handles::get(h) { + VMValue::Integer(0) + } else { + VMValue::Integer(0) + } } }, crate::mir::MirType::Bool => match v { @@ -28,33 +36,48 @@ impl CallBoundaryBox { JitValue::I64(i) => VMValue::Bool(i != 0), JitValue::F64(f) => VMValue::Bool(f != 0.0), JitValue::Handle(h) => { - if let Some(_) = crate::jit::rt::handles::get(h) { VMValue::Bool(true) } else { VMValue::Bool(false) } + if let Some(_) = crate::jit::rt::handles::get(h) { + VMValue::Bool(true) + } else { + VMValue::Bool(false) + } } }, // Box-like returns: if we received a handle id (encoded as I64), resolve to BoxRef; also honor explicit Handle - crate::mir::MirType::Box(_) | crate::mir::MirType::String | crate::mir::MirType::Array(_) | crate::mir::MirType::Future(_) => { - match v { - JitValue::I64(i) => { - let h = i as u64; - if let Some(arc) = crate::jit::rt::handles::get(h) { VMValue::BoxRef(arc) } - else { VMValue::Integer(i) } + crate::mir::MirType::Box(_) + | crate::mir::MirType::String + | crate::mir::MirType::Array(_) + | crate::mir::MirType::Future(_) => match v { + JitValue::I64(i) => { + let h = i as u64; + if let Some(arc) = crate::jit::rt::handles::get(h) { + VMValue::BoxRef(arc) + } else { + VMValue::Integer(i) } - JitValue::Handle(h) => { - if let Some(arc) = crate::jit::rt::handles::get(h) { VMValue::BoxRef(arc) } else { VMValue::Void } - } - JitValue::F64(f) => VMValue::Float(f), - JitValue::Bool(b) => VMValue::Bool(b), } - } + JitValue::Handle(h) => { + if let Some(arc) = crate::jit::rt::handles::get(h) { + VMValue::BoxRef(arc) + } else { + VMValue::Void + } + } + JitValue::F64(f) => VMValue::Float(f), + JitValue::Bool(b) => VMValue::Bool(b), + }, _ => { // Default adapter with heuristic: treat I64 matching a known handle as BoxRef match v { JitValue::I64(i) => { let h = i as u64; - if let Some(arc) = crate::jit::rt::handles::get(h) { VMValue::BoxRef(arc) } - else { super::abi::adapter::from_jit_value(JitValue::I64(i)) } + if let Some(arc) = crate::jit::rt::handles::get(h) { + VMValue::BoxRef(arc) + } else { + super::abi::adapter::from_jit_value(JitValue::I64(i)) + } } - _ => super::abi::adapter::from_jit_value(v) + _ => super::abi::adapter::from_jit_value(v), } } } diff --git a/src/jit/config.rs b/src/jit/config.rs index 09833303..78415406 100644 --- a/src/jit/config.rs +++ b/src/jit/config.rs @@ -5,29 +5,34 @@ #[derive(Debug, Clone, Default)] pub struct JitConfig { - pub exec: bool, // NYASH_JIT_EXEC - pub stats: bool, // NYASH_JIT_STATS - pub stats_json: bool, // NYASH_JIT_STATS_JSON - pub dump: bool, // NYASH_JIT_DUMP - pub threshold: Option,// NYASH_JIT_THRESHOLD - pub phi_min: bool, // NYASH_JIT_PHI_MIN - pub hostcall: bool, // NYASH_JIT_HOSTCALL - pub handle_debug: bool, // NYASH_JIT_HANDLE_DEBUG - pub native_f64: bool, // NYASH_JIT_NATIVE_F64 - pub native_bool: bool, // NYASH_JIT_NATIVE_BOOL - pub native_bool_abi: bool, // NYASH_JIT_ABI_B1 (experimental) - pub ret_bool_b1: bool, // NYASH_JIT_RET_B1 (footing; currently returns i64 0/1) - pub relax_numeric: bool, // NYASH_JIT_HOSTCALL_RELAX_NUMERIC (i64->f64 coercion) + pub exec: bool, // NYASH_JIT_EXEC + pub stats: bool, // NYASH_JIT_STATS + pub stats_json: bool, // NYASH_JIT_STATS_JSON + pub dump: bool, // NYASH_JIT_DUMP + pub threshold: Option, // NYASH_JIT_THRESHOLD + pub phi_min: bool, // NYASH_JIT_PHI_MIN + pub hostcall: bool, // NYASH_JIT_HOSTCALL + pub handle_debug: bool, // NYASH_JIT_HANDLE_DEBUG + pub native_f64: bool, // NYASH_JIT_NATIVE_F64 + pub native_bool: bool, // NYASH_JIT_NATIVE_BOOL + pub native_bool_abi: bool, // NYASH_JIT_ABI_B1 (experimental) + pub ret_bool_b1: bool, // NYASH_JIT_RET_B1 (footing; currently returns i64 0/1) + pub relax_numeric: bool, // NYASH_JIT_HOSTCALL_RELAX_NUMERIC (i64->f64 coercion) } impl JitConfig { pub fn from_env() -> Self { let getb = |k: &str| std::env::var(k).ok().as_deref() == Some("1"); - let threshold = std::env::var("NYASH_JIT_THRESHOLD").ok().and_then(|s| s.parse::().ok()); + let threshold = std::env::var("NYASH_JIT_THRESHOLD") + .ok() + .and_then(|s| s.parse::().ok()); // Respect explicit dump flag, but also treat a non-empty NYASH_JIT_DOT path // as an implicit request to enable dump (so Box/CLI/env stay consistent). let dump_flag = getb("NYASH_JIT_DUMP") - || std::env::var("NYASH_JIT_DOT").ok().map(|s| !s.is_empty()).unwrap_or(false); + || std::env::var("NYASH_JIT_DOT") + .ok() + .map(|s| !s.is_empty()) + .unwrap_or(false); Self { exec: getb("NYASH_JIT_EXEC"), stats: getb("NYASH_JIT_STATS"), @@ -48,12 +53,18 @@ impl JitConfig { /// Apply current struct values into environment variables. /// This keeps existing env untouched unless the value is explicitly set here. pub fn apply_env(&self) { - let setb = |k: &str, v: bool| { if v { std::env::set_var(k, "1"); } }; + let setb = |k: &str, v: bool| { + if v { + std::env::set_var(k, "1"); + } + }; setb("NYASH_JIT_EXEC", self.exec); setb("NYASH_JIT_STATS", self.stats); setb("NYASH_JIT_STATS_JSON", self.stats_json); setb("NYASH_JIT_DUMP", self.dump); - if let Some(t) = self.threshold { std::env::set_var("NYASH_JIT_THRESHOLD", t.to_string()); } + if let Some(t) = self.threshold { + std::env::set_var("NYASH_JIT_THRESHOLD", t.to_string()); + } setb("NYASH_JIT_PHI_MIN", self.phi_min); setb("NYASH_JIT_HOSTCALL", self.hostcall); setb("NYASH_JIT_HANDLE_DEBUG", self.handle_debug); @@ -74,7 +85,9 @@ static GLOBAL_JIT_CONFIG: OnceCell> = OnceCell::new(); /// Get current JIT config (falls back to env-derived default if unset) pub fn current() -> JitConfig { if let Some(lock) = GLOBAL_JIT_CONFIG.get() { - if let Ok(cfg) = lock.read() { return cfg.clone(); } + if let Ok(cfg) = lock.read() { + return cfg.clone(); + } } JitConfig::from_env() } @@ -82,7 +95,10 @@ pub fn current() -> JitConfig { /// Set current JIT config (overrides env lookups in hot paths) pub fn set_current(cfg: JitConfig) { if let Some(lock) = GLOBAL_JIT_CONFIG.get() { - if let Ok(mut w) = lock.write() { *w = cfg; return; } + if let Ok(mut w) = lock.write() { + *w = cfg; + return; + } } let _ = GLOBAL_JIT_CONFIG.set(RwLock::new(cfg)); } @@ -99,11 +115,15 @@ pub fn probe_capabilities() -> JitCapabilities { // Current toolchain: allow forcing via env for experiments; otherwise false. // When upgrading Cranelift to a version with B1 signature support, set NYASH_JIT_ABI_B1_SUPPORT=1 let forced = std::env::var("NYASH_JIT_ABI_B1_SUPPORT").ok().as_deref() == Some("1"); - JitCapabilities { supports_b1_sig: forced } + JitCapabilities { + supports_b1_sig: forced, + } } /// Apply runtime capabilities onto a JitConfig (e.g., disable b1 ABI when unsupported) pub fn apply_runtime_caps(mut cfg: JitConfig, caps: JitCapabilities) -> JitConfig { - 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; + } cfg } diff --git a/src/jit/engine.rs b/src/jit/engine.rs index 4f4ed224..74a51cf7 100644 --- a/src/jit/engine.rs +++ b/src/jit/engine.rs @@ -12,9 +12,15 @@ pub struct JitEngine { initialized: bool, next_handle: u64, /// Stub function table: handle -> callable closure - fntab: HashMap crate::jit::abi::JitValue + Send + Sync>>, + fntab: HashMap< + u64, + Arc crate::jit::abi::JitValue + Send + Sync>, + >, /// Host externs by symbol name (Phase 10_d) - externs: HashMap crate::backend::vm::VMValue + Send + Sync>>, + externs: HashMap< + String, + Arc crate::backend::vm::VMValue + Send + Sync>, + >, #[cfg(feature = "cranelift-jit")] isa: Option, // Last lower stats (per function) @@ -30,19 +36,26 @@ impl JitEngine { next_handle: 1, fntab: HashMap::new(), externs: HashMap::new(), - #[cfg(feature = "cranelift-jit")] isa: None, + #[cfg(feature = "cranelift-jit")] + isa: None, last_phi_total: 0, last_phi_b1: 0, last_ret_bool_hint: false, }; #[cfg(feature = "cranelift-jit")] - { this.isa = None; } + { + this.isa = None; + } this.register_default_externs(); this } /// Compile a function if supported; returns an opaque handle id - pub fn compile_function(&mut self, func_name: &str, mir: &crate::mir::MirFunction) -> Option { + pub fn compile_function( + &mut self, + func_name: &str, + mir: &crate::mir::MirFunction, + ) -> Option { let t0 = std::time::Instant::now(); // Phase 10_b skeleton: walk MIR with LowerCore and report coverage // Reset compile-phase counters (e.g., fallback decisions) before lowering this function @@ -59,17 +72,25 @@ impl JitEngine { // Strict: fail compile if any fallback decisions were taken during lowering let lower_fallbacks = crate::jit::events::lower_fallbacks_get(); if lower_fallbacks > 0 && std::env::var("NYASH_JIT_STRICT").ok().as_deref() == Some("1") { - eprintln!("[JIT][strict] lower produced fallback decisions for {}: {} — failing compile", func_name, lower_fallbacks); + eprintln!( + "[JIT][strict] lower produced fallback decisions for {}: {} — failing compile", + func_name, lower_fallbacks + ); return None; } // Capture per-function lower stats for manager to query later let (phi_t, phi_b1, ret_b) = lower.last_stats(); - self.last_phi_total = phi_t; self.last_phi_b1 = phi_b1; self.last_ret_bool_hint = ret_b; + self.last_phi_total = phi_t; + self.last_phi_b1 = phi_b1; + self.last_ret_bool_hint = ret_b; // Record per-function stats into manager via callback if available (handled by caller) let cfg_now = crate::jit::config::current(); // Strict mode: any unsupported lowering must fail-fast if lower.unsupported > 0 && std::env::var("NYASH_JIT_STRICT").ok().as_deref() == Some("1") { - eprintln!("[JIT][strict] unsupported lowering ops for {}: {} — failing compile", func_name, lower.unsupported); + eprintln!( + "[JIT][strict] unsupported lowering ops for {}: {} — failing compile", + func_name, lower.unsupported + ); return None; } if cfg_now.dump { @@ -104,9 +125,14 @@ impl JitEngine { } // If lowering left any unsupported instructions, do not register a closure. // This preserves VM semantics until coverage is complete for the function. - if lower.unsupported > 0 && std::env::var("NYASH_AOT_ALLOW_UNSUPPORTED").ok().as_deref() != Some("1") { + if lower.unsupported > 0 + && std::env::var("NYASH_AOT_ALLOW_UNSUPPORTED").ok().as_deref() != Some("1") + { if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") || cfg_now.dump { - eprintln!("[JIT] skip compile for {}: unsupported={} (>0)", func_name, lower.unsupported); + eprintln!( + "[JIT] skip compile for {}: unsupported={} (>0)", + func_name, lower.unsupported + ); } return None; } @@ -131,13 +157,25 @@ impl JitEngine { } else if let Some(bytes) = objb.take_object_bytes() { use std::path::Path; let p = Path::new(&path); - let out_path = if p.is_dir() || path.ends_with('/') { p.join(format!("{}.o", func_name)) } else { p.to_path_buf() }; - if let Some(parent) = out_path.parent() { let _ = std::fs::create_dir_all(parent); } + let out_path = if p.is_dir() || path.ends_with('/') { + p.join(format!("{}.o", func_name)) + } else { + p.to_path_buf() + }; + if let Some(parent) = out_path.parent() { + let _ = std::fs::create_dir_all(parent); + } match std::fs::write(&out_path, bytes) { Ok(_) => { eprintln!("[AOT] wrote object: {}", out_path.display()); } - Err(e) => { eprintln!("[AOT] failed to write object {}: {}", out_path.display(), e); } + Err(e) => { + eprintln!( + "[AOT] failed to write object {}: {}", + out_path.display(), + e + ); + } } } } @@ -156,11 +194,23 @@ impl JitEngine { if let Some(bytes) = objb.take_object_bytes() { use std::path::Path; let p = Path::new(&path); - let out_path = if p.is_dir() || path.ends_with('/') { p.join(format!("{}.o", func_name)) } else { p.to_path_buf() }; - if let Some(parent) = out_path.parent() { let _ = std::fs::create_dir_all(parent); } + let out_path = if p.is_dir() || path.ends_with('/') { + p.join(format!("{}.o", func_name)) + } else { + p.to_path_buf() + }; + if let Some(parent) = out_path.parent() { + let _ = std::fs::create_dir_all(parent); + } match std::fs::write(&out_path, bytes) { - Ok(_) => eprintln!("[AOT] wrote object: {}", out_path.display()), - Err(e) => eprintln!("[AOT] failed to write object {}: {}", out_path.display(), e), + Ok(_) => { + eprintln!("[AOT] wrote object: {}", out_path.display()) + } + Err(e) => eprintln!( + "[AOT] failed to write object {}: {}", + out_path.display(), + e + ), } } else { eprintln!("[AOT] no object bytes available for {}", func_name); @@ -177,26 +227,47 @@ impl JitEngine { // Report as not compiled so VM path remains authoritative. if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") { let dt = t0.elapsed(); - eprintln!("[JIT] compile skipped (no cranelift) for {} after {}ms", func_name, dt.as_millis()); + eprintln!( + "[JIT] compile skipped (no cranelift) for {} after {}ms", + func_name, + dt.as_millis() + ); } return None; } } /// Get statistics from the last lowered function - pub fn last_lower_stats(&self) -> (u64, u64, bool) { (self.last_phi_total, self.last_phi_b1, self.last_ret_bool_hint) } + pub fn last_lower_stats(&self) -> (u64, u64, bool) { + ( + self.last_phi_total, + self.last_phi_b1, + self.last_ret_bool_hint, + ) + } /// Execute compiled function by handle with trap fallback. /// Returns Some(VMValue) if executed successfully; None on missing handle or trap (panic). - pub fn execute_handle(&self, handle: u64, args: &[crate::jit::abi::JitValue]) -> Option { - let f = match self.fntab.get(&handle) { Some(f) => f, None => return None }; + pub fn execute_handle( + &self, + handle: u64, + args: &[crate::jit::abi::JitValue], + ) -> Option { + let f = match self.fntab.get(&handle) { + Some(f) => f, + None => return None, + }; let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| (f)(args))); match res { Ok(v) => Some(v), Err(_) => { - if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") || - std::env::var("NYASH_JIT_TRAP_LOG").ok().as_deref() == Some("1") { - eprintln!("[JIT] trap: panic during handle={} execution — falling back to VM", handle); + if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") + || std::env::var("NYASH_JIT_TRAP_LOG").ok().as_deref() == Some("1") + { + eprintln!( + "[JIT] trap: panic during handle={} execution — falling back to VM", + handle + ); } None } @@ -223,21 +294,48 @@ impl JitEngine { self.register_extern(hb::SYM_HOST_MAP_GET, Arc::new(|args| hb::map_get(args))); self.register_extern(hb::SYM_HOST_MAP_SET, Arc::new(|args| hb::map_set(args))); self.register_extern(hb::SYM_HOST_MAP_HAS, Arc::new(|args| hb::map_has(args))); - self.register_extern(hb::SYM_HOST_CONSOLE_LOG, Arc::new(|args| hb::console_log(args))); - self.register_extern(hb::SYM_HOST_CONSOLE_WARN, Arc::new(|args| hb::console_warn(args))); - self.register_extern(hb::SYM_HOST_CONSOLE_ERROR, Arc::new(|args| hb::console_error(args))); - self.register_extern(hb::SYM_HOST_INSTANCE_GETFIELD, Arc::new(|args| hb::instance_getfield(args))); - self.register_extern(hb::SYM_HOST_INSTANCE_SETFIELD, Arc::new(|args| hb::instance_setfield(args))); - self.register_extern(hb::SYM_HOST_STRING_LEN, Arc::new(|args| hb::string_len(args))); + self.register_extern( + hb::SYM_HOST_CONSOLE_LOG, + Arc::new(|args| hb::console_log(args)), + ); + self.register_extern( + hb::SYM_HOST_CONSOLE_WARN, + Arc::new(|args| hb::console_warn(args)), + ); + self.register_extern( + hb::SYM_HOST_CONSOLE_ERROR, + Arc::new(|args| hb::console_error(args)), + ); + self.register_extern( + hb::SYM_HOST_INSTANCE_GETFIELD, + Arc::new(|args| hb::instance_getfield(args)), + ); + self.register_extern( + hb::SYM_HOST_INSTANCE_SETFIELD, + Arc::new(|args| hb::instance_setfield(args)), + ); + self.register_extern( + hb::SYM_HOST_STRING_LEN, + Arc::new(|args| hb::string_len(args)), + ); } } - pub fn register_extern(&mut self, name: &str, f: Arc crate::backend::vm::VMValue + Send + Sync>) { + pub fn register_extern( + &mut self, + name: &str, + f: Arc crate::backend::vm::VMValue + Send + Sync>, + ) { self.externs.insert(name.to_string(), f); } /// Lookup an extern symbol (to be used by the lowering once call emission is added) - pub fn lookup_extern(&self, name: &str) -> Option crate::backend::vm::VMValue + Send + Sync>> { + pub fn lookup_extern( + &self, + name: &str, + ) -> Option< + Arc crate::backend::vm::VMValue + Send + Sync>, + > { self.externs.get(name).cloned() } } diff --git a/src/jit/events.rs b/src/jit/events.rs index c26c2e76..656bf4d5 100644 --- a/src/jit/events.rs +++ b/src/jit/events.rs @@ -47,10 +47,14 @@ fn should_emit_runtime() -> bool { fn write_line(s: &str) { if let Ok(path) = std::env::var("NYASH_JIT_EVENTS_PATH") { - let _ = std::fs::OpenOptions::new().create(true).append(true).open(path).and_then(|mut f| { - use std::io::Write; - writeln!(f, "{}", s) - }); + let _ = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open(path) + .and_then(|mut f| { + use std::io::Write; + writeln!(f, "{}", s) + }); } else { println!("{}", s); } @@ -68,29 +72,67 @@ struct Event<'a, T: Serialize> { extra: T, } -pub fn emit(kind: &str, function: &str, handle: Option, ms: Option, extra: T) { - if !base_emit_enabled() { return; } - let ev = Event { kind, function, handle, ms, extra }; - if let Ok(s) = serde_json::to_string(&ev) { write_line(&s); } +pub fn emit( + kind: &str, + function: &str, + handle: Option, + ms: Option, + extra: T, +) { + if !base_emit_enabled() { + return; + } + let ev = Event { + kind, + function, + handle, + ms, + extra, + }; + if let Ok(s) = serde_json::to_string(&ev) { + write_line(&s); + } } -fn emit_any(kind: &str, function: &str, handle: Option, ms: Option, extra: serde_json::Value) { - let ev = Event { kind, function, handle, ms, extra }; - if let Ok(s) = serde_json::to_string(&ev) { write_line(&s); } +fn emit_any( + kind: &str, + function: &str, + handle: Option, + ms: Option, + extra: serde_json::Value, +) { + let ev = Event { + kind, + function, + handle, + ms, + extra, + }; + if let Ok(s) = serde_json::to_string(&ev) { + write_line(&s); + } } /// Emit an event during lowering (compile-time planning). Adds phase="lower". pub fn emit_lower(mut extra: serde_json::Value, kind: &str, function: &str) { // Always record decisions for strict-mode enforcement record_lower_decision(&extra); - if !should_emit_lower() { return; } - if let serde_json::Value::Object(ref mut map) = extra { map.insert("phase".into(), serde_json::Value::String("lower".into())); } + if !should_emit_lower() { + return; + } + if let serde_json::Value::Object(ref mut map) = extra { + map.insert("phase".into(), serde_json::Value::String("lower".into())); + } emit_any(kind, function, None, None, extra); } /// Emit an event during runtime execution. Adds phase="execute". pub fn emit_runtime(mut extra: serde_json::Value, kind: &str, function: &str) { - if !should_emit_runtime() { return; } - if let serde_json::Value::Object(ref mut map) = extra { map.insert("phase".into(), serde_json::Value::String("execute".into())); } + if !should_emit_runtime() { + return; + } + if let serde_json::Value::Object(ref mut map) = extra { + map.insert("phase".into(), serde_json::Value::String("execute".into())); + } emit_any(kind, function, None, None, extra); } diff --git a/src/jit/extern/async.rs b/src/jit/extern/async.rs index c990da5a..78dca584 100644 --- a/src/jit/extern/async.rs +++ b/src/jit/extern/async.rs @@ -1,7 +1,10 @@ //! Async/Future-related JIT extern symbols #[allow(unused_imports)] -use crate::{backend::vm::VMValue, box_trait::{NyashBox, IntegerBox, BoolBox, StringBox}}; +use crate::{ + backend::vm::VMValue, + box_trait::{BoolBox, IntegerBox, NyashBox, StringBox}, +}; /// Symbol name for awaiting a FutureBox and returning a value/handle (i64) pub const SYM_FUTURE_AWAIT_H: &str = "nyash.future.await_h"; @@ -15,7 +18,10 @@ pub extern "C" fn nyash_future_await_h(arg0: i64) -> i64 { let mut fut_opt: Option = None; if arg0 > 0 { if let Some(obj) = handles::get(arg0 as u64) { - if let Some(fb) = obj.as_any().downcast_ref::() { + if let Some(fb) = obj + .as_any() + .downcast_ref::() + { fut_opt = Some(fb.clone()); } } @@ -23,7 +29,11 @@ pub extern "C" fn nyash_future_await_h(arg0: i64) -> i64 { #[cfg(not(feature = "jit-direct-only"))] if fut_opt.is_none() { crate::jit::rt::with_legacy_vm_args(|args| { - let pick = if arg0 >= 0 { (arg0 as usize)..(arg0 as usize + 1) } else { 0..args.len() }; + let pick = if arg0 >= 0 { + (arg0 as usize)..(arg0 as usize + 1) + } else { + 0..args.len() + }; for i in pick { if let Some(VMValue::BoxRef(b)) = args.get(i) { if let Some(fb) = b.as_any().downcast_ref::() { @@ -34,7 +44,9 @@ pub extern "C" fn nyash_future_await_h(arg0: i64) -> i64 { } }); } - let Some(fut) = fut_opt else { return 0; }; + let Some(fut) = fut_opt else { + return 0; + }; // Cooperative wait with scheduler polling and timeout let max_ms: u64 = crate::config::env::await_max_ms(); let start = std::time::Instant::now(); diff --git a/src/jit/extern/collections.rs b/src/jit/extern/collections.rs index 90e881c4..2116cc52 100644 --- a/src/jit/extern/collections.rs +++ b/src/jit/extern/collections.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use crate::backend::vm::VMValue; -use crate::box_trait::{NyashBox, IntegerBox, StringBox}; +use crate::box_trait::{IntegerBox, NyashBox, StringBox}; /// Symbol names for host externs (stable ABI for JIT) pub const SYM_ARRAY_LEN: &str = "nyash.array.len"; @@ -75,34 +75,47 @@ pub fn array_get(args: &[VMValue]) -> VMValue { pub fn array_set(args: &[VMValue]) -> VMValue { // Enforce policy for mutating operation - if crate::jit::policy::current().read_only && - !crate::jit::policy::current().hostcall_whitelist.iter().any(|s| s == SYM_ARRAY_SET) + if crate::jit::policy::current().read_only + && !crate::jit::policy::current() + .hostcall_whitelist + .iter() + .any(|s| s == SYM_ARRAY_SET) { crate::jit::events::emit_runtime( serde_json::json!({"id": SYM_ARRAY_SET, "decision":"fallback", "reason":"policy_denied_mutating"}), - "hostcall", "" + "hostcall", + "", ); return VMValue::Integer(0); } - if let (Some(arr), Some(VMValue::Integer(idx)), Some(value)) = (as_array(args), args.get(1), args.get(2)) { + if let (Some(arr), Some(VMValue::Integer(idx)), Some(value)) = + (as_array(args), args.get(1), args.get(2)) + { let val_box: Box = value.to_nyash_box(); let res = arr.set(Box::new(IntegerBox::new(*idx)), val_box); crate::jit::events::emit_runtime( serde_json::json!({"id": SYM_ARRAY_SET, "decision":"allow", "argc":3, "arg_types":["Handle","I64","Handle"]}), - "hostcall", "" + "hostcall", + "", ); return VMValue::from_nyash_box(res); } - VMValue::BoxRef(Arc::new(StringBox::new("Error: array.set expects (ArrayBox, i64, value)"))) + VMValue::BoxRef(Arc::new(StringBox::new( + "Error: array.set expects (ArrayBox, i64, value)", + ))) } pub fn array_push(args: &[VMValue]) -> VMValue { - if crate::jit::policy::current().read_only && - !crate::jit::policy::current().hostcall_whitelist.iter().any(|s| s == SYM_ARRAY_PUSH) + if crate::jit::policy::current().read_only + && !crate::jit::policy::current() + .hostcall_whitelist + .iter() + .any(|s| s == SYM_ARRAY_PUSH) { crate::jit::events::emit_runtime( serde_json::json!({"id": SYM_ARRAY_PUSH, "decision":"fallback", "reason":"policy_denied_mutating"}), - "hostcall", "" + "hostcall", + "", ); return VMValue::Integer(0); } @@ -111,11 +124,14 @@ pub fn array_push(args: &[VMValue]) -> VMValue { let res = arr.push(val_box); crate::jit::events::emit_runtime( serde_json::json!({"id": SYM_ARRAY_PUSH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}), - "hostcall", "" + "hostcall", + "", ); return VMValue::from_nyash_box(res); } - VMValue::BoxRef(Arc::new(StringBox::new("Error: array.push expects (ArrayBox, value)"))) + VMValue::BoxRef(Arc::new(StringBox::new( + "Error: array.push expects (ArrayBox, value)", + ))) } pub fn map_get(args: &[VMValue]) -> VMValue { @@ -127,12 +143,16 @@ pub fn map_get(args: &[VMValue]) -> VMValue { } pub fn map_set(args: &[VMValue]) -> VMValue { - if crate::jit::policy::current().read_only && - !crate::jit::policy::current().hostcall_whitelist.iter().any(|s| s == SYM_MAP_SET) + if crate::jit::policy::current().read_only + && !crate::jit::policy::current() + .hostcall_whitelist + .iter() + .any(|s| s == SYM_MAP_SET) { crate::jit::events::emit_runtime( serde_json::json!({"id": SYM_MAP_SET, "decision":"fallback", "reason":"policy_denied_mutating"}), - "hostcall", "" + "hostcall", + "", ); return VMValue::Integer(0); } @@ -142,11 +162,14 @@ pub fn map_set(args: &[VMValue]) -> VMValue { let out = map.set(key_box, val_box); crate::jit::events::emit_runtime( serde_json::json!({"id": SYM_MAP_SET, "decision":"allow", "argc":3, "arg_types":["Handle","Handle","Handle"]}), - "hostcall", "" + "hostcall", + "", ); return VMValue::from_nyash_box(out); } - VMValue::BoxRef(Arc::new(StringBox::new("Error: map.set expects (MapBox, key, value)"))) + VMValue::BoxRef(Arc::new(StringBox::new( + "Error: map.set expects (MapBox, key, value)", + ))) } pub fn map_size(args: &[VMValue]) -> VMValue { diff --git a/src/jit/extern/handles.rs b/src/jit/extern/handles.rs index c3b9413a..a20bbb7a 100644 --- a/src/jit/extern/handles.rs +++ b/src/jit/extern/handles.rs @@ -1,4 +1,3 @@ //! Handle-related extern symbol names pub const SYM_HANDLE_OF: &str = "nyash.handle.of"; - diff --git a/src/jit/extern/host_bridge.rs b/src/jit/extern/host_bridge.rs index fc23957d..5b2bb10e 100644 --- a/src/jit/extern/host_bridge.rs +++ b/src/jit/extern/host_bridge.rs @@ -17,11 +17,15 @@ fn tlv_encode_values(args: &[VMValue]) -> Vec { // Try to downcast common primitives for stable TLV if let Some(sb) = arc.as_any().downcast_ref::() { enc::string(&mut buf, &sb.value); - } else if let Some(ib) = arc.as_any().downcast_ref::() { + } else if let Some(ib) = arc.as_any().downcast_ref::() + { enc::i64(&mut buf, ib.value); } else if let Some(bb) = arc.as_any().downcast_ref::() { enc::bool(&mut buf, bb.value); - } else if let Some(fb) = arc.as_any().downcast_ref::() { + } else if let Some(fb) = arc + .as_any() + .downcast_ref::() + { enc::f64(&mut buf, fb.value); } else { // Fallback: send HostHandle so host can operate on it if needed @@ -45,29 +49,54 @@ fn call_slot(handle: u64, slot: u64, argv: &[VMValue]) -> VMValue { let tlv = tlv_encode_values(argv); let mut out = vec![0u8; 256]; let mut out_len: usize = out.len(); - let code = unsafe { crate::runtime::host_api::nyrt_host_call_slot(handle, slot, tlv.as_ptr(), tlv.len(), out.as_mut_ptr(), &mut out_len) }; - if code != 0 { return VMValue::Void; } - if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) { + let code = unsafe { + crate::runtime::host_api::nyrt_host_call_slot( + handle, + slot, + tlv.as_ptr(), + tlv.len(), + out.as_mut_ptr(), + &mut out_len, + ) + }; + if code != 0 { + return VMValue::Void; + } + if let Some((tag, _sz, payload)) = + crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) + { match tag { - 6|7 => { + 6 | 7 => { let s = crate::runtime::plugin_ffi_common::decode::string(payload); let sb = crate::box_trait::StringBox::new(&s); let arc: std::sync::Arc = std::sync::Arc::new(sb); VMValue::BoxRef(arc) } - 1 => crate::runtime::plugin_ffi_common::decode::bool(payload).map(VMValue::Bool).unwrap_or(VMValue::Void), - 2 => crate::runtime::plugin_ffi_common::decode::i32(payload).map(|v| VMValue::Integer(v as i64)).unwrap_or(VMValue::Void), - 3 => crate::runtime::plugin_ffi_common::decode::u64(payload).map(|v| VMValue::Integer(v as i64)).unwrap_or(VMValue::Void), - 5 => crate::runtime::plugin_ffi_common::decode::f64(payload).map(VMValue::Float).unwrap_or(VMValue::Void), + 1 => crate::runtime::plugin_ffi_common::decode::bool(payload) + .map(VMValue::Bool) + .unwrap_or(VMValue::Void), + 2 => crate::runtime::plugin_ffi_common::decode::i32(payload) + .map(|v| VMValue::Integer(v as i64)) + .unwrap_or(VMValue::Void), + 3 => crate::runtime::plugin_ffi_common::decode::u64(payload) + .map(|v| VMValue::Integer(v as i64)) + .unwrap_or(VMValue::Void), + 5 => crate::runtime::plugin_ffi_common::decode::f64(payload) + .map(VMValue::Float) + .unwrap_or(VMValue::Void), 9 => { if let Some(h) = crate::runtime::plugin_ffi_common::decode::u64(payload) { - if let Some(arc) = crate::runtime::host_handles::get(h) { return VMValue::BoxRef(arc); } + if let Some(arc) = crate::runtime::host_handles::get(h) { + return VMValue::BoxRef(arc); + } } VMValue::Void } _ => VMValue::Void, } - } else { VMValue::Void } + } else { + VMValue::Void + } } fn to_handle(recv: &VMValue) -> Option { @@ -81,41 +110,69 @@ fn to_handle(recv: &VMValue) -> Option { pub const SYM_HOST_ARRAY_GET: &str = "nyash.host.array.get"; // (ArrayBox, i64) pub const SYM_HOST_ARRAY_SET: &str = "nyash.host.array.set"; // (ArrayBox, i64, val) pub const SYM_HOST_ARRAY_LEN: &str = "nyash.host.array.len"; // (ArrayBox) -pub const SYM_HOST_MAP_GET: &str = "nyash.host.map.get"; // (MapBox, key) -pub const SYM_HOST_MAP_SET: &str = "nyash.host.map.set"; // (MapBox, key, val) +pub const SYM_HOST_MAP_GET: &str = "nyash.host.map.get"; // (MapBox, key) +pub const SYM_HOST_MAP_SET: &str = "nyash.host.map.set"; // (MapBox, key, val) pub const SYM_HOST_MAP_SIZE: &str = "nyash.host.map.size"; // (MapBox) -pub const SYM_HOST_MAP_HAS: &str = "nyash.host.map.has"; // (MapBox, key) -pub const SYM_HOST_CONSOLE_LOG: &str = "nyash.host.console.log"; // (value) +pub const SYM_HOST_MAP_HAS: &str = "nyash.host.map.has"; // (MapBox, key) +pub const SYM_HOST_CONSOLE_LOG: &str = "nyash.host.console.log"; // (value) pub const SYM_HOST_CONSOLE_WARN: &str = "nyash.host.console.warn"; // (value) pub const SYM_HOST_CONSOLE_ERROR: &str = "nyash.host.console.error"; // (value) pub const SYM_HOST_INSTANCE_GETFIELD: &str = "nyash.host.instance.getField"; // (InstanceBox, name) pub const SYM_HOST_INSTANCE_SETFIELD: &str = "nyash.host.instance.setField"; // (InstanceBox, name, value) -// Arity-stable variants for Cranelift imports (avoid signature conflicts) + // Arity-stable variants for Cranelift imports (avoid signature conflicts) pub const SYM_HOST_INSTANCE_GETFIELD2: &str = "nyash.host.instance.getField2"; // (InstanceBox, name) pub const SYM_HOST_INSTANCE_SETFIELD3: &str = "nyash.host.instance.setField3"; // (InstanceBox, name, value) pub const SYM_HOST_INSTANCE_FIELD3: &str = "nyash.host.instance.field3"; // (recv,name,val or sentinel) pub const SYM_HOST_STRING_LEN: &str = "nyash.host.string.len"; // (StringBox) pub fn array_get(args: &[VMValue]) -> VMValue { - if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 100, &args[1..]) } else { VMValue::Void } + if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { + call_slot(h, 100, &args[1..]) + } else { + VMValue::Void + } } pub fn array_set(args: &[VMValue]) -> VMValue { - if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 101, &args[1..]) } else { VMValue::Void } + if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { + call_slot(h, 101, &args[1..]) + } else { + VMValue::Void + } } pub fn array_len(args: &[VMValue]) -> VMValue { - if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 102, &[]) } else { VMValue::Integer(0) } + if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { + call_slot(h, 102, &[]) + } else { + VMValue::Integer(0) + } } pub fn map_get(args: &[VMValue]) -> VMValue { - if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 203, &args[1..]) } else { VMValue::Void } + if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { + call_slot(h, 203, &args[1..]) + } else { + VMValue::Void + } } pub fn map_set(args: &[VMValue]) -> VMValue { - if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 204, &args[1..]) } else { VMValue::Void } + if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { + call_slot(h, 204, &args[1..]) + } else { + VMValue::Void + } } pub fn map_size(args: &[VMValue]) -> VMValue { - if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 200, &[]) } else { VMValue::Integer(0) } + if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { + call_slot(h, 200, &[]) + } else { + VMValue::Integer(0) + } } pub fn map_has(args: &[VMValue]) -> VMValue { - if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 202, &args[1..]) } else { VMValue::Bool(false) } + if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { + call_slot(h, 202, &args[1..]) + } else { + VMValue::Bool(false) + } } pub fn console_log(args: &[VMValue]) -> VMValue { @@ -127,22 +184,38 @@ pub fn console_log(args: &[VMValue]) -> VMValue { } pub fn console_warn(args: &[VMValue]) -> VMValue { - if let Some(a0) = args.get(0) { eprintln!("[warn] {}", a0.to_string()); } + if let Some(a0) = args.get(0) { + eprintln!("[warn] {}", a0.to_string()); + } VMValue::Void } pub fn console_error(args: &[VMValue]) -> VMValue { - if let Some(a0) = args.get(0) { eprintln!("[error] {}", a0.to_string()); } + if let Some(a0) = args.get(0) { + eprintln!("[error] {}", a0.to_string()); + } VMValue::Void } pub fn instance_getfield(args: &[VMValue]) -> VMValue { - if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 1, &args[1..]) } else { VMValue::Void } + if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { + call_slot(h, 1, &args[1..]) + } else { + VMValue::Void + } } pub fn instance_setfield(args: &[VMValue]) -> VMValue { - if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 2, &args[1..]) } else { VMValue::Void } + if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { + call_slot(h, 2, &args[1..]) + } else { + VMValue::Void + } } pub fn string_len(args: &[VMValue]) -> VMValue { - if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 300, &[]) } else { VMValue::Integer(0) } + if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { + call_slot(h, 300, &[]) + } else { + VMValue::Integer(0) + } } diff --git a/src/jit/extern/mod.rs b/src/jit/extern/mod.rs index 044413e8..09959baa 100644 --- a/src/jit/extern/mod.rs +++ b/src/jit/extern/mod.rs @@ -4,10 +4,10 @@ //! JIT can call via symbol names. Lowering will resolve MIR ops into //! these externs once call emission is added. -pub mod collections; -pub mod host_bridge; -pub mod handles; -pub mod birth; -pub mod runtime; pub mod r#async; +pub mod birth; +pub mod collections; +pub mod handles; +pub mod host_bridge; pub mod result; +pub mod runtime; diff --git a/src/jit/extern/result.rs b/src/jit/extern/result.rs index dd6eafce..a78d790f 100644 --- a/src/jit/extern/result.rs +++ b/src/jit/extern/result.rs @@ -10,9 +10,11 @@ pub const SYM_RESULT_ERR_H: &str = "nyash.result.err_h"; #[cfg(feature = "cranelift-jit")] pub extern "C" fn nyash_result_ok_h(handle: i64) -> i64 { - use crate::jit::rt::handles; use crate::boxes::result::NyashResultBox; - if handle <= 0 { return 0; } + use crate::jit::rt::handles; + if handle <= 0 { + return 0; + } if let Some(obj) = handles::get(handle as u64) { let boxed = obj.clone_box(); let res = NyashResultBox::new_ok(boxed); @@ -25,8 +27,8 @@ pub extern "C" fn nyash_result_ok_h(handle: i64) -> i64 { #[cfg(feature = "cranelift-jit")] pub extern "C" fn nyash_result_err_h(handle: i64) -> i64 { - use crate::jit::rt::handles; use crate::boxes::result::NyashResultBox; + use crate::jit::rt::handles; // If handle <= 0, synthesize a Timeout StringBox error for await paths. let err_box: Box = if handle <= 0 { Box::new(crate::box_trait::StringBox::new("Timeout".to_string())) diff --git a/src/jit/extern/runtime.rs b/src/jit/extern/runtime.rs index 744fa52d..9aba2695 100644 --- a/src/jit/extern/runtime.rs +++ b/src/jit/extern/runtime.rs @@ -5,4 +5,3 @@ pub const SYM_RT_CHECKPOINT: &str = "nyash.rt.checkpoint"; /// Write barrier hint for GC (no-op stub for now) pub const SYM_GC_BARRIER_WRITE: &str = "nyash.gc.barrier_write"; - diff --git a/src/jit/hostcall_registry.rs b/src/jit/hostcall_registry.rs index e4c1a9e7..ed0d6d65 100644 --- a/src/jit/hostcall_registry.rs +++ b/src/jit/hostcall_registry.rs @@ -1,12 +1,15 @@ //! Minimal hostcall registry (v0): classify symbols as read-only or mutating use once_cell::sync::OnceCell; -use std::collections::HashSet; use std::collections::HashMap; +use std::collections::HashSet; use std::sync::RwLock; #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum HostcallKind { ReadOnly, Mutating } +pub enum HostcallKind { + ReadOnly, + Mutating, +} #[derive(Debug, Default)] struct Registry { @@ -19,7 +22,9 @@ struct Registry { static REG: OnceCell> = OnceCell::new(); fn ensure_default() { - if REG.get().is_some() { return; } + if REG.get().is_some() { + return; + } let mut r = Registry::default(); // Read-only defaults for s in [ @@ -35,37 +40,153 @@ fn ensure_default() { "nyash.string.eq_hh", "nyash.string.lt_hh", "nyash.array.get_h", - ] { r.ro.insert(s.to_string()); } + ] { + r.ro.insert(s.to_string()); + } // Mutating defaults - for s in [ - "nyash.array.push_h", "nyash.array.set_h", "nyash.map.set_h" - ] { r.mu.insert(s.to_string()); } + for s in ["nyash.array.push_h", "nyash.array.set_h", "nyash.map.set_h"] { + r.mu.insert(s.to_string()); + } // Signatures (v0): register known symbols with simple arg/ret kinds // math.* thin bridge: f64 signatures only (allow when args match exactly) - r.sig.entry("nyash.math.sin".to_string()).or_default().push(Signature { args: vec![ArgKind::F64], ret: ArgKind::F64 }); - r.sig.entry("nyash.math.cos".to_string()).or_default().push(Signature { args: vec![ArgKind::F64], ret: ArgKind::F64 }); - r.sig.entry("nyash.math.abs".to_string()).or_default().push(Signature { args: vec![ArgKind::F64], ret: ArgKind::F64 }); - r.sig.entry("nyash.math.min".to_string()).or_default().push(Signature { args: vec![ArgKind::F64, ArgKind::F64], ret: ArgKind::F64 }); - r.sig.entry("nyash.math.max".to_string()).or_default().push(Signature { args: vec![ArgKind::F64, ArgKind::F64], ret: ArgKind::F64 }); + r.sig + .entry("nyash.math.sin".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::F64], + ret: ArgKind::F64, + }); + r.sig + .entry("nyash.math.cos".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::F64], + ret: ArgKind::F64, + }); + r.sig + .entry("nyash.math.abs".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::F64], + ret: ArgKind::F64, + }); + r.sig + .entry("nyash.math.min".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::F64, ArgKind::F64], + ret: ArgKind::F64, + }); + r.sig + .entry("nyash.math.max".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::F64, ArgKind::F64], + ret: ArgKind::F64, + }); // Collections (handle-based) // Map get: support both integer and handle keys (overload) - r.sig.entry("nyash.map.get_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::I64], ret: ArgKind::Handle }); - r.sig.entry("nyash.map.get_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::Handle], ret: ArgKind::Handle }); - r.sig.entry("nyash.map.size_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle], ret: ArgKind::I64 }); - r.sig.entry("nyash.array.get_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::I64], ret: ArgKind::Handle }); - r.sig.entry("nyash.array.len_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle], ret: ArgKind::I64 }); + r.sig + .entry("nyash.map.get_h".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::Handle, ArgKind::I64], + ret: ArgKind::Handle, + }); + r.sig + .entry("nyash.map.get_h".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::Handle, ArgKind::Handle], + ret: ArgKind::Handle, + }); + r.sig + .entry("nyash.map.size_h".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::Handle], + ret: ArgKind::I64, + }); + r.sig + .entry("nyash.array.get_h".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::Handle, ArgKind::I64], + ret: ArgKind::Handle, + }); + r.sig + .entry("nyash.array.len_h".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::Handle], + ret: ArgKind::I64, + }); // String helpers - r.sig.entry("nyash.string.len_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle], ret: ArgKind::I64 }); - r.sig.entry("nyash.string.charCodeAt_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::I64], ret: ArgKind::I64 }); - r.sig.entry("nyash.string.concat_hh".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::Handle], ret: ArgKind::Handle }); - r.sig.entry("nyash.semantics.add_hh".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::Handle], ret: ArgKind::Handle }); - r.sig.entry("nyash.string.eq_hh".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::Handle], ret: ArgKind::I64 }); - r.sig.entry("nyash.string.lt_hh".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::Handle], ret: ArgKind::I64 }); + r.sig + .entry("nyash.string.len_h".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::Handle], + ret: ArgKind::I64, + }); + r.sig + .entry("nyash.string.charCodeAt_h".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::Handle, ArgKind::I64], + ret: ArgKind::I64, + }); + r.sig + .entry("nyash.string.concat_hh".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::Handle, ArgKind::Handle], + ret: ArgKind::Handle, + }); + r.sig + .entry("nyash.semantics.add_hh".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::Handle, ArgKind::Handle], + ret: ArgKind::Handle, + }); + r.sig + .entry("nyash.string.eq_hh".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::Handle, ArgKind::Handle], + ret: ArgKind::I64, + }); + r.sig + .entry("nyash.string.lt_hh".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::Handle, ArgKind::Handle], + ret: ArgKind::I64, + }); // Any helpers (length/is_empty) - r.sig.entry("nyash.any.length_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle], ret: ArgKind::I64 }); - r.sig.entry("nyash.any.is_empty_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle], ret: ArgKind::I64 }); + r.sig + .entry("nyash.any.length_h".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::Handle], + ret: ArgKind::I64, + }); + r.sig + .entry("nyash.any.is_empty_h".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::Handle], + ret: ArgKind::I64, + }); // Map.has(handle, i64) -> i64(0/1) - r.sig.entry("nyash.map.has_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::I64], ret: ArgKind::I64 }); + r.sig + .entry("nyash.map.has_h".to_string()) + .or_default() + .push(Signature { + args: vec![ArgKind::Handle, ArgKind::I64], + ret: ArgKind::I64, + }); let _ = REG.set(RwLock::new(r)); } @@ -73,8 +194,12 @@ pub fn classify(symbol: &str) -> HostcallKind { ensure_default(); if let Some(lock) = REG.get() { if let Ok(g) = lock.read() { - if g.ro.contains(symbol) { return HostcallKind::ReadOnly; } - if g.mu.contains(symbol) { return HostcallKind::Mutating; } + if g.ro.contains(symbol) { + return HostcallKind::ReadOnly; + } + if g.mu.contains(symbol) { + return HostcallKind::Mutating; + } } } // Default to read-only to be permissive in v0 @@ -84,22 +209,37 @@ pub fn classify(symbol: &str) -> HostcallKind { pub fn add_readonly(symbol: &str) { ensure_default(); if let Some(lock) = REG.get() { - if let Ok(mut w) = lock.write() { w.ro.insert(symbol.to_string()); } + if let Ok(mut w) = lock.write() { + w.ro.insert(symbol.to_string()); + } } } pub fn add_mutating(symbol: &str) { ensure_default(); if let Some(lock) = REG.get() { - if let Ok(mut w) = lock.write() { w.mu.insert(symbol.to_string()); } + if let Ok(mut w) = lock.write() { + w.mu.insert(symbol.to_string()); + } } } pub fn set_from_csv(ro_csv: &str, mu_csv: &str) { ensure_default(); if let Some(lock) = REG.get() { if let Ok(mut w) = lock.write() { - w.ro.clear(); w.mu.clear(); - for s in ro_csv.split(',') { let t = s.trim(); if !t.is_empty() { w.ro.insert(t.to_string()); } } - for s in mu_csv.split(',') { let t = s.trim(); if !t.is_empty() { w.mu.insert(t.to_string()); } } + w.ro.clear(); + w.mu.clear(); + for s in ro_csv.split(',') { + let t = s.trim(); + if !t.is_empty() { + w.ro.insert(t.to_string()); + } + } + for s in mu_csv.split(',') { + let t = s.trim(); + if !t.is_empty() { + w.mu.insert(t.to_string()); + } + } } } } @@ -107,8 +247,10 @@ pub fn snapshot() -> (Vec, Vec) { ensure_default(); if let Some(lock) = REG.get() { if let Ok(g) = lock.read() { - let mut ro: Vec = g.ro.iter().cloned().collect(); ro.sort(); - let mut mu: Vec = g.mu.iter().cloned().collect(); mu.sort(); + let mut ro: Vec = g.ro.iter().cloned().collect(); + ro.sort(); + let mut mu: Vec = g.mu.iter().cloned().collect(); + mu.sort(); return (ro, mu); } } @@ -117,7 +259,11 @@ pub fn snapshot() -> (Vec, Vec) { // ==== Signature (v0 scaffolding) ==== #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ArgKind { I64, F64, Handle } +pub enum ArgKind { + I64, + F64, + Handle, +} #[derive(Debug, Clone)] pub struct Signature { @@ -143,9 +289,23 @@ pub fn set_signature_csv(symbol: &str, args_csv: &str, ret_str: &str) -> bool { .map(|t| parse_kind(t)) .collect(); let mut args: Vec = Vec::new(); - for p in parsed { if let Some(k) = p { args.push(k) } else { ok = false; } } - let ret = match parse_kind(ret_str) { Some(k) => k, None => { ok = false; ArgKind::I64 } }; - if !ok { return false; } + for p in parsed { + if let Some(k) = p { + args.push(k) + } else { + ok = false; + } + } + let ret = match parse_kind(ret_str) { + Some(k) => k, + None => { + ok = false; + ArgKind::I64 + } + }; + if !ok { + return false; + } let sig = Signature { args, ret }; if let Some(lock) = REG.get() { if let Ok(mut w) = lock.write() { @@ -168,11 +328,20 @@ pub fn check_signature(symbol: &str, observed_args: &[ArgKind]) -> Result<(), &' let relax = cfg_now.relax_numeric || cfg_now.native_f64; // Match against any one of the overload signatures 'outer: for sig in sigs.iter() { - if sig.args.len() != observed_args.len() { continue; } + if sig.args.len() != observed_args.len() { + continue; + } for (expected, observed) in sig.args.iter().zip(observed_args.iter()) { - if expected == observed { continue; } + if expected == observed { + continue; + } // v0 coercion: allow I64 → F64 only when relaxed numeric is enabled - if relax && matches!(expected, ArgKind::F64) && matches!(observed, ArgKind::I64) { continue; } + if relax + && matches!(expected, ArgKind::F64) + && matches!(observed, ArgKind::I64) + { + continue; + } // Mismatch for this candidate signature continue 'outer; } diff --git a/src/jit/lower/builder.rs b/src/jit/lower/builder.rs index 3f9de9bc..de20f77b 100644 --- a/src/jit/lower/builder.rs +++ b/src/jit/lower/builder.rs @@ -4,20 +4,37 @@ //! submodules and are enabled via feature flags. #[derive(Debug, Clone, Copy)] -pub enum BinOpKind { Add, Sub, Mul, Div, Mod } +pub enum BinOpKind { + Add, + Sub, + Mul, + Div, + Mod, +} #[derive(Debug, Clone, Copy)] -pub enum CmpKind { Eq, Ne, Lt, Le, Gt, Ge } +pub enum CmpKind { + Eq, + Ne, + Lt, + Le, + Gt, + Ge, +} #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ParamKind { I64, F64, B1 } +pub enum ParamKind { + I64, + F64, + B1, +} pub trait IRBuilder { fn begin_function(&mut self, name: &str); fn end_function(&mut self); - fn prepare_signature_i64(&mut self, _argc: usize, _has_ret: bool) { } - fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) { } - fn emit_param_i64(&mut self, _index: usize) { } + fn prepare_signature_i64(&mut self, _argc: usize, _has_ret: bool) {} + fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) {} + fn emit_param_i64(&mut self, _index: usize) {} fn emit_const_i64(&mut self, _val: i64); fn emit_const_f64(&mut self, _val: f64); fn emit_binop(&mut self, _op: BinOpKind); @@ -25,33 +42,59 @@ pub trait IRBuilder { fn emit_jump(&mut self); fn emit_branch(&mut self); fn emit_return(&mut self); - fn emit_select_i64(&mut self) { } - 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_fixed3(&mut self, _symbol: &str, _has_ret: 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_select_i64(&mut self) {} + 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_fixed3(&mut self, _symbol: &str, _has_ret: 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) {} // Create a StringBox handle from a string literal and push its handle (i64) onto the stack. - fn emit_string_handle_from_literal(&mut self, _s: &str) { } - fn prepare_blocks(&mut self, _count: usize) { } - fn switch_to_block(&mut self, _index: usize) { } - fn seal_block(&mut self, _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 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_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_b1_at(&mut self, _pos: usize) { self.push_block_param_i64_at(_pos); } - 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.br_if_top_is_true(_then_index, _else_index); } - fn jump_with_args(&mut self, _target_index: usize, _n: usize) { self.jump_to(_target_index); } - fn hint_ret_bool(&mut self, _is_b1: bool) { } - fn ensure_local_i64(&mut self, _index: usize) { } - fn store_local_i64(&mut self, _index: usize) { } - fn load_local_i64(&mut self, _index: usize) { } + fn emit_string_handle_from_literal(&mut self, _s: &str) {} + fn prepare_blocks(&mut self, _count: usize) {} + fn switch_to_block(&mut self, _index: usize) {} + fn seal_block(&mut self, _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 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_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_b1_at(&mut self, _pos: usize) { + self.push_block_param_i64_at(_pos); + } + 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.br_if_top_is_true(_then_index, _else_index); + } + fn jump_with_args(&mut self, _target_index: usize, _n: usize) { + self.jump_to(_target_index); + } + fn hint_ret_bool(&mut self, _is_b1: bool) {} + fn ensure_local_i64(&mut self, _index: usize) {} + fn store_local_i64(&mut self, _index: usize) {} + fn load_local_i64(&mut self, _index: usize) {} // Optional debug hook: print a local i64 value with a tag (Cranelift JIT only) - fn emit_debug_i64_local(&mut self, _tag: i64, _slot: usize) { } + fn emit_debug_i64_local(&mut self, _tag: i64, _slot: usize) {} } mod noop; diff --git a/src/jit/lower/builder/cranelift.rs b/src/jit/lower/builder/cranelift.rs index c7e8d4ad..48d80cd1 100644 --- a/src/jit/lower/builder/cranelift.rs +++ b/src/jit/lower/builder/cranelift.rs @@ -2,41 +2,35 @@ // Cranelift-based IR builder moved out of builder.rs for readability and maintainability -use super::{IRBuilder, BinOpKind, CmpKind, ParamKind}; +use super::{BinOpKind, CmpKind, IRBuilder, ParamKind}; use cranelift_codegen::ir::InstBuilder; use cranelift_module::Module; // TLS utilities and runtime shims live next to this builder under the same module -use super::tls::{self, clif_tls, tls_call_import_ret, tls_call_import_with_iconsts}; use super::rt_shims::{ - nyash_host_stub0, - nyash_jit_dbg_i64, - nyash_jit_block_enter, - nyash_plugin_invoke3_i64, - nyash_plugin_invoke3_f64, + nyash_host_stub0, nyash_jit_block_enter, nyash_jit_dbg_i64, nyash_plugin_invoke3_f64, + nyash_plugin_invoke3_i64, nyash_plugin_invoke_name_call_i64, nyash_plugin_invoke_name_getattr_i64, - nyash_plugin_invoke_name_call_i64, }; +use super::tls::{self, clif_tls, tls_call_import_ret, tls_call_import_with_iconsts}; // Handle-based extern thunks used by lowering use super::super::extern_thunks::{ - nyash_math_sin_f64, nyash_math_cos_f64, nyash_math_abs_f64, nyash_math_min_f64, nyash_math_max_f64, - nyash_array_len_h, nyash_array_get_h, nyash_array_set_h, nyash_array_push_h, nyash_array_last_h, - nyash_map_size_h, nyash_map_get_h, nyash_map_get_hh, nyash_map_set_h, nyash_map_has_h, - nyash_any_length_h, nyash_any_is_empty_h, - nyash_string_charcode_at_h, nyash_string_len_h, nyash_string_birth_h, nyash_integer_birth_h, - nyash_string_concat_hh, nyash_string_eq_hh, nyash_string_lt_hh, - nyash_box_birth_h, nyash_box_birth_i64, - nyash_handle_of, - nyash_rt_checkpoint, nyash_gc_barrier_write, - nyash_console_birth_h, nyash_string_from_ptr, + nyash_any_is_empty_h, nyash_any_length_h, nyash_array_get_h, nyash_array_last_h, + nyash_array_len_h, nyash_array_push_h, nyash_array_set_h, nyash_box_birth_h, + nyash_box_birth_i64, nyash_console_birth_h, nyash_gc_barrier_write, nyash_handle_of, + nyash_integer_birth_h, nyash_map_get_h, nyash_map_get_hh, nyash_map_has_h, nyash_map_set_h, + nyash_map_size_h, nyash_math_abs_f64, nyash_math_cos_f64, nyash_math_max_f64, + nyash_math_min_f64, nyash_math_sin_f64, nyash_rt_checkpoint, nyash_string_birth_h, + nyash_string_charcode_at_h, nyash_string_concat_hh, nyash_string_eq_hh, nyash_string_from_ptr, + nyash_string_len_h, nyash_string_lt_hh, }; use crate::jit::r#extern::r#async::nyash_future_await_h; -use crate::jit::r#extern::result::{nyash_result_ok_h, nyash_result_err_h}; +use crate::jit::r#extern::result::{nyash_result_err_h, nyash_result_ok_h}; use crate::{ - mir::{MirType, Effect as OpEffect, MirFunction}, jit::events, + mir::{Effect as OpEffect, MirFunction, MirType}, }; pub struct CraneliftBuilder { @@ -55,7 +49,11 @@ pub struct CraneliftBuilder { // Local stack slots for minimal Load/Store lowering (i64 only) local_slots: std::collections::HashMap, // Finalized function pointer (if any) - compiled_closure: Option crate::jit::abi::JitValue + Send + Sync>>, + compiled_closure: Option< + std::sync::Arc< + dyn Fn(&[crate::jit::abi::JitValue]) -> crate::jit::abi::JitValue + Send + Sync, + >, + >, // Desired simple ABI (Phase 10_c minimal): i64 params count and i64 return desired_argc: usize, desired_has_ret: bool, @@ -76,8 +74,11 @@ pub struct CraneliftBuilder { impl IRBuilder for CraneliftBuilder { fn prepare_signature_typed(&mut self, params: &[ParamKind], ret_is_f64: bool) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; - fn abi_param_for_kind(k: ParamKind, cfg: &crate::jit::config::JitConfig) -> cranelift_codegen::ir::AbiParam { + use cranelift_codegen::ir::{types, AbiParam, Signature}; + fn abi_param_for_kind( + k: ParamKind, + cfg: &crate::jit::config::JitConfig, + ) -> cranelift_codegen::ir::AbiParam { use cranelift_codegen::ir::types; match k { ParamKind::I64 => cranelift_codegen::ir::AbiParam::new(types::I64), @@ -86,7 +87,11 @@ impl IRBuilder for CraneliftBuilder { let _ = cfg.native_bool_abi; #[cfg(feature = "jit-b1-abi")] { - if crate::jit::config::probe_capabilities().supports_b1_sig && cfg.native_bool_abi { return cranelift_codegen::ir::AbiParam::new(types::B1); } + if crate::jit::config::probe_capabilities().supports_b1_sig + && cfg.native_bool_abi + { + return cranelift_codegen::ir::AbiParam::new(types::B1); + } } cranelift_codegen::ir::AbiParam::new(types::I64) } @@ -98,27 +103,37 @@ impl IRBuilder for CraneliftBuilder { let call_conv = self.module.isa().default_call_conv(); let mut sig = Signature::new(call_conv); let cfg_now = crate::jit::config::current(); - for &k in params { sig.params.push(abi_param_for_kind(k, &cfg_now)); } + for &k in params { + sig.params.push(abi_param_for_kind(k, &cfg_now)); + } if self.desired_has_ret { - if self.desired_ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); } - else { + if self.desired_ret_is_f64 { + sig.returns.push(AbiParam::new(types::F64)); + } else { let mut used_b1 = false; #[cfg(feature = "jit-b1-abi")] { let cfg_now = crate::jit::config::current(); - if crate::jit::config::probe_capabilities().supports_b1_sig && cfg_now.native_bool_abi && self.ret_hint_is_b1 { + if crate::jit::config::probe_capabilities().supports_b1_sig + && cfg_now.native_bool_abi + && self.ret_hint_is_b1 + { sig.returns.push(AbiParam::new(types::B1)); used_b1 = true; } } - if !used_b1 { sig.returns.push(AbiParam::new(types::I64)); } + if !used_b1 { + sig.returns.push(AbiParam::new(types::I64)); + } } } self.ctx.func.signature = sig; self.typed_sig_prepared = true; } fn emit_param_i64(&mut self, index: usize) { - if let Some(v) = self.entry_param(index) { self.value_stack.push(v); } + if let Some(v) = self.entry_param(index) { + self.value_stack.push(v); + } } fn prepare_signature_i64(&mut self, argc: usize, _has_ret: bool) { self.desired_argc = argc; @@ -128,7 +143,7 @@ impl IRBuilder for CraneliftBuilder { self.desired_ret_is_f64 = false; } fn begin_function(&mut self, name: &str) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; + use cranelift_codegen::ir::{types, AbiParam, Signature}; self.current_name = Some(name.to_string()); self.value_stack.clear(); clif_tls::FB.with(|cell| { @@ -136,40 +151,66 @@ impl IRBuilder for CraneliftBuilder { let call_conv = self.module.isa().default_call_conv(); let mut sig = Signature::new(call_conv); if std::env::var("NYASH_JIT_TRACE_SIG").ok().as_deref() == Some("1") { - eprintln!("[SIG] begin desired: argc={} has_ret={} ret_is_f64={} typed_prepared={}", self.desired_argc, self.desired_has_ret, self.desired_ret_is_f64, self.typed_sig_prepared); + eprintln!( + "[SIG] begin desired: argc={} has_ret={} ret_is_f64={} typed_prepared={}", + self.desired_argc, + self.desired_has_ret, + self.desired_ret_is_f64, + self.typed_sig_prepared + ); } if !self.typed_sig_prepared { - for _ in 0..self.desired_argc { sig.params.push(AbiParam::new(types::I64)); } + for _ in 0..self.desired_argc { + sig.params.push(AbiParam::new(types::I64)); + } if self.desired_has_ret { - if self.desired_ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); } - else { sig.returns.push(AbiParam::new(types::I64)); } + if self.desired_ret_is_f64 { + sig.returns.push(AbiParam::new(types::F64)); + } else { + sig.returns.push(AbiParam::new(types::I64)); + } } } else { - for _ in 0..self.desired_argc { sig.params.push(AbiParam::new(types::I64)); } + for _ in 0..self.desired_argc { + sig.params.push(AbiParam::new(types::I64)); + } if self.desired_has_ret { let mut used_b1 = false; #[cfg(feature = "jit-b1-abi")] { let cfg_now = crate::jit::config::current(); - if crate::jit::config::probe_capabilities().supports_b1_sig && cfg_now.native_bool_abi && self.ret_hint_is_b1 { + if crate::jit::config::probe_capabilities().supports_b1_sig + && cfg_now.native_bool_abi + && self.ret_hint_is_b1 + { sig.returns.push(AbiParam::new(types::B1)); used_b1 = true; } } if !used_b1 { - if self.desired_ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); } - else { sig.returns.push(AbiParam::new(types::I64)); } + if self.desired_ret_is_f64 { + sig.returns.push(AbiParam::new(types::F64)); + } else { + sig.returns.push(AbiParam::new(types::I64)); + } } } } tls.ctx.func.signature = sig; tls.ctx.func.name = cranelift_codegen::ir::UserFuncName::user(0, 0); - unsafe { tls.create(); } + unsafe { + tls.create(); + } tls.with(|fb| { - if self.blocks.is_empty() { let block = fb.create_block(); self.blocks.push(block); } + if self.blocks.is_empty() { + let block = fb.create_block(); + self.blocks.push(block); + } if self.pending_blocks > self.blocks.len() { let to_create = self.pending_blocks - self.blocks.len(); - for _ in 0..to_create { self.blocks.push(fb.create_block()); } + for _ in 0..to_create { + self.blocks.push(fb.create_block()); + } } let entry = self.blocks[0]; fb.append_block_params_for_function_params(entry); @@ -184,8 +225,13 @@ impl IRBuilder for CraneliftBuilder { sig.params.push(AbiParam::new(types::I64)); sig.params.push(AbiParam::new(types::I64)); sig.returns.push(AbiParam::new(types::I64)); - let fid = self.module - .declare_function("nyash.jit.dbg_i64", cranelift_module::Linkage::Import, &sig) + let fid = self + .module + .declare_function( + "nyash.jit.dbg_i64", + cranelift_module::Linkage::Import, + &sig, + ) .expect("declare dbg_i64 at entry"); let fref = self.module.declare_func_in_func(fid, fb.func); let ttag = fb.ins().iconst(types::I64, 900); @@ -203,7 +249,9 @@ impl IRBuilder for CraneliftBuilder { } fn end_function(&mut self) { use cranelift_module::Linkage; - if self.entry_block.is_none() { return; } + if self.entry_block.is_none() { + return; + } let mut ctx_opt: Option = None; clif_tls::FB.with(|cell| { if let Some(mut tls) = cell.take() { @@ -225,7 +273,10 @@ impl IRBuilder for CraneliftBuilder { fb.ins().stack_load(types::I64, ss, 0) } else { let params = fb.func.dfg.block_params(rb).to_vec(); - params.get(0).copied().unwrap_or_else(|| fb.ins().iconst(types::I64, 0)) + params + .get(0) + .copied() + .unwrap_or_else(|| fb.ins().iconst(types::I64, 0)) }; // Unconditional runtime debug call to observe return value just before final return (feed result back) { @@ -234,33 +285,57 @@ impl IRBuilder for CraneliftBuilder { sig.params.push(AbiParam::new(types::I64)); sig.params.push(AbiParam::new(types::I64)); sig.returns.push(AbiParam::new(types::I64)); - let fid = self.module.declare_function("nyash.jit.dbg_i64", Linkage::Import, &sig).expect("declare dbg_i64"); + let fid = self + .module + .declare_function("nyash.jit.dbg_i64", Linkage::Import, &sig) + .expect("declare dbg_i64"); let fref = self.module.declare_func_in_func(fid, fb.func); let tag = fb.ins().iconst(types::I64, 210); let call_inst = fb.ins().call(fref, &[tag, v]); - if let Some(rv) = fb.inst_results(call_inst).get(0).copied() { v = rv; } + if let Some(rv) = fb.inst_results(call_inst).get(0).copied() { + v = rv; + } + } + let ret_ty = fb + .func + .signature + .returns + .get(0) + .map(|p| p.value_type) + .unwrap_or(types::I64); + if ret_ty == types::F64 { + v = fb.ins().fcvt_from_sint(types::F64, v); } - let ret_ty = fb.func.signature.returns.get(0).map(|p| p.value_type).unwrap_or(types::I64); - if ret_ty == types::F64 { v = fb.ins().fcvt_from_sint(types::F64, v); } fb.ins().return_(&[v]); } } // Seal all blocks to satisfy CLIF verifier - for &b in &self.blocks { fb.seal_block(b); } + for &b in &self.blocks { + fb.seal_block(b); + } }); ctx_opt = Some(tls.take_context()); } }); if let Some(mut ctx) = ctx_opt.take() { let func_name = self.current_name.as_deref().unwrap_or("jit_func"); - let func_id = self.module.declare_function(func_name, Linkage::Local, &ctx.func.signature).expect("declare function"); + let func_id = self + .module + .declare_function(func_name, Linkage::Local, &ctx.func.signature) + .expect("declare function"); if std::env::var("NYASH_JIT_TRACE_SIG").ok().as_deref() == Some("1") { - eprintln!("[SIG] end returns={} params={}", ctx.func.signature.returns.len(), ctx.func.signature.params.len()); + eprintln!( + "[SIG] end returns={} params={}", + ctx.func.signature.returns.len(), + ctx.func.signature.params.len() + ); } if std::env::var("NYASH_JIT_DUMP_CLIF").ok().as_deref() == Some("1") { eprintln!("[CLIF] {}\n{}", func_name, ctx.func.display()); } - self.module.define_function(func_id, &mut ctx).expect("define function"); + self.module + .define_function(func_id, &mut ctx) + .expect("define function"); self.module.clear_context(&mut ctx); let _ = self.module.finalize_definitions(); let code = self.module.get_finalized_function(func_id); @@ -270,48 +345,112 @@ impl IRBuilder for CraneliftBuilder { let ret_is_f64 = self.desired_has_ret && self.desired_ret_is_f64; let code_usize = code as usize; unsafe { - let closure = std::sync::Arc::new(move |args: &[crate::jit::abi::JitValue]| -> crate::jit::abi::JitValue { - let mut a: [i64; 6] = [0; 6]; - let take = core::cmp::min(core::cmp::min(argc, 6), args.len()); - for i in 0..take { - a[i] = match args[i] { - crate::jit::abi::JitValue::I64(v) => v, - crate::jit::abi::JitValue::Bool(b) => if b { 1 } else { 0 }, - crate::jit::abi::JitValue::F64(f) => f as i64, - crate::jit::abi::JitValue::Handle(h) => h as i64, - }; - } - let ret_i64 = if has_ret { - match argc { - 0 => { let f: extern "C" fn() -> i64 = std::mem::transmute(code_usize); f() } - 1 => { let f: extern "C" fn(i64) -> i64 = std::mem::transmute(code_usize); f(a[0]) } - 2 => { let f: extern "C" fn(i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1]) } - 3 => { let f: extern "C" fn(i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2]) } - 4 => { let f: extern "C" fn(i64,i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3]) } - 5 => { let f: extern "C" fn(i64,i64,i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4]) } - _ => { let f: extern "C" fn(i64,i64,i64,i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4],a[5]) } + let closure = std::sync::Arc::new( + move |args: &[crate::jit::abi::JitValue]| -> crate::jit::abi::JitValue { + let mut a: [i64; 6] = [0; 6]; + let take = core::cmp::min(core::cmp::min(argc, 6), args.len()); + for i in 0..take { + a[i] = match args[i] { + crate::jit::abi::JitValue::I64(v) => v, + crate::jit::abi::JitValue::Bool(b) => { + if b { + 1 + } else { + 0 + } + } + crate::jit::abi::JitValue::F64(f) => f as i64, + crate::jit::abi::JitValue::Handle(h) => h as i64, + }; } - } else { 0 }; - if has_ret && ret_is_f64 { - let ret_f64 = match argc { - 0 => { let f: extern "C" fn() -> f64 = std::mem::transmute(code_usize); f() } - 1 => { let f: extern "C" fn(i64) -> f64 = std::mem::transmute(code_usize); f(a[0]) } - 2 => { let f: extern "C" fn(i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1]) } - 3 => { let f: extern "C" fn(i64,i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2]) } - 4 => { let f: extern "C" fn(i64,i64,i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3]) } - 5 => { let f: extern "C" fn(i64,i64,i64,i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4]) } - _ => { let f: extern "C" fn(i64,i64,i64,i64,i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4],a[5]) } + let ret_i64 = if has_ret { + match argc { + 0 => { + let f: extern "C" fn() -> i64 = std::mem::transmute(code_usize); + f() + } + 1 => { + let f: extern "C" fn(i64) -> i64 = + std::mem::transmute(code_usize); + f(a[0]) + } + 2 => { + let f: extern "C" fn(i64, i64) -> i64 = + std::mem::transmute(code_usize); + f(a[0], a[1]) + } + 3 => { + let f: extern "C" fn(i64, i64, i64) -> i64 = + std::mem::transmute(code_usize); + f(a[0], a[1], a[2]) + } + 4 => { + let f: extern "C" fn(i64, i64, i64, i64) -> i64 = + std::mem::transmute(code_usize); + f(a[0], a[1], a[2], a[3]) + } + 5 => { + let f: extern "C" fn(i64, i64, i64, i64, i64) -> i64 = + std::mem::transmute(code_usize); + f(a[0], a[1], a[2], a[3], a[4]) + } + _ => { + let f: extern "C" fn(i64, i64, i64, i64, i64, i64) -> i64 = + std::mem::transmute(code_usize); + f(a[0], a[1], a[2], a[3], a[4], a[5]) + } + } + } else { + 0 }; + if has_ret && ret_is_f64 { + let ret_f64 = match argc { + 0 => { + let f: extern "C" fn() -> f64 = std::mem::transmute(code_usize); + f() + } + 1 => { + let f: extern "C" fn(i64) -> f64 = + std::mem::transmute(code_usize); + f(a[0]) + } + 2 => { + let f: extern "C" fn(i64, i64) -> f64 = + std::mem::transmute(code_usize); + f(a[0], a[1]) + } + 3 => { + let f: extern "C" fn(i64, i64, i64) -> f64 = + std::mem::transmute(code_usize); + f(a[0], a[1], a[2]) + } + 4 => { + let f: extern "C" fn(i64, i64, i64, i64) -> f64 = + std::mem::transmute(code_usize); + f(a[0], a[1], a[2], a[3]) + } + 5 => { + let f: extern "C" fn(i64, i64, i64, i64, i64) -> f64 = + std::mem::transmute(code_usize); + f(a[0], a[1], a[2], a[3], a[4]) + } + _ => { + let f: extern "C" fn(i64, i64, i64, i64, i64, i64) -> f64 = + std::mem::transmute(code_usize); + f(a[0], a[1], a[2], a[3], a[4], a[5]) + } + }; + if std::env::var("NYASH_JIT_TRACE_CALL").ok().as_deref() == Some("1") { + eprintln!("[JIT-CALL] ret_f64={}", ret_f64); + } + return crate::jit::abi::JitValue::F64(ret_f64); + } if std::env::var("NYASH_JIT_TRACE_CALL").ok().as_deref() == Some("1") { - eprintln!("[JIT-CALL] ret_f64={}", ret_f64); + eprintln!("[JIT-CALL] ret_i64={}", ret_i64); } - return crate::jit::abi::JitValue::F64(ret_f64); - } - if std::env::var("NYASH_JIT_TRACE_CALL").ok().as_deref() == Some("1") { - eprintln!("[JIT-CALL] ret_i64={}", ret_i64); - } - crate::jit::abi::JitValue::I64(ret_i64) - }); + crate::jit::abi::JitValue::I64(ret_i64) + }, + ); self.compiled_closure = Some(closure); } } @@ -330,7 +469,9 @@ impl IRBuilder for CraneliftBuilder { } fn emit_binop(&mut self, op: BinOpKind) { use cranelift_codegen::ir::types; - if self.value_stack.len() < 2 { return; } + if self.value_stack.len() < 2 { + return; + } let mut rhs = self.value_stack.pop().unwrap(); let mut lhs = self.value_stack.pop().unwrap(); let res = Self::with_fb(|fb| { @@ -339,8 +480,12 @@ impl IRBuilder for CraneliftBuilder { let native_f64 = crate::jit::config::current().native_f64; let use_f64 = native_f64 && (lty == types::F64 || rty == types::F64); if use_f64 { - if lty != types::F64 { lhs = fb.ins().fcvt_from_sint(types::F64, lhs); } - if rty != types::F64 { rhs = fb.ins().fcvt_from_sint(types::F64, rhs); } + if lty != types::F64 { + lhs = fb.ins().fcvt_from_sint(types::F64, lhs); + } + if rty != types::F64 { + rhs = fb.ins().fcvt_from_sint(types::F64, rhs); + } match op { BinOpKind::Add => fb.ins().fadd(lhs, rhs), BinOpKind::Sub => fb.ins().fsub(lhs, rhs), @@ -348,8 +493,12 @@ impl IRBuilder for CraneliftBuilder { BinOpKind::Div => fb.ins().fdiv(lhs, rhs), // Cranelift does not have a native fmod; approximate by integer remainder on truncated values BinOpKind::Mod => { - let li = fb.ins().fcvt_to_sint(cranelift_codegen::ir::types::I64, lhs); - let ri = fb.ins().fcvt_to_sint(cranelift_codegen::ir::types::I64, rhs); + let li = fb + .ins() + .fcvt_to_sint(cranelift_codegen::ir::types::I64, lhs); + let ri = fb + .ins() + .fcvt_to_sint(cranelift_codegen::ir::types::I64, rhs); fb.ins().srem(li, ri) } } @@ -367,8 +516,13 @@ impl IRBuilder for CraneliftBuilder { self.stats.1 += 1; } fn emit_compare(&mut self, op: CmpKind) { - use cranelift_codegen::ir::{condcodes::{IntCC, FloatCC}, types}; - if self.value_stack.len() < 2 { return; } + use cranelift_codegen::ir::{ + condcodes::{FloatCC, IntCC}, + types, + }; + if self.value_stack.len() < 2 { + return; + } let mut rhs = self.value_stack.pop().unwrap(); let mut lhs = self.value_stack.pop().unwrap(); Self::with_fb(|fb| { @@ -377,8 +531,12 @@ impl IRBuilder for CraneliftBuilder { let native_f64 = crate::jit::config::current().native_f64; let use_f64 = native_f64 && (lty == types::F64 || rty == types::F64); let b1 = if use_f64 { - if lty != types::F64 { lhs = fb.ins().fcvt_from_sint(types::F64, lhs); } - if rty != types::F64 { rhs = fb.ins().fcvt_from_sint(types::F64, rhs); } + if lty != types::F64 { + lhs = fb.ins().fcvt_from_sint(types::F64, lhs); + } + if rty != types::F64 { + rhs = fb.ins().fcvt_from_sint(types::F64, rhs); + } let cc = match op { CmpKind::Eq => FloatCC::Equal, CmpKind::Ne => FloatCC::NotEqual, @@ -404,25 +562,37 @@ impl IRBuilder for CraneliftBuilder { }); } fn emit_select_i64(&mut self) { - use cranelift_codegen::ir::{types, condcodes::IntCC}; - if self.value_stack.len() < 3 { return; } + use cranelift_codegen::ir::{condcodes::IntCC, types}; + if self.value_stack.len() < 3 { + return; + } let mut else_v = self.value_stack.pop().unwrap(); let mut then_v = self.value_stack.pop().unwrap(); let mut cond_v = self.value_stack.pop().unwrap(); let sel = Self::with_fb(|fb| { let cty = fb.func.dfg.value_type(cond_v); - if cty == types::I64 { cond_v = fb.ins().icmp_imm(IntCC::NotEqual, cond_v, 0); crate::jit::rt::b1_norm_inc(1); } + if cty == types::I64 { + cond_v = fb.ins().icmp_imm(IntCC::NotEqual, cond_v, 0); + crate::jit::rt::b1_norm_inc(1); + } let tty = fb.func.dfg.value_type(then_v); - if tty != types::I64 { then_v = fb.ins().fcvt_to_sint(types::I64, then_v); } + if tty != types::I64 { + then_v = fb.ins().fcvt_to_sint(types::I64, then_v); + } let ety = fb.func.dfg.value_type(else_v); - if ety != types::I64 { else_v = fb.ins().fcvt_to_sint(types::I64, else_v); } + if ety != types::I64 { + else_v = fb.ins().fcvt_to_sint(types::I64, else_v); + } if std::env::var("NYASH_JIT_TRACE_SEL").ok().as_deref() == Some("1") { use cranelift_codegen::ir::{AbiParam, Signature}; let mut sig = Signature::new(self.module.isa().default_call_conv()); sig.params.push(AbiParam::new(types::I64)); sig.params.push(AbiParam::new(types::I64)); sig.returns.push(AbiParam::new(types::I64)); - let fid = self.module.declare_function("nyash.jit.dbg_i64", cranelift_module::Linkage::Import, &sig).expect("declare dbg_i64"); + let fid = self + .module + .declare_function("nyash.jit.dbg_i64", cranelift_module::Linkage::Import, &sig) + .expect("declare dbg_i64"); let fref = self.module.declare_func_in_func(fid, fb.func); let t_cond = fb.ins().iconst(types::I64, 100); let one = fb.ins().iconst(types::I64, 1); @@ -438,23 +608,47 @@ impl IRBuilder for CraneliftBuilder { }); self.value_stack.push(sel); } - fn emit_jump(&mut self) { self.stats.3 += 1; } - fn emit_branch(&mut self) { self.stats.3 += 1; } + fn emit_jump(&mut self) { + self.stats.3 += 1; + } + fn emit_branch(&mut self) { + self.stats.3 += 1; + } fn emit_return(&mut self) { use cranelift_codegen::ir::types; self.stats.4 += 1; Self::with_fb(|fb| { - if fb.func.signature.returns.is_empty() { fb.ins().return_(&[]); return; } - let mut v = if let Some(x) = self.value_stack.pop() { x } else { fb.ins().iconst(types::I64, 0) }; + if fb.func.signature.returns.is_empty() { + fb.ins().return_(&[]); + return; + } + let mut v = if let Some(x) = self.value_stack.pop() { + x + } else { + fb.ins().iconst(types::I64, 0) + }; let v_ty = fb.func.dfg.value_type(v); - if v_ty != types::I64 { v = if v_ty == types::F64 { fb.ins().fcvt_to_sint(types::I64, v) } else { let one = fb.ins().iconst(types::I64, 1); let zero = fb.ins().iconst(types::I64, 0); fb.ins().select(v, one, zero) } } - if std::env::var("NYASH_JIT_TRACE_RET").ok().as_deref() == Some("1") || std::env::var("NYASH_JIT_FORCE_RET_DBG").ok().as_deref() == Some("1") { + if v_ty != types::I64 { + v = if v_ty == types::F64 { + fb.ins().fcvt_to_sint(types::I64, v) + } else { + let one = fb.ins().iconst(types::I64, 1); + let zero = fb.ins().iconst(types::I64, 0); + fb.ins().select(v, one, zero) + } + } + if std::env::var("NYASH_JIT_TRACE_RET").ok().as_deref() == Some("1") + || std::env::var("NYASH_JIT_FORCE_RET_DBG").ok().as_deref() == Some("1") + { use cranelift_codegen::ir::{AbiParam, Signature}; let mut sig = Signature::new(self.module.isa().default_call_conv()); sig.params.push(AbiParam::new(types::I64)); sig.params.push(AbiParam::new(types::I64)); sig.returns.push(AbiParam::new(types::I64)); - let fid = self.module.declare_function("nyash.jit.dbg_i64", cranelift_module::Linkage::Import, &sig).expect("declare dbg_i64"); + let fid = self + .module + .declare_function("nyash.jit.dbg_i64", cranelift_module::Linkage::Import, &sig) + .expect("declare dbg_i64"); let fref = self.module.declare_func_in_func(fid, fb.func); let tag = fb.ins().iconst(types::I64, 201); let _ = fb.ins().call(fref, &[tag, v]); @@ -462,10 +656,15 @@ impl IRBuilder for CraneliftBuilder { // Persist return value in a dedicated stack slot to avoid SSA arg mishaps on ret block if self.ret_slot.is_none() { use cranelift_codegen::ir::StackSlotData; - let ss = fb.create_sized_stack_slot(StackSlotData::new(cranelift_codegen::ir::StackSlotKind::ExplicitSlot, 8)); + let ss = fb.create_sized_stack_slot(StackSlotData::new( + cranelift_codegen::ir::StackSlotKind::ExplicitSlot, + 8, + )); self.ret_slot = Some(ss); } - if let Some(ss) = self.ret_slot { fb.ins().stack_store(v, ss, 0); } + if let Some(ss) = self.ret_slot { + fb.ins().stack_store(v, ss, 0); + } // Unconditional debug of return value just before ret block jump (feed result back to v) { use cranelift_codegen::ir::{AbiParam, Signature}; @@ -473,22 +672,31 @@ impl IRBuilder for CraneliftBuilder { sig.params.push(AbiParam::new(types::I64)); sig.params.push(AbiParam::new(types::I64)); sig.returns.push(AbiParam::new(types::I64)); - let fid = self.module.declare_function("nyash.jit.dbg_i64", cranelift_module::Linkage::Import, &sig).expect("declare dbg_i64"); + let fid = self + .module + .declare_function("nyash.jit.dbg_i64", cranelift_module::Linkage::Import, &sig) + .expect("declare dbg_i64"); let fref = self.module.declare_func_in_func(fid, fb.func); let tag = fb.ins().iconst(types::I64, 211); let call_inst = fb.ins().call(fref, &[tag, v]); - if let Some(rv) = fb.inst_results(call_inst).get(0).copied() { v = rv; } + if let Some(rv) = fb.inst_results(call_inst).get(0).copied() { + v = rv; + } + } + if let Some(rb) = self.ret_block { + fb.ins().jump(rb, &[v]); } - if let Some(rb) = self.ret_block { fb.ins().jump(rb, &[v]); } }); self.cur_needs_term = false; } fn emit_host_call(&mut self, symbol: &str, _argc: usize, has_ret: bool) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; + use cranelift_codegen::ir::{types, AbiParam, Signature}; // Structured lower event for import call { let mut arg_types: Vec<&'static str> = Vec::new(); - for _ in 0.._argc { arg_types.push("I64"); } + for _ in 0.._argc { + arg_types.push("I64"); + } crate::jit::events::emit_lower( serde_json::json!({ "id": symbol, @@ -498,7 +706,8 @@ impl IRBuilder for CraneliftBuilder { "arg_types": arg_types, "ret": if has_ret { "I64" } else { "Void" } }), - "hostcall","" + "hostcall", + "", ); } let call_conv = self.module.isa().default_call_conv(); @@ -506,22 +715,46 @@ impl IRBuilder for CraneliftBuilder { // Collect up to _argc i64 values from stack (right-to-left) and pad with zeros to match arity let mut args: Vec = Vec::new(); let take_n = _argc.min(self.value_stack.len()); - for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { args.push(v); } } + for _ in 0..take_n { + if let Some(v) = self.value_stack.pop() { + args.push(v); + } + } args.reverse(); Self::with_fb(|fb| { - while args.len() < _argc { args.push(fb.ins().iconst(types::I64, 0)); } + while args.len() < _argc { + args.push(fb.ins().iconst(types::I64, 0)); + } }); - for _ in 0.._argc { sig.params.push(AbiParam::new(types::I64)); } - - let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare import failed"); - if let Some(v) = tls_call_import_ret(&mut self.module, func_id, &args, has_ret) { self.value_stack.push(v); } + for _ in 0.._argc { + sig.params.push(AbiParam::new(types::I64)); + } + + let func_id = self + .module + .declare_function(symbol, cranelift_module::Linkage::Import, &sig) + .expect("declare import failed"); + if let Some(v) = tls_call_import_ret(&mut self.module, func_id, &args, has_ret) { + self.value_stack.push(v); + } } - fn emit_host_call_typed(&mut self, symbol: &str, params: &[ParamKind], has_ret: bool, ret_is_f64: bool) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; + fn emit_host_call_typed( + &mut self, + symbol: &str, + params: &[ParamKind], + has_ret: bool, + ret_is_f64: bool, + ) { + use cranelift_codegen::ir::{types, AbiParam, Signature}; // Structured lower event for typed import call { let mut arg_types: Vec<&'static str> = Vec::new(); - for k in params { arg_types.push(match k { ParamKind::I64 | ParamKind::B1 => "I64", ParamKind::F64 => "F64" }); } + for k in params { + arg_types.push(match k { + ParamKind::I64 | ParamKind::B1 => "I64", + ParamKind::F64 => "F64", + }); + } crate::jit::events::emit_lower( serde_json::json!({ "id": symbol, @@ -531,12 +764,17 @@ impl IRBuilder for CraneliftBuilder { "arg_types": arg_types, "ret": if has_ret { if ret_is_f64 { "F64" } else { "I64" } } else { "Void" } }), - "hostcall","" + "hostcall", + "", ); } let mut args: Vec = Vec::new(); let take_n = params.len().min(self.value_stack.len()); - for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { args.push(v); } } + for _ in 0..take_n { + if let Some(v) = self.value_stack.pop() { + args.push(v); + } + } args.reverse(); let call_conv = self.module.isa().default_call_conv(); let mut sig = Signature::new(call_conv); @@ -545,50 +783,87 @@ impl IRBuilder for CraneliftBuilder { ParamKind::F64 => AbiParam::new(types::F64), ParamKind::B1 => AbiParam::new(types::I64), }; - for k in params { sig.params.push(abi_param_for_kind(k)); } - if has_ret { if ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); } else { sig.returns.push(AbiParam::new(types::I64)); } } - let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare typed import failed"); - if let Some(v) = tls_call_import_ret(&mut self.module, func_id, &args, has_ret) { self.value_stack.push(v); } + for k in params { + sig.params.push(abi_param_for_kind(k)); + } + if has_ret { + if ret_is_f64 { + sig.returns.push(AbiParam::new(types::F64)); + } else { + sig.returns.push(AbiParam::new(types::I64)); + } + } + let func_id = self + .module + .declare_function(symbol, cranelift_module::Linkage::Import, &sig) + .expect("declare typed import failed"); + if let Some(v) = tls_call_import_ret(&mut self.module, func_id, &args, has_ret) { + self.value_stack.push(v); + } } fn emit_debug_i64_local(&mut self, tag: i64, slot: usize) { - if std::env::var("NYASH_JIT_TRACE_LEN").ok().as_deref() != Some("1") { return; } + if std::env::var("NYASH_JIT_TRACE_LEN").ok().as_deref() != Some("1") { + return; + } use cranelift_codegen::ir::types; // Push tag and value let t = Self::with_fb(|fb| fb.ins().iconst(types::I64, tag)); self.value_stack.push(t); self.load_local_i64(slot); // Use existing typed hostcall helper to pass two I64 args - self.emit_host_call_typed("nyash.jit.dbg_i64", &[ParamKind::I64, ParamKind::I64], true, false); + self.emit_host_call_typed( + "nyash.jit.dbg_i64", + &[ParamKind::I64, ParamKind::I64], + true, + false, + ); // Drop the returned value to keep stack balanced let _ = self.value_stack.pop(); } fn emit_host_call_fixed3(&mut self, symbol: &str, has_ret: bool) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; + use cranelift_codegen::ir::{types, AbiParam, Signature}; let mut args: Vec = Vec::new(); // Pop up to 3 values; pad with zeros to reach exactly 3 let take_n = core::cmp::min(3, self.value_stack.len()); - for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { args.push(v); } } + for _ in 0..take_n { + if let Some(v) = self.value_stack.pop() { + args.push(v); + } + } args.reverse(); Self::with_fb(|fb| { - while args.len() < 3 { args.push(fb.ins().iconst(types::I64, 0)); } + while args.len() < 3 { + args.push(fb.ins().iconst(types::I64, 0)); + } }); let call_conv = self.module.isa().default_call_conv(); let mut sig = Signature::new(call_conv); - for _ in 0..3 { sig.params.push(AbiParam::new(types::I64)); } + for _ in 0..3 { + sig.params.push(AbiParam::new(types::I64)); + } // Always declare with I64 return to keep signature stable across call sites sig.returns.push(AbiParam::new(types::I64)); - let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare import fixed3 failed"); + let func_id = self + .module + .declare_function(symbol, cranelift_module::Linkage::Import, &sig) + .expect("declare import fixed3 failed"); if let Some(v) = tls_call_import_ret(&mut self.module, func_id, &args, true) { - if has_ret { self.value_stack.push(v); } + if has_ret { + self.value_stack.push(v); + } } } fn emit_plugin_invoke(&mut self, type_id: u32, method_id: u32, argc: usize, has_ret: bool) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; + use cranelift_codegen::ir::{types, AbiParam, Signature}; // Pop argc values (right-to-left): receiver + up to 2 args let mut arg_vals: Vec = { let take_n = argc.min(self.value_stack.len()); let mut tmp = Vec::new(); - for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { tmp.push(v); } } + for _ in 0..take_n { + if let Some(v) = self.value_stack.pop() { + tmp.push(v); + } + } tmp.reverse(); tmp }; @@ -599,52 +874,108 @@ impl IRBuilder for CraneliftBuilder { let mut sig_h = Signature::new(call_conv_h); sig_h.params.push(AbiParam::new(types::I64)); sig_h.returns.push(AbiParam::new(types::I64)); - let func_id_h = self.module.declare_function(h::SYM_HANDLE_OF, cranelift_module::Linkage::Import, &sig_h).expect("declare handle.of failed"); - tls_call_import_ret(&mut self.module, func_id_h, &arg_vals[0..1], true).expect("handle.of ret") + let func_id_h = self + .module + .declare_function(h::SYM_HANDLE_OF, cranelift_module::Linkage::Import, &sig_h) + .expect("declare handle.of failed"); + tls_call_import_ret(&mut self.module, func_id_h, &arg_vals[0..1], true) + .expect("handle.of ret") }; arg_vals[0] = a0_handle; // f64 shim allowed by env allowlist let use_f64 = if has_ret { if let Ok(list) = std::env::var("NYASH_JIT_PLUGIN_F64") { list.split(',').any(|e| { let mut it = e.split(':'); matches!((it.next(), it.next()), (Some(t), Some(m)) if t.parse::().ok()==Some(type_id) && m.parse::().ok()==Some(method_id)) }) - } else { false } - } else { false }; + } else { + false + } + } else { + false + }; let call_conv = self.module.isa().default_call_conv(); let mut sig = Signature::new(call_conv); - for _ in 0..6 { sig.params.push(AbiParam::new(types::I64)); } - if has_ret { sig.returns.push(AbiParam::new(if use_f64 { types::F64 } else { types::I64 })); } - let symbol = if use_f64 { "nyash_plugin_invoke3_f64" } else { "nyash_plugin_invoke3_i64" }; - let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare plugin shim failed"); + for _ in 0..6 { + sig.params.push(AbiParam::new(types::I64)); + } + if has_ret { + sig.returns + .push(AbiParam::new(if use_f64 { types::F64 } else { types::I64 })); + } + let symbol = if use_f64 { + "nyash_plugin_invoke3_f64" + } else { + "nyash_plugin_invoke3_i64" + }; + let func_id = self + .module + .declare_function(symbol, cranelift_module::Linkage::Import, &sig) + .expect("declare plugin shim failed"); let ret_val = Self::with_fb(|fb| { - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } - while arg_vals.len() < 3 { let z = fb.ins().iconst(types::I64, 0); arg_vals.push(z); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } + while arg_vals.len() < 3 { + let z = fb.ins().iconst(types::I64, 0); + arg_vals.push(z); + } // handle.of on receiver (redundant-safe) let call_conv_h = self.module.isa().default_call_conv(); let mut sig_h = Signature::new(call_conv_h); sig_h.params.push(AbiParam::new(types::I64)); sig_h.returns.push(AbiParam::new(types::I64)); - let func_id_h = self.module.declare_function(crate::jit::r#extern::handles::SYM_HANDLE_OF, cranelift_module::Linkage::Import, &sig_h).expect("declare handle.of failed"); + let func_id_h = self + .module + .declare_function( + crate::jit::r#extern::handles::SYM_HANDLE_OF, + cranelift_module::Linkage::Import, + &sig_h, + ) + .expect("declare handle.of failed"); let fref_h = self.module.declare_func_in_func(func_id_h, fb.func); let call_h = fb.ins().call(fref_h, &[arg_vals[0]]); - if let Some(rv) = fb.inst_results(call_h).get(0).copied() { arg_vals[0] = rv; } + if let Some(rv) = fb.inst_results(call_h).get(0).copied() { + arg_vals[0] = rv; + } let fref = self.module.declare_func_in_func(func_id, fb.func); let c_type = fb.ins().iconst(types::I64, type_id as i64); let c_meth = fb.ins().iconst(types::I64, method_id as i64); let c_argc = fb.ins().iconst(types::I64, argc as i64); - let call_inst = fb.ins().call(fref, &[c_type, c_meth, c_argc, arg_vals[0], arg_vals[1], arg_vals[2]]); - if has_ret { fb.inst_results(call_inst).get(0).copied() } else { None } + let call_inst = fb.ins().call( + fref, + &[ + c_type, + c_meth, + c_argc, + arg_vals[0], + arg_vals[1], + arg_vals[2], + ], + ); + if has_ret { + fb.inst_results(call_inst).get(0).copied() + } else { + None + } }); - if let Some(v) = ret_val { self.value_stack.push(v); } + if let Some(v) = ret_val { + self.value_stack.push(v); + } } fn emit_plugin_invoke_by_name(&mut self, method: &str, argc: usize, has_ret: bool) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; - // Collect call args + use cranelift_codegen::ir::{types, AbiParam, Signature}; + // Collect call args let mut arg_vals: Vec = { let take_n = argc.min(self.value_stack.len()); let mut tmp = Vec::new(); - for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { tmp.push(v); } } - tmp.reverse(); tmp + for _ in 0..take_n { + if let Some(v) = self.value_stack.pop() { + tmp.push(v); + } + } + tmp.reverse(); + tmp }; // Signature: nyash_plugin_invoke_name_*(argc, a0, a1, a2) let mut sig = Signature::new(self.module.isa().default_call_conv()); @@ -653,26 +984,48 @@ impl IRBuilder for CraneliftBuilder { sig.params.push(AbiParam::new(types::I64)); sig.params.push(AbiParam::new(types::I64)); sig.returns.push(AbiParam::new(types::I64)); - - let sym = match method { "getattr" => "nyash_plugin_invoke_name_getattr_i64", _ => "nyash_plugin_invoke_name_call_i64" }; - let func_id = self.module.declare_function(sym, cranelift_module::Linkage::Import, &sig).expect("declare name shim failed"); + + let sym = match method { + "getattr" => "nyash_plugin_invoke_name_getattr_i64", + _ => "nyash_plugin_invoke_name_call_i64", + }; + let func_id = self + .module + .declare_function(sym, cranelift_module::Linkage::Import, &sig) + .expect("declare name shim failed"); let ret_val = Self::with_fb(|fb| { - while arg_vals.len() < 3 { let z = fb.ins().iconst(types::I64, 0); arg_vals.push(z); } + while arg_vals.len() < 3 { + let z = fb.ins().iconst(types::I64, 0); + arg_vals.push(z); + } let fref = self.module.declare_func_in_func(func_id, fb.func); let cargc = fb.ins().iconst(types::I64, argc as i64); - let call_inst = fb.ins().call(fref, &[cargc, arg_vals[0], arg_vals[1], arg_vals[2]]); - if has_ret { fb.inst_results(call_inst).get(0).copied() } else { None } + let call_inst = fb + .ins() + .call(fref, &[cargc, arg_vals[0], arg_vals[1], arg_vals[2]]); + if has_ret { + fb.inst_results(call_inst).get(0).copied() + } else { + None + } }); - if let Some(v) = ret_val { self.value_stack.push(v); } + if let Some(v) = ret_val { + self.value_stack.push(v); + } } fn emit_string_handle_from_literal(&mut self, s: &str) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; + use cranelift_codegen::ir::{types, AbiParam, Signature}; // Pack up to 16 bytes into two u64 words (little-endian) let bytes = s.as_bytes(); - let mut lo: u64 = 0; let mut hi: u64 = 0; + let mut lo: u64 = 0; + let mut hi: u64 = 0; let take = core::cmp::min(16, bytes.len()); - for i in 0..take.min(8) { lo |= (bytes[i] as u64) << (8 * i as u32); } - for i in 8..take { hi |= (bytes[i] as u64) << (8 * (i - 8) as u32); } + for i in 0..take.min(8) { + lo |= (bytes[i] as u64) << (8 * i as u32); + } + for i in 8..take { + hi |= (bytes[i] as u64) << (8 * (i - 8) as u32); + } // Call thunk: nyash.string.from_u64x2(lo, hi, len) -> handle(i64) let call_conv = self.module.isa().default_call_conv(); let mut sig = Signature::new(call_conv); @@ -680,14 +1033,24 @@ impl IRBuilder for CraneliftBuilder { sig.params.push(AbiParam::new(types::I64)); // hi sig.params.push(AbiParam::new(types::I64)); // len sig.returns.push(AbiParam::new(types::I64)); - let func_id = self.module.declare_function(crate::jit::r#extern::collections::SYM_STRING_FROM_U64X2, cranelift_module::Linkage::Import, &sig).expect("declare string.from_u64x2"); + let func_id = self + .module + .declare_function( + crate::jit::r#extern::collections::SYM_STRING_FROM_U64X2, + cranelift_module::Linkage::Import, + &sig, + ) + .expect("declare string.from_u64x2"); let v = Self::with_fb(|fb| { let lo_v = fb.ins().iconst(types::I64, lo as i64); let hi_v = fb.ins().iconst(types::I64, hi as i64); let len_v = fb.ins().iconst(types::I64, bytes.len() as i64); let fref = self.module.declare_func_in_func(func_id, fb.func); let call_inst = fb.ins().call(fref, &[lo_v, hi_v, len_v]); - fb.inst_results(call_inst).get(0).copied().expect("str.from_ptr ret") + fb.inst_results(call_inst) + .get(0) + .copied() + .expect("str.from_ptr ret") }); self.value_stack.push(v); self.stats.0 += 1; @@ -695,20 +1058,32 @@ impl IRBuilder for CraneliftBuilder { fn prepare_blocks(&mut self, count: usize) { // Allow being called before begin_function; stash desired count let mut need_tls = false; - clif_tls::FB.with(|cell| { need_tls = cell.borrow().is_none(); }); + clif_tls::FB.with(|cell| { + need_tls = cell.borrow().is_none(); + }); if need_tls { self.pending_blocks = self.pending_blocks.max(count); return; } Self::with_fb(|fb| { - if count == 0 { return; } - if self.blocks.len() < count { for _ in 0..(count - self.blocks.len()) { self.blocks.push(fb.create_block()); } } + if count == 0 { + return; + } + if self.blocks.len() < count { + for _ in 0..(count - self.blocks.len()) { + self.blocks.push(fb.create_block()); + } + } }); } fn switch_to_block(&mut self, index: usize) { - if index >= self.blocks.len() { return; } + if index >= self.blocks.len() { + return; + } // Avoid redundant switch_to_block calls that can trip FunctionBuilder state - if self.current_block_index == Some(index) { return; } + if self.current_block_index == Some(index) { + return; + } Self::with_fb(|fb| { // If switching away from a non-terminated block, inject jump to keep CFG sane if let Some(cur) = self.current_block_index { @@ -723,80 +1098,173 @@ impl IRBuilder for CraneliftBuilder { self.cur_needs_term = true; }); } - fn seal_block(&mut self, _index: usize) { /* final sealing handled in end_function */ } + fn seal_block(&mut self, _index: usize) { /* final sealing handled in end_function */ + } fn br_if_top_is_true(&mut self, then_index: usize, else_index: usize) { use cranelift_codegen::ir::condcodes::IntCC; Self::with_fb(|fb| { - if then_index >= self.blocks.len() || else_index >= self.blocks.len() { return; } - let cond_val = if let Some(v) = self.value_stack.pop() { v } else { fb.ins().iconst(cranelift_codegen::ir::types::I64, 0) }; + if then_index >= self.blocks.len() || else_index >= self.blocks.len() { + return; + } + let cond_val = if let Some(v) = self.value_stack.pop() { + v + } else { + fb.ins().iconst(cranelift_codegen::ir::types::I64, 0) + }; let b1 = fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0); - fb.ins().brif(b1, self.blocks[then_index], &[], self.blocks[else_index], &[]); + fb.ins().brif( + b1, + self.blocks[then_index], + &[], + self.blocks[else_index], + &[], + ); }); - self.cur_needs_term = false; self.stats.3 += 1; - } - fn jump_to(&mut self, target_index: usize) { - Self::with_fb(|fb| { if target_index < self.blocks.len() { fb.ins().jump(self.blocks[target_index], &[]); } }); + self.cur_needs_term = false; self.stats.3 += 1; } - fn ensure_block_params_i64(&mut self, index: usize, count: usize) { self.block_param_counts.insert(index, count); } + fn jump_to(&mut self, target_index: usize) { + Self::with_fb(|fb| { + if target_index < self.blocks.len() { + fb.ins().jump(self.blocks[target_index], &[]); + } + }); + self.stats.3 += 1; + } + fn ensure_block_params_i64(&mut self, index: usize, count: usize) { + self.block_param_counts.insert(index, count); + } fn push_block_param_i64_at(&mut self, pos: usize) { let v = Self::with_fb(|fb| { - let b = if let Some(i) = self.current_block_index { self.blocks[i] } else { self.entry_block.unwrap() }; + let b = if let Some(i) = self.current_block_index { + self.blocks[i] + } else { + self.entry_block.unwrap() + }; let params = fb.func.dfg.block_params(b).to_vec(); - params.get(pos).copied().unwrap_or_else(|| fb.ins().iconst(cranelift_codegen::ir::types::I64, 0)) + params + .get(pos) + .copied() + .unwrap_or_else(|| fb.ins().iconst(cranelift_codegen::ir::types::I64, 0)) }); self.value_stack.push(v); } - fn br_if_with_args(&mut self, then_index: usize, else_index: usize, then_n: usize, else_n: usize) { - use cranelift_codegen::ir::{types, condcodes::IntCC}; - if then_index >= self.blocks.len() || else_index >= self.blocks.len() { return; } + fn br_if_with_args( + &mut self, + then_index: usize, + else_index: usize, + then_n: usize, + else_n: usize, + ) { + use cranelift_codegen::ir::{condcodes::IntCC, types}; + if then_index >= self.blocks.len() || else_index >= self.blocks.len() { + return; + } let mut else_args: Vec = Vec::new(); - for _ in 0..else_n { if let Some(v) = self.value_stack.pop() { else_args.push(v); } } + for _ in 0..else_n { + if let Some(v) = self.value_stack.pop() { + else_args.push(v); + } + } else_args.reverse(); let mut then_args: Vec = Vec::new(); - for _ in 0..then_n { if let Some(v) = self.value_stack.pop() { then_args.push(v); } } + for _ in 0..then_n { + if let Some(v) = self.value_stack.pop() { + then_args.push(v); + } + } then_args.reverse(); Self::with_fb(|fb| { let then_has_inst = self.materialize_succ_params(fb, then_index); let else_has_inst = self.materialize_succ_params(fb, else_index); - let cond_b1 = if let Some(v) = self.value_stack.pop() { let ty = fb.func.dfg.value_type(v); if ty == types::I64 { let out = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); crate::jit::rt::b1_norm_inc(1); out } else { v } } else { let zero = fb.ins().iconst(types::I64, 0); let out = fb.ins().icmp_imm(IntCC::NotEqual, zero, 0); crate::jit::rt::b1_norm_inc(1); out }; + let cond_b1 = if let Some(v) = self.value_stack.pop() { + let ty = fb.func.dfg.value_type(v); + if ty == types::I64 { + let out = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); + crate::jit::rt::b1_norm_inc(1); + out + } else { + v + } + } else { + let zero = fb.ins().iconst(types::I64, 0); + let out = fb.ins().icmp_imm(IntCC::NotEqual, zero, 0); + crate::jit::rt::b1_norm_inc(1); + out + }; let targs = if then_has_inst { Vec::new() } else { then_args }; let eargs = if else_has_inst { Vec::new() } else { else_args }; - fb.ins().brif(cond_b1, self.blocks[then_index], &targs, self.blocks[else_index], &eargs); + fb.ins().brif( + cond_b1, + self.blocks[then_index], + &targs, + self.blocks[else_index], + &eargs, + ); }); - self.cur_needs_term = false; self.stats.3 += 1; + self.cur_needs_term = false; + self.stats.3 += 1; } fn jump_with_args(&mut self, target_index: usize, n: usize) { let mut args: Vec = Vec::new(); - for _ in 0..n { if let Some(v) = self.value_stack.pop() { args.push(v); } } + for _ in 0..n { + if let Some(v) = self.value_stack.pop() { + args.push(v); + } + } args.reverse(); Self::with_fb(|fb| { let has_inst = self.materialize_succ_params(fb, target_index); - if has_inst { args.clear(); } + if has_inst { + args.clear(); + } fb.ins().jump(self.blocks[target_index], &args); }); - self.cur_needs_term = false; self.stats.3 += 1; + self.cur_needs_term = false; + self.stats.3 += 1; + } + fn hint_ret_bool(&mut self, is_b1: bool) { + self.ret_hint_is_b1 = is_b1; } - fn hint_ret_bool(&mut self, is_b1: bool) { self.ret_hint_is_b1 = is_b1; } fn ensure_local_i64(&mut self, index: usize) { use cranelift_codegen::ir::{StackSlotData, StackSlotKind}; - if self.local_slots.contains_key(&index) { return; } - Self::with_fb(|fb| { let slot = fb.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8)); self.local_slots.insert(index, slot); }); + if self.local_slots.contains_key(&index) { + return; + } + Self::with_fb(|fb| { + let slot = + fb.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8)); + self.local_slots.insert(index, slot); + }); } fn store_local_i64(&mut self, index: usize) { - use cranelift_codegen::ir::{types, condcodes::IntCC}; + use cranelift_codegen::ir::{condcodes::IntCC, types}; if let Some(mut v) = self.value_stack.pop() { - if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); } + if !self.local_slots.contains_key(&index) { + self.ensure_local_i64(index); + } let slot = self.local_slots.get(&index).copied(); Self::with_fb(|fb| { let ty = fb.func.dfg.value_type(v); if ty != types::I64 { - if ty == types::F64 { v = fb.ins().fcvt_to_sint(types::I64, v); } - else { let one = fb.ins().iconst(types::I64, 1); let zero = fb.ins().iconst(types::I64, 0); let b1 = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); v = fb.ins().select(b1, one, zero); } + if ty == types::F64 { + v = fb.ins().fcvt_to_sint(types::I64, v); + } else { + let one = fb.ins().iconst(types::I64, 1); + let zero = fb.ins().iconst(types::I64, 0); + let b1 = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); + v = fb.ins().select(b1, one, zero); + } + } + if let Some(slot) = slot { + fb.ins().stack_store(v, slot, 0); } - if let Some(slot) = slot { fb.ins().stack_store(v, slot, 0); } if std::env::var("NYASH_JIT_TRACE_LOCAL").ok().as_deref() == Some("1") { - eprintln!("[JIT-LOCAL] store idx={} (tracked_slots={})", index, self.local_slots.len()); + eprintln!( + "[JIT-LOCAL] store idx={} (tracked_slots={})", + index, + self.local_slots.len() + ); } }); if std::env::var("NYASH_JIT_TRACE_LOCAL").ok().as_deref() == Some("1") { @@ -804,26 +1272,43 @@ impl IRBuilder for CraneliftBuilder { let tag = Self::with_fb(|fb| fb.ins().iconst(types::I64, (1000 + index as i64))); self.value_stack.push(tag); self.value_stack.push(v); - self.emit_host_call_typed("nyash.jit.dbg_i64", &[ParamKind::I64, ParamKind::I64], true, false); + self.emit_host_call_typed( + "nyash.jit.dbg_i64", + &[ParamKind::I64, ParamKind::I64], + true, + false, + ); let _ = self.value_stack.pop(); } } } fn load_local_i64(&mut self, index: usize) { use cranelift_codegen::ir::types; - if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); } + if !self.local_slots.contains_key(&index) { + self.ensure_local_i64(index); + } if let Some(&slot) = self.local_slots.get(&index) { let v = Self::with_fb(|fb| fb.ins().stack_load(types::I64, slot, 0)); if std::env::var("NYASH_JIT_TRACE_LOCAL").ok().as_deref() == Some("1") { - eprintln!("[JIT-LOCAL] load idx={} (tracked_slots={})", index, self.local_slots.len()); + eprintln!( + "[JIT-LOCAL] load idx={} (tracked_slots={})", + index, + self.local_slots.len() + ); } - self.value_stack.push(v); self.stats.0 += 1; + self.value_stack.push(v); + self.stats.0 += 1; if std::env::var("NYASH_JIT_TRACE_LOCAL").ok().as_deref() == Some("1") { // tag = 2000 + index let tag = Self::with_fb(|fb| fb.ins().iconst(types::I64, (2000 + index as i64))); self.value_stack.push(tag); self.value_stack.push(v); - self.emit_host_call_typed("nyash.jit.dbg_i64", &[ParamKind::I64, ParamKind::I64], true, false); + self.emit_host_call_typed( + "nyash.jit.dbg_i64", + &[ParamKind::I64, ParamKind::I64], + true, + false, + ); let _ = self.value_stack.pop(); } } @@ -831,20 +1316,36 @@ impl IRBuilder for CraneliftBuilder { } impl CraneliftBuilder { - fn materialize_succ_params(&mut self, fb: &mut cranelift_frontend::FunctionBuilder<'static>, succ_index: usize) -> bool { + fn materialize_succ_params( + &mut self, + fb: &mut cranelift_frontend::FunctionBuilder<'static>, + succ_index: usize, + ) -> bool { use cranelift_codegen::ir::types; - if succ_index >= self.blocks.len() { return false; } + if succ_index >= self.blocks.len() { + return false; + } let b = self.blocks[succ_index]; let has_inst = fb.func.layout.first_inst(b).is_some(); if !has_inst { - let desired = self.block_param_counts.get(&succ_index).copied().unwrap_or(0); + let desired = self + .block_param_counts + .get(&succ_index) + .copied() + .unwrap_or(0); let current = fb.func.dfg.block_params(b).len(); - if desired > current { for _ in current..desired { let _ = fb.append_block_param(b, types::I64); } } + if desired > current { + for _ in current..desired { + let _ = fb.append_block_param(b, types::I64); + } + } } has_inst } fn entry_param(&mut self, index: usize) -> Option { - if let Some(b) = self.entry_block { return Self::with_fb(|fb| fb.func.dfg.block_params(b).get(index).copied()); } + if let Some(b) = self.entry_block { + return Self::with_fb(|fb| fb.func.dfg.block_params(b).get(index).copied()); + } None } fn with_fb(f: impl FnOnce(&mut cranelift_frontend::FunctionBuilder<'static>) -> R) -> R { @@ -855,15 +1356,25 @@ impl CraneliftBuilder { }) } pub fn new() -> Self { - let mut builder = cranelift_jit::JITBuilder::new(cranelift_module::default_libcall_names()).expect("JITBuilder"); + let mut builder = cranelift_jit::JITBuilder::new(cranelift_module::default_libcall_names()) + .expect("JITBuilder"); // Hostcall symbols builder.symbol("nyash.host.stub0", nyash_host_stub0 as *const u8); builder.symbol("nyash.jit.dbg_i64", nyash_jit_dbg_i64 as *const u8); builder.symbol("nyash.jit.block_enter", nyash_jit_block_enter as *const u8); // Async/Result - builder.symbol(crate::jit::r#extern::r#async::SYM_FUTURE_AWAIT_H, nyash_future_await_h as *const u8); - builder.symbol(crate::jit::r#extern::result::SYM_RESULT_OK_H, nyash_result_ok_h as *const u8); - builder.symbol(crate::jit::r#extern::result::SYM_RESULT_ERR_H, nyash_result_err_h as *const u8); + builder.symbol( + crate::jit::r#extern::r#async::SYM_FUTURE_AWAIT_H, + nyash_future_await_h as *const u8, + ); + builder.symbol( + crate::jit::r#extern::result::SYM_RESULT_OK_H, + nyash_result_ok_h as *const u8, + ); + builder.symbol( + crate::jit::r#extern::result::SYM_RESULT_ERR_H, + nyash_result_err_h as *const u8, + ); // Math builder.symbol("nyash.math.sin_f64", nyash_math_sin_f64 as *const u8); builder.symbol("nyash.math.cos_f64", nyash_math_cos_f64 as *const u8); @@ -872,7 +1383,7 @@ impl CraneliftBuilder { builder.symbol("nyash.math.max_f64", nyash_math_max_f64 as *const u8); // Handle-based collection/string/runtime { - use crate::jit::r#extern::{collections as c, handles as h, birth as b, runtime as r}; + use crate::jit::r#extern::{birth as b, collections as c, handles as h, runtime as r}; builder.symbol(c::SYM_ARRAY_LEN_H, nyash_array_len_h as *const u8); builder.symbol(c::SYM_ARRAY_GET_H, nyash_array_get_h as *const u8); builder.symbol(c::SYM_ARRAY_SET_H, nyash_array_set_h as *const u8); @@ -886,7 +1397,10 @@ impl CraneliftBuilder { builder.symbol(c::SYM_ANY_LEN_H, nyash_any_length_h as *const u8); builder.symbol(c::SYM_STRING_LEN_H, nyash_string_len_h as *const u8); builder.symbol(c::SYM_ANY_IS_EMPTY_H, nyash_any_is_empty_h as *const u8); - builder.symbol(c::SYM_STRING_CHARCODE_AT_H, nyash_string_charcode_at_h as *const u8); + builder.symbol( + c::SYM_STRING_CHARCODE_AT_H, + nyash_string_charcode_at_h as *const u8, + ); builder.symbol(c::SYM_STRING_BIRTH_H, nyash_string_birth_h as *const u8); builder.symbol(c::SYM_INTEGER_BIRTH_H, nyash_integer_birth_h as *const u8); builder.symbol("nyash.console.birth_h", nyash_console_birth_h as *const u8); @@ -895,38 +1409,73 @@ impl CraneliftBuilder { builder.symbol(c::SYM_STRING_LT_HH, nyash_string_lt_hh as *const u8); builder.symbol(b::SYM_BOX_BIRTH_H, nyash_box_birth_h as *const u8); builder.symbol("nyash.box.birth_i64", nyash_box_birth_i64 as *const u8); - builder.symbol(crate::jit::r#extern::birth::SYM_INSTANCE_BIRTH_NAME_U64X2, super::super::extern_thunks::nyash_instance_birth_name_u64x2 as *const u8); + builder.symbol( + crate::jit::r#extern::birth::SYM_INSTANCE_BIRTH_NAME_U64X2, + super::super::extern_thunks::nyash_instance_birth_name_u64x2 as *const u8, + ); builder.symbol(h::SYM_HANDLE_OF, nyash_handle_of as *const u8); builder.symbol(r::SYM_RT_CHECKPOINT, nyash_rt_checkpoint as *const u8); builder.symbol(r::SYM_GC_BARRIER_WRITE, nyash_gc_barrier_write as *const u8); } // Plugin invoke shims - builder.symbol("nyash_plugin_invoke3_i64", nyash_plugin_invoke3_i64 as *const u8); - builder.symbol("nyash_plugin_invoke3_f64", nyash_plugin_invoke3_f64 as *const u8); - builder.symbol("nyash_plugin_invoke_name_getattr_i64", nyash_plugin_invoke_name_getattr_i64 as *const u8); - builder.symbol("nyash_plugin_invoke_name_call_i64", nyash_plugin_invoke_name_call_i64 as *const u8); - builder.symbol(crate::jit::r#extern::collections::SYM_STRING_FROM_U64X2, super::super::extern_thunks::nyash_string_from_u64x2 as *const u8); + builder.symbol( + "nyash_plugin_invoke3_i64", + nyash_plugin_invoke3_i64 as *const u8, + ); + builder.symbol( + "nyash_plugin_invoke3_f64", + nyash_plugin_invoke3_f64 as *const u8, + ); + builder.symbol( + "nyash_plugin_invoke_name_getattr_i64", + nyash_plugin_invoke_name_getattr_i64 as *const u8, + ); + builder.symbol( + "nyash_plugin_invoke_name_call_i64", + nyash_plugin_invoke_name_call_i64 as *const u8, + ); + builder.symbol( + crate::jit::r#extern::collections::SYM_STRING_FROM_U64X2, + super::super::extern_thunks::nyash_string_from_u64x2 as *const u8, + ); // Host-bridge (by-slot) imports (opt-in) if std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1") { use crate::jit::r#extern::host_bridge as hb; // Instance.getField/setField (recv_h, name_i[, val_i]) // Use arity-stable import symbols to avoid signature collisions - builder.symbol(hb::SYM_HOST_INSTANCE_FIELD3, super::super::extern_thunks::nyash_host_instance_field3 as *const u8); + builder.symbol( + hb::SYM_HOST_INSTANCE_FIELD3, + super::super::extern_thunks::nyash_host_instance_field3 as *const u8, + ); // String.len (recv_h) - builder.symbol(hb::SYM_HOST_STRING_LEN, super::super::extern_thunks::nyash_host_string_len as *const u8); + builder.symbol( + hb::SYM_HOST_STRING_LEN, + super::super::extern_thunks::nyash_host_string_len as *const u8, + ); // Console.* (value) - builder.symbol(hb::SYM_HOST_CONSOLE_LOG, super::super::extern_thunks::nyash_host_console_log_i64 as *const u8); - builder.symbol(hb::SYM_HOST_CONSOLE_WARN, super::super::extern_thunks::nyash_host_console_warn_i64 as *const u8); - builder.symbol(hb::SYM_HOST_CONSOLE_ERROR, super::super::extern_thunks::nyash_host_console_error_i64 as *const u8); + builder.symbol( + hb::SYM_HOST_CONSOLE_LOG, + super::super::extern_thunks::nyash_host_console_log_i64 as *const u8, + ); + builder.symbol( + hb::SYM_HOST_CONSOLE_WARN, + super::super::extern_thunks::nyash_host_console_warn_i64 as *const u8, + ); + builder.symbol( + hb::SYM_HOST_CONSOLE_ERROR, + super::super::extern_thunks::nyash_host_console_error_i64 as *const u8, + ); } let module = cranelift_jit::JITModule::new(builder); let ctx = cranelift_codegen::Context::new(); let fbc = cranelift_frontend::FunctionBuilderContext::new(); CraneliftBuilder { - module, ctx, fbc, - stats: (0,0,0,0,0), + module, + ctx, + fbc, + stats: (0, 0, 0, 0, 0), current_name: None, value_stack: Vec::new(), entry_block: None, @@ -947,7 +1496,13 @@ impl CraneliftBuilder { sealed_blocks: std::collections::HashSet::new(), } } - pub fn take_compiled_closure(&mut self) -> Option crate::jit::abi::JitValue + Send + Sync>> { + pub fn take_compiled_closure( + &mut self, + ) -> Option< + std::sync::Arc< + dyn Fn(&[crate::jit::abi::JitValue]) -> crate::jit::abi::JitValue + Send + Sync, + >, + > { self.compiled_closure.take() } } diff --git a/src/jit/lower/builder/noop.rs b/src/jit/lower/builder/noop.rs index 14670ade..de51a0da 100644 --- a/src/jit/lower/builder/noop.rs +++ b/src/jit/lower/builder/noop.rs @@ -1,4 +1,4 @@ -use super::{IRBuilder, BinOpKind, CmpKind, ParamKind}; +use super::{BinOpKind, CmpKind, IRBuilder, ParamKind}; pub struct NoopBuilder { pub consts: usize, @@ -9,27 +9,81 @@ pub struct NoopBuilder { } impl NoopBuilder { - pub fn new() -> Self { Self { consts: 0, binops: 0, cmps: 0, branches: 0, rets: 0 } } + pub fn new() -> Self { + Self { + consts: 0, + binops: 0, + cmps: 0, + branches: 0, + rets: 0, + } + } } impl IRBuilder for NoopBuilder { fn begin_function(&mut self, _name: &str) {} fn end_function(&mut self) {} - fn emit_param_i64(&mut self, _index: usize) { self.consts += 1; } - fn emit_const_i64(&mut self, _val: i64) { self.consts += 1; } - fn emit_const_f64(&mut self, _val: f64) { self.consts += 1; } - fn emit_binop(&mut self, _op: BinOpKind) { self.binops += 1; } - fn emit_compare(&mut self, _op: CmpKind) { self.cmps += 1; } - fn emit_jump(&mut self) { self.branches += 1; } - fn emit_branch(&mut self) { self.branches += 1; } - fn emit_return(&mut self) { self.rets += 1; } - fn emit_select_i64(&mut self) { self.binops += 1; } - fn emit_host_call_typed(&mut self, _symbol: &str, _params: &[ParamKind], has_ret: bool, _ret_is_f64: bool) { if has_ret { self.consts += 1; } } - fn emit_host_call_fixed3(&mut self, _symbol: &str, has_ret: bool) { if has_ret { self.consts += 1; } } - fn emit_plugin_invoke(&mut self, _type_id: u32, _method_id: u32, _argc: usize, has_ret: bool) { if has_ret { self.consts += 1; } } - fn emit_plugin_invoke_by_name(&mut self, _method: &str, _argc: usize, has_ret: bool) { if has_ret { self.consts += 1; } } - fn emit_string_handle_from_literal(&mut self, _s: &str) { self.consts += 1; } - fn ensure_local_i64(&mut self, _index: usize) { } - fn store_local_i64(&mut self, _index: usize) { self.consts += 1; } - fn load_local_i64(&mut self, _index: usize) { self.consts += 1; } + fn emit_param_i64(&mut self, _index: usize) { + self.consts += 1; + } + fn emit_const_i64(&mut self, _val: i64) { + self.consts += 1; + } + fn emit_const_f64(&mut self, _val: f64) { + self.consts += 1; + } + fn emit_binop(&mut self, _op: BinOpKind) { + self.binops += 1; + } + fn emit_compare(&mut self, _op: CmpKind) { + self.cmps += 1; + } + fn emit_jump(&mut self) { + self.branches += 1; + } + fn emit_branch(&mut self) { + self.branches += 1; + } + fn emit_return(&mut self) { + self.rets += 1; + } + fn emit_select_i64(&mut self) { + self.binops += 1; + } + fn emit_host_call_typed( + &mut self, + _symbol: &str, + _params: &[ParamKind], + has_ret: bool, + _ret_is_f64: bool, + ) { + if has_ret { + self.consts += 1; + } + } + fn emit_host_call_fixed3(&mut self, _symbol: &str, has_ret: bool) { + if has_ret { + self.consts += 1; + } + } + fn emit_plugin_invoke(&mut self, _type_id: u32, _method_id: u32, _argc: usize, has_ret: bool) { + if has_ret { + self.consts += 1; + } + } + fn emit_plugin_invoke_by_name(&mut self, _method: &str, _argc: usize, has_ret: bool) { + if has_ret { + self.consts += 1; + } + } + fn emit_string_handle_from_literal(&mut self, _s: &str) { + self.consts += 1; + } + fn ensure_local_i64(&mut self, _index: usize) {} + fn store_local_i64(&mut self, _index: usize) { + self.consts += 1; + } + fn load_local_i64(&mut self, _index: usize) { + self.consts += 1; + } } diff --git a/src/jit/lower/builder/object.rs b/src/jit/lower/builder/object.rs index 3c94d554..ce3a3cba 100644 --- a/src/jit/lower/builder/object.rs +++ b/src/jit/lower/builder/object.rs @@ -1,7 +1,7 @@ #![cfg(feature = "cranelift-jit")] use super::{IRBuilder, ParamKind}; -use cranelift_module::Module; use cranelift_codegen::ir::InstBuilder; +use cranelift_module::Module; pub struct ObjectBuilder { pub(crate) module: cranelift_object::ObjectModule, @@ -19,32 +19,74 @@ pub struct ObjectBuilder { pub(crate) ret_hint_is_b1: bool, pub(crate) local_slots: std::collections::HashMap, pub(crate) block_param_counts: std::collections::HashMap, - pub stats: (u64,u64,u64,u64,u64), + pub stats: (u64, u64, u64, u64, u64), pub object_bytes: Option>, // Track rough kinds of values on the stack for bridging (e.g., plugin tagged invoke) value_tags: Vec, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -enum ValueTag { I64, F64, Handle, Unknown } +enum ValueTag { + I64, + F64, + Handle, + Unknown, +} impl ObjectBuilder { pub fn new() -> Self { use cranelift_codegen::settings; - let isa = cranelift_native::builder().expect("host ISA").finish(settings::Flags::new(settings::builder())).expect("finish ISA"); - let obj_builder = cranelift_object::ObjectBuilder::new(isa, "nyash_aot".to_string(), cranelift_module::default_libcall_names()).expect("ObjectBuilder"); + let isa = cranelift_native::builder() + .expect("host ISA") + .finish(settings::Flags::new(settings::builder())) + .expect("finish ISA"); + let obj_builder = cranelift_object::ObjectBuilder::new( + isa, + "nyash_aot".to_string(), + cranelift_module::default_libcall_names(), + ) + .expect("ObjectBuilder"); let module = cranelift_object::ObjectModule::new(obj_builder); - Self { module, ctx: cranelift_codegen::Context::new(), fbc: cranelift_frontend::FunctionBuilderContext::new(), current_name: None, entry_block: None, blocks: Vec::new(), current_block_index: None, value_stack: Vec::new(), typed_sig_prepared: false, desired_argc: 0, desired_has_ret: true, desired_ret_is_f64: false, ret_hint_is_b1: false, local_slots: std::collections::HashMap::new(), block_param_counts: std::collections::HashMap::new(), stats: (0,0,0,0,0), object_bytes: None, value_tags: Vec::new() } + Self { + module, + ctx: cranelift_codegen::Context::new(), + fbc: cranelift_frontend::FunctionBuilderContext::new(), + current_name: None, + entry_block: None, + blocks: Vec::new(), + current_block_index: None, + value_stack: Vec::new(), + typed_sig_prepared: false, + desired_argc: 0, + desired_has_ret: true, + desired_ret_is_f64: false, + ret_hint_is_b1: false, + local_slots: std::collections::HashMap::new(), + block_param_counts: std::collections::HashMap::new(), + stats: (0, 0, 0, 0, 0), + object_bytes: None, + value_tags: Vec::new(), + } } fn fresh_module() -> cranelift_object::ObjectModule { use cranelift_codegen::settings; - let isa = cranelift_native::builder().expect("host ISA").finish(settings::Flags::new(settings::builder())).expect("finish ISA"); - let obj_builder = cranelift_object::ObjectBuilder::new(isa, "nyash_aot".to_string(), cranelift_module::default_libcall_names()).expect("ObjectBuilder"); + let isa = cranelift_native::builder() + .expect("host ISA") + .finish(settings::Flags::new(settings::builder())) + .expect("finish ISA"); + let obj_builder = cranelift_object::ObjectBuilder::new( + isa, + "nyash_aot".to_string(), + cranelift_module::default_libcall_names(), + ) + .expect("ObjectBuilder"); cranelift_object::ObjectModule::new(obj_builder) } - pub fn take_object_bytes(&mut self) -> Option> { self.object_bytes.take() } + pub fn take_object_bytes(&mut self) -> Option> { + self.object_bytes.take() + } fn entry_param(&mut self, index: usize) -> Option { use cranelift_frontend::FunctionBuilder; @@ -52,7 +94,9 @@ impl ObjectBuilder { if let Some(b) = self.entry_block { fb.switch_to_block(b); let params = fb.func.dfg.block_params(b).to_vec(); - if let Some(v) = params.get(index).copied() { return Some(v); } + if let Some(v) = params.get(index).copied() { + return Some(v); + } } None } @@ -60,7 +104,7 @@ impl ObjectBuilder { impl IRBuilder for ObjectBuilder { fn begin_function(&mut self, name: &str) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; + use cranelift_codegen::ir::{types, AbiParam, Signature}; use cranelift_frontend::FunctionBuilder; self.current_name = Some(name.to_string()); self.value_stack.clear(); @@ -74,12 +118,22 @@ impl IRBuilder for ObjectBuilder { if !self.typed_sig_prepared { let call_conv = self.module.isa().default_call_conv(); let mut sig = Signature::new(call_conv); - for _ in 0..self.desired_argc { sig.params.push(AbiParam::new(types::I64)); } - if self.desired_has_ret { if self.desired_ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); } else { sig.returns.push(AbiParam::new(types::I64)); } } + for _ in 0..self.desired_argc { + sig.params.push(AbiParam::new(types::I64)); + } + if self.desired_has_ret { + if self.desired_ret_is_f64 { + sig.returns.push(AbiParam::new(types::F64)); + } else { + sig.returns.push(AbiParam::new(types::I64)); + } + } self.ctx.func.signature = sig; } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if self.blocks.is_empty() { self.blocks.push(fb.create_block()); } + if self.blocks.is_empty() { + self.blocks.push(fb.create_block()); + } let entry = self.blocks[0]; fb.append_block_params_for_function_params(entry); fb.switch_to_block(entry); @@ -90,56 +144,92 @@ impl IRBuilder for ObjectBuilder { use cranelift_codegen::ir::StackSlotData; use cranelift_frontend::FunctionBuilder; let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } fb.finalize(); // Export as ny_main so that nyrt can locate the entrypoint when linking AOT objects - let obj_id = self.module.declare_function("ny_main", cranelift_module::Linkage::Export, &self.ctx.func.signature).expect("declare func"); - self.module.define_function(obj_id, &mut self.ctx).expect("define"); - self.module.clear_context(&mut self.ctx); - let finished = std::mem::replace(&mut self.module, Self::fresh_module()); - let product = finished.finish(); - self.object_bytes = Some(product.emit().expect("emit object")); + let obj_id = self + .module + .declare_function( + "ny_main", + cranelift_module::Linkage::Export, + &self.ctx.func.signature, + ) + .expect("declare func"); + self.module + .define_function(obj_id, &mut self.ctx) + .expect("define"); + self.module.clear_context(&mut self.ctx); + let finished = std::mem::replace(&mut self.module, Self::fresh_module()); + let product = finished.finish(); + self.object_bytes = Some(product.emit().expect("emit object")); // Clear per-function state to allow reuse self.blocks.clear(); self.entry_block = None; self.current_block_index = None; } - fn prepare_signature_i64(&mut self, argc: usize, has_ret: bool) { self.desired_argc = argc; self.desired_has_ret = has_ret; } - fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) { self.typed_sig_prepared = true; } - fn emit_param_i64(&mut self, index: usize) { if let Some(v) = self.entry_param(index) { self.value_stack.push(v); self.value_tags.push(ValueTag::Unknown); } } + fn prepare_signature_i64(&mut self, argc: usize, has_ret: bool) { + self.desired_argc = argc; + self.desired_has_ret = has_ret; + } + fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) { + self.typed_sig_prepared = true; + } + fn emit_param_i64(&mut self, index: usize) { + if let Some(v) = self.entry_param(index) { + self.value_stack.push(v); + self.value_tags.push(ValueTag::Unknown); + } + } fn emit_const_i64(&mut self, val: i64) { use cranelift_codegen::ir::types; use cranelift_frontend::FunctionBuilder; let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } let v = fb.ins().iconst(types::I64, val); self.value_stack.push(v); self.value_tags.push(ValueTag::I64); self.stats.0 += 1; } fn emit_const_f64(&mut self, val: f64) { - use cranelift_frontend::FunctionBuilder; use cranelift_codegen::ir::types; + use cranelift_frontend::FunctionBuilder; let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } let v = fb.ins().f64const(val); self.value_stack.push(v); self.value_tags.push(ValueTag::F64); } fn emit_binop(&mut self, op: super::BinOpKind) { - use cranelift_frontend::FunctionBuilder; use cranelift_codegen::ir::types; - if self.value_stack.len() < 2 { return; } + use cranelift_frontend::FunctionBuilder; + if self.value_stack.len() < 2 { + return; + } let mut rhs = self.value_stack.pop().unwrap(); let mut lhs = self.value_stack.pop().unwrap(); let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } // Ensure i64 operands - if fb.func.dfg.value_type(lhs) != types::I64 { lhs = fb.ins().fcvt_to_sint(types::I64, lhs); } - if fb.func.dfg.value_type(rhs) != types::I64 { rhs = fb.ins().fcvt_to_sint(types::I64, rhs); } + if fb.func.dfg.value_type(lhs) != types::I64 { + lhs = fb.ins().fcvt_to_sint(types::I64, lhs); + } + if fb.func.dfg.value_type(rhs) != types::I64 { + rhs = fb.ins().fcvt_to_sint(types::I64, rhs); + } let res = match op { super::BinOpKind::Add => fb.ins().iadd(lhs, rhs), super::BinOpKind::Sub => fb.ins().isub(lhs, rhs), @@ -152,17 +242,26 @@ impl IRBuilder for ObjectBuilder { self.stats.1 += 1; } fn emit_compare(&mut self, op: super::CmpKind) { + use cranelift_codegen::ir::{condcodes::IntCC, types}; use cranelift_frontend::FunctionBuilder; - use cranelift_codegen::ir::{types, condcodes::IntCC}; - if self.value_stack.len() < 2 { return; } + if self.value_stack.len() < 2 { + return; + } let mut rhs = self.value_stack.pop().unwrap(); let mut lhs = self.value_stack.pop().unwrap(); let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } // Ensure i64 operands - if fb.func.dfg.value_type(lhs) != types::I64 { lhs = fb.ins().fcvt_to_sint(types::I64, lhs); } - if fb.func.dfg.value_type(rhs) != types::I64 { rhs = fb.ins().fcvt_to_sint(types::I64, rhs); } + if fb.func.dfg.value_type(lhs) != types::I64 { + lhs = fb.ins().fcvt_to_sint(types::I64, lhs); + } + if fb.func.dfg.value_type(rhs) != types::I64 { + rhs = fb.ins().fcvt_to_sint(types::I64, rhs); + } let cc = match op { super::CmpKind::Eq => IntCC::Equal, super::CmpKind::Ne => IntCC::NotEqual, @@ -179,24 +278,47 @@ impl IRBuilder for ObjectBuilder { self.value_tags.push(ValueTag::I64); self.stats.2 += 1; } - fn emit_jump(&mut self) { self.stats.3 += 1; } - fn emit_branch(&mut self) { self.stats.3 += 1; } + fn emit_jump(&mut self) { + self.stats.3 += 1; + } + fn emit_branch(&mut self) { + self.stats.3 += 1; + } fn emit_return(&mut self) { use cranelift_frontend::FunctionBuilder; let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } if self.desired_has_ret { if self.desired_ret_is_f64 { use cranelift_codegen::ir::types; - let v = if let Some(v) = self.value_stack.pop() { v } else { fb.ins().f64const(0.0) }; + let v = if let Some(v) = self.value_stack.pop() { + v + } else { + fb.ins().f64const(0.0) + }; // Coerce i64 to f64 if needed - let v2 = if fb.func.dfg.value_type(v) != types::F64 { fb.ins().fcvt_from_sint(types::F64, v) } else { v }; + let v2 = if fb.func.dfg.value_type(v) != types::F64 { + fb.ins().fcvt_from_sint(types::F64, v) + } else { + v + }; fb.ins().return_(&[v2]); } else { use cranelift_codegen::ir::types; - let v = if let Some(v) = self.value_stack.pop() { v } else { fb.ins().iconst(types::I64, 0) }; - let v2 = if fb.func.dfg.value_type(v) != types::I64 { fb.ins().fcvt_to_sint(types::I64, v) } else { v }; + let v = if let Some(v) = self.value_stack.pop() { + v + } else { + fb.ins().iconst(types::I64, 0) + }; + let v2 = if fb.func.dfg.value_type(v) != types::I64 { + fb.ins().fcvt_to_sint(types::I64, v) + } else { + v + }; fb.ins().return_(&[v2]); } } else { @@ -207,110 +329,206 @@ impl IRBuilder for ObjectBuilder { fn ensure_local_i64(&mut self, index: usize) { use cranelift_codegen::ir::StackSlotData; use cranelift_frontend::FunctionBuilder; - if self.local_slots.contains_key(&index) { return; } + if self.local_slots.contains_key(&index) { + return; + } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - let slot = fb.create_sized_stack_slot(StackSlotData::new(cranelift_codegen::ir::StackSlotKind::ExplicitSlot, 8)); + let slot = fb.create_sized_stack_slot(StackSlotData::new( + cranelift_codegen::ir::StackSlotKind::ExplicitSlot, + 8, + )); self.local_slots.insert(index, slot); } fn store_local_i64(&mut self, index: usize) { + use cranelift_codegen::ir::{condcodes::IntCC, types}; use cranelift_frontend::FunctionBuilder; - use cranelift_codegen::ir::{types, condcodes::IntCC}; - if let Some(mut v) = self.value_stack.pop() { + if let Some(mut v) = self.value_stack.pop() { let _ = self.value_tags.pop(); - if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); } - let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if !self.local_slots.contains_key(&index) { + self.ensure_local_i64(index); + } + let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } // Coerce to i64 if needed let ty = fb.func.dfg.value_type(v); if ty != types::I64 { - if ty == types::F64 { v = fb.ins().fcvt_to_sint(types::I64, v); } - else { let one = fb.ins().iconst(types::I64, 1); let zero = fb.ins().iconst(types::I64, 0); let b1 = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); v = fb.ins().select(b1, one, zero); } + if ty == types::F64 { + v = fb.ins().fcvt_to_sint(types::I64, v); + } else { + let one = fb.ins().iconst(types::I64, 1); + let zero = fb.ins().iconst(types::I64, 0); + let b1 = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); + v = fb.ins().select(b1, one, zero); + } + } + if let Some(&slot) = self.local_slots.get(&index) { + fb.ins().stack_store(v, slot, 0); } - if let Some(&slot) = self.local_slots.get(&index) { fb.ins().stack_store(v, slot, 0); } } } fn load_local_i64(&mut self, index: usize) { - use cranelift_frontend::FunctionBuilder; use cranelift_codegen::ir::types; - if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); } + use cranelift_frontend::FunctionBuilder; + if !self.local_slots.contains_key(&index) { + self.ensure_local_i64(index); + } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } if let Some(&slot) = self.local_slots.get(&index) { let v = fb.ins().stack_load(types::I64, slot, 0); self.value_stack.push(v); self.value_tags.push(ValueTag::Unknown); } } - fn prepare_blocks(&mut self, count: usize) { use cranelift_frontend::FunctionBuilder; if count == 0 { return; } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); if self.blocks.len() < count { for _ in 0..(count - self.blocks.len()) { self.blocks.push(fb.create_block()); } } } - fn switch_to_block(&mut self, index: usize) { use cranelift_frontend::FunctionBuilder; if index >= self.blocks.len() { return; } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); fb.switch_to_block(self.blocks[index]); self.current_block_index = Some(index); } - fn ensure_block_params_i64(&mut self, index: usize, count: usize) { + fn prepare_blocks(&mut self, count: usize) { use cranelift_frontend::FunctionBuilder; + if count == 0 { + return; + } + let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); + if self.blocks.len() < count { + for _ in 0..(count - self.blocks.len()) { + self.blocks.push(fb.create_block()); + } + } + } + fn switch_to_block(&mut self, index: usize) { + use cranelift_frontend::FunctionBuilder; + if index >= self.blocks.len() { + return; + } + let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); + fb.switch_to_block(self.blocks[index]); + self.current_block_index = Some(index); + } + fn ensure_block_params_i64(&mut self, index: usize, count: usize) { use cranelift_codegen::ir::types; - if index >= self.blocks.len() { return; } + use cranelift_frontend::FunctionBuilder; + if index >= self.blocks.len() { + return; + } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); let b = self.blocks[index]; let has_inst = fb.func.layout.first_inst(b).is_some(); if !has_inst { let current = fb.func.dfg.block_params(b).len(); - if count > current { for _ in current..count { let _ = fb.append_block_param(b, types::I64); } } + if count > current { + for _ in current..count { + let _ = fb.append_block_param(b, types::I64); + } + } } self.block_param_counts.insert(index, count); } fn push_block_param_i64_at(&mut self, pos: usize) { - use cranelift_frontend::FunctionBuilder; use cranelift_codegen::ir::types; + use cranelift_frontend::FunctionBuilder; let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - let b = if let Some(i) = self.current_block_index { self.blocks[i] } else if let Some(e) = self.entry_block { e } else { return; }; + let b = if let Some(i) = self.current_block_index { + self.blocks[i] + } else if let Some(e) = self.entry_block { + e + } else { + return; + }; let params = fb.func.dfg.block_params(b).to_vec(); - let v = params.get(pos).copied().unwrap_or_else(|| fb.ins().iconst(types::I64, 0)); + let v = params + .get(pos) + .copied() + .unwrap_or_else(|| fb.ins().iconst(types::I64, 0)); self.value_stack.push(v); } fn br_if_top_is_true(&mut self, then_index: usize, else_index: usize) { + use cranelift_codegen::ir::{condcodes::IntCC, types}; use cranelift_frontend::FunctionBuilder; - use cranelift_codegen::ir::{types, condcodes::IntCC}; - if then_index >= self.blocks.len() || else_index >= self.blocks.len() { return; } + if then_index >= self.blocks.len() || else_index >= self.blocks.len() { + return; + } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - let cond_val = if let Some(v) = self.value_stack.pop() { v } else { fb.ins().iconst(types::I64, 0) }; - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } - let b1 = if fb.func.dfg.value_type(cond_val) == types::I64 { fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0) } else { fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0) }; - fb.ins().brif(b1, self.blocks[then_index], &[], self.blocks[else_index], &[]); + let cond_val = if let Some(v) = self.value_stack.pop() { + v + } else { + fb.ins().iconst(types::I64, 0) + }; + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } + let b1 = if fb.func.dfg.value_type(cond_val) == types::I64 { + fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0) + } else { + fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0) + }; + fb.ins().brif( + b1, + self.blocks[then_index], + &[], + self.blocks[else_index], + &[], + ); self.stats.3 += 1; } fn jump_to(&mut self, target_index: usize) { use cranelift_frontend::FunctionBuilder; - if target_index >= self.blocks.len() { return; } + if target_index >= self.blocks.len() { + return; + } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } fb.ins().jump(self.blocks[target_index], &[]); self.stats.3 += 1; } fn emit_select_i64(&mut self) { + use cranelift_codegen::ir::{condcodes::IntCC, types}; use cranelift_frontend::FunctionBuilder; - use cranelift_codegen::ir::{types, condcodes::IntCC}; - if self.value_stack.len() < 3 { return; } + if self.value_stack.len() < 3 { + return; + } let mut else_v = self.value_stack.pop().unwrap(); let mut then_v = self.value_stack.pop().unwrap(); let cond_v = self.value_stack.pop().unwrap(); let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } - let cond_b1 = if fb.func.dfg.value_type(cond_v) == types::I64 { fb.ins().icmp_imm(IntCC::NotEqual, cond_v, 0) } else { fb.ins().icmp_imm(IntCC::NotEqual, cond_v, 0) }; - if fb.func.dfg.value_type(then_v) != types::I64 { then_v = fb.ins().fcvt_to_sint(types::I64, then_v); } - if fb.func.dfg.value_type(else_v) != types::I64 { else_v = fb.ins().fcvt_to_sint(types::I64, else_v); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } + let cond_b1 = if fb.func.dfg.value_type(cond_v) == types::I64 { + fb.ins().icmp_imm(IntCC::NotEqual, cond_v, 0) + } else { + fb.ins().icmp_imm(IntCC::NotEqual, cond_v, 0) + }; + if fb.func.dfg.value_type(then_v) != types::I64 { + then_v = fb.ins().fcvt_to_sint(types::I64, then_v); + } + if fb.func.dfg.value_type(else_v) != types::I64 { + else_v = fb.ins().fcvt_to_sint(types::I64, else_v); + } let sel = fb.ins().select(cond_b1, then_v, else_v); self.value_stack.push(sel); } fn emit_host_call(&mut self, symbol: &str, argc: usize, has_ret: bool) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; + use cranelift_codegen::ir::{types, AbiParam, Signature}; use cranelift_frontend::FunctionBuilder; // Structured lower event for import call (AOT builder) { let mut arg_types: Vec<&'static str> = Vec::new(); - for _ in 0..argc { arg_types.push("I64"); } + for _ in 0..argc { + arg_types.push("I64"); + } crate::jit::events::emit_lower( serde_json::json!({ "id": symbol, @@ -320,32 +538,68 @@ impl IRBuilder for ObjectBuilder { "arg_types": arg_types, "ret": if has_ret { "I64" } else { "Void" } }), - "hostcall","" + "hostcall", + "", ); } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } let mut sig = Signature::new(self.module.isa().default_call_conv()); - for _ in 0..argc { sig.params.push(AbiParam::new(types::I64)); } - if has_ret { sig.returns.push(AbiParam::new(types::I64)); } - let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare hostcall"); + for _ in 0..argc { + sig.params.push(AbiParam::new(types::I64)); + } + if has_ret { + sig.returns.push(AbiParam::new(types::I64)); + } + let func_id = self + .module + .declare_function(symbol, cranelift_module::Linkage::Import, &sig) + .expect("declare hostcall"); let mut args: Vec = Vec::with_capacity(argc); - for _ in 0..argc { if let Some(v) = self.value_stack.pop() { args.push(v); } else { args.push(fb.ins().iconst(types::I64, 0)); } } + for _ in 0..argc { + if let Some(v) = self.value_stack.pop() { + args.push(v); + } else { + args.push(fb.ins().iconst(types::I64, 0)); + } + } args.reverse(); // Ensure i64 for all - for a in args.iter_mut() { if fb.func.dfg.value_type(*a) != types::I64 { *a = fb.ins().fcvt_to_sint(types::I64, *a); } } + for a in args.iter_mut() { + if fb.func.dfg.value_type(*a) != types::I64 { + *a = fb.ins().fcvt_to_sint(types::I64, *a); + } + } let fref = self.module.declare_func_in_func(func_id, fb.func); let call_inst = fb.ins().call(fref, &args); - if has_ret { if let Some(v) = fb.inst_results(call_inst).get(0).copied() { self.value_stack.push(v); } } + if has_ret { + if let Some(v) = fb.inst_results(call_inst).get(0).copied() { + self.value_stack.push(v); + } + } } - fn emit_host_call_typed(&mut self, symbol: &str, params: &[super::ParamKind], has_ret: bool, ret_is_f64: bool) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; + fn emit_host_call_typed( + &mut self, + symbol: &str, + params: &[super::ParamKind], + has_ret: bool, + ret_is_f64: bool, + ) { + use cranelift_codegen::ir::{types, AbiParam, Signature}; use cranelift_frontend::FunctionBuilder; // Structured lower event for typed import call (AOT builder) { let mut arg_types: Vec<&'static str> = Vec::new(); - for k in params { arg_types.push(match k { super::ParamKind::I64 | super::ParamKind::B1 => "I64", super::ParamKind::F64 => "F64" }); } + for k in params { + arg_types.push(match k { + super::ParamKind::I64 | super::ParamKind::B1 => "I64", + super::ParamKind::F64 => "F64", + }); + } crate::jit::events::emit_lower( serde_json::json!({ "id": symbol, @@ -355,30 +609,62 @@ impl IRBuilder for ObjectBuilder { "arg_types": arg_types, "ret": if has_ret { if ret_is_f64 { "F64" } else { "I64" } } else { "Void" } }), - "hostcall","" + "hostcall", + "", ); } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } let mut sig = Signature::new(self.module.isa().default_call_conv()); for &k in params { - match k { super::ParamKind::I64 => sig.params.push(AbiParam::new(types::I64)), super::ParamKind::F64 => sig.params.push(AbiParam::new(types::F64)), super::ParamKind::B1 => sig.params.push(AbiParam::new(types::I64)) } + match k { + super::ParamKind::I64 => sig.params.push(AbiParam::new(types::I64)), + super::ParamKind::F64 => sig.params.push(AbiParam::new(types::F64)), + super::ParamKind::B1 => sig.params.push(AbiParam::new(types::I64)), + } } - if has_ret { if ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); } else { sig.returns.push(AbiParam::new(types::I64)); } } - let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare hostcall typed"); + if has_ret { + if ret_is_f64 { + sig.returns.push(AbiParam::new(types::F64)); + } else { + sig.returns.push(AbiParam::new(types::I64)); + } + } + let func_id = self + .module + .declare_function(symbol, cranelift_module::Linkage::Import, &sig) + .expect("declare hostcall typed"); // Gather args from stack (reverse) let mut args: Vec = Vec::with_capacity(params.len()); for &k in params.iter().rev() { let mut v = if let Some(v) = self.value_stack.pop() { v } else { - match k { super::ParamKind::I64 | super::ParamKind::B1 => fb.ins().iconst(types::I64, 0), super::ParamKind::F64 => fb.ins().f64const(0.0) } + match k { + super::ParamKind::I64 | super::ParamKind::B1 => fb.ins().iconst(types::I64, 0), + super::ParamKind::F64 => fb.ins().f64const(0.0), + } }; // Coerce v = match k { - super::ParamKind::I64 | super::ParamKind::B1 => { if fb.func.dfg.value_type(v) != types::I64 { fb.ins().fcvt_to_sint(types::I64, v) } else { v } }, - super::ParamKind::F64 => { if fb.func.dfg.value_type(v) != types::F64 { fb.ins().fcvt_from_sint(types::F64, v) } else { v } }, + super::ParamKind::I64 | super::ParamKind::B1 => { + if fb.func.dfg.value_type(v) != types::I64 { + fb.ins().fcvt_to_sint(types::I64, v) + } else { + v + } + } + super::ParamKind::F64 => { + if fb.func.dfg.value_type(v) != types::F64 { + fb.ins().fcvt_from_sint(types::F64, v) + } else { + v + } + } }; args.push(v); } @@ -387,8 +673,12 @@ impl IRBuilder for ObjectBuilder { let call_inst = fb.ins().call(fref, &args); if has_ret { if let Some(mut v) = fb.inst_results(call_inst).get(0).copied() { - if ret_is_f64 && fb.func.dfg.value_type(v) != types::F64 { v = fb.ins().fcvt_from_sint(types::F64, v); } - if !ret_is_f64 && fb.func.dfg.value_type(v) != types::I64 { v = fb.ins().fcvt_to_sint(types::I64, v); } + if ret_is_f64 && fb.func.dfg.value_type(v) != types::F64 { + v = fb.ins().fcvt_from_sint(types::F64, v); + } + if !ret_is_f64 && fb.func.dfg.value_type(v) != types::I64 { + v = fb.ins().fcvt_to_sint(types::I64, v); + } self.value_stack.push(v); } } @@ -397,107 +687,219 @@ impl IRBuilder for ObjectBuilder { self.emit_host_call(symbol, 3, has_ret); } fn emit_string_handle_from_literal(&mut self, s: &str) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; + use cranelift_codegen::ir::{types, AbiParam, Signature}; use cranelift_frontend::FunctionBuilder; // Pack up to 16 bytes of the literal into two u64 words let bytes = s.as_bytes(); - let mut lo: u64 = 0; let mut hi: u64 = 0; + let mut lo: u64 = 0; + let mut hi: u64 = 0; let take = core::cmp::min(16, bytes.len()); - for i in 0..take.min(8) { lo |= (bytes[i] as u64) << (8 * i as u32); } - for i in 8..take { hi |= (bytes[i] as u64) << (8 * (i - 8) as u32); } + for i in 0..take.min(8) { + lo |= (bytes[i] as u64) << (8 * i as u32); + } + for i in 8..take { + hi |= (bytes[i] as u64) << (8 * (i - 8) as u32); + } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } // Declare import: nyash.string.from_u64x2(lo, hi, len) -> i64 let mut sig = Signature::new(self.module.isa().default_call_conv()); sig.params.push(AbiParam::new(types::I64)); sig.params.push(AbiParam::new(types::I64)); sig.params.push(AbiParam::new(types::I64)); sig.returns.push(AbiParam::new(types::I64)); - let func_id = self.module.declare_function(crate::jit::r#extern::collections::SYM_STRING_FROM_U64X2, cranelift_module::Linkage::Import, &sig).expect("declare string.from_u64x2"); + let func_id = self + .module + .declare_function( + crate::jit::r#extern::collections::SYM_STRING_FROM_U64X2, + cranelift_module::Linkage::Import, + &sig, + ) + .expect("declare string.from_u64x2"); let lo_v = fb.ins().iconst(types::I64, lo as i64); let hi_v = fb.ins().iconst(types::I64, hi as i64); let len_v = fb.ins().iconst(types::I64, bytes.len() as i64); let fref = self.module.declare_func_in_func(func_id, fb.func); let call_inst = fb.ins().call(fref, &[lo_v, hi_v, len_v]); - if let Some(v) = fb.inst_results(call_inst).get(0).copied() { self.value_stack.push(v); self.value_tags.push(ValueTag::Handle); } + if let Some(v) = fb.inst_results(call_inst).get(0).copied() { + self.value_stack.push(v); + self.value_tags.push(ValueTag::Handle); + } } - fn br_if_with_args(&mut self, then_index: usize, else_index: usize, then_n: usize, else_n: usize) { + fn br_if_with_args( + &mut self, + then_index: usize, + else_index: usize, + then_n: usize, + else_n: usize, + ) { + use cranelift_codegen::ir::{condcodes::IntCC, types}; use cranelift_frontend::FunctionBuilder; - use cranelift_codegen::ir::{types, condcodes::IntCC}; - if then_index >= self.blocks.len() || else_index >= self.blocks.len() { return; } + if then_index >= self.blocks.len() || else_index >= self.blocks.len() { + return; + } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } // Pop else args, then then args (stack topに近い方から) let mut else_args: Vec = Vec::new(); - for _ in 0..else_n { if let Some(v) = self.value_stack.pop() { else_args.push(v); let _ = self.value_tags.pop(); } else { else_args.push(fb.ins().iconst(types::I64, 0)); } } + for _ in 0..else_n { + if let Some(v) = self.value_stack.pop() { + else_args.push(v); + let _ = self.value_tags.pop(); + } else { + else_args.push(fb.ins().iconst(types::I64, 0)); + } + } else_args.reverse(); let mut then_args: Vec = Vec::new(); - for _ in 0..then_n { if let Some(v) = self.value_stack.pop() { then_args.push(v); let _ = self.value_tags.pop(); } else { then_args.push(fb.ins().iconst(types::I64, 0)); } } + for _ in 0..then_n { + if let Some(v) = self.value_stack.pop() { + then_args.push(v); + let _ = self.value_tags.pop(); + } else { + then_args.push(fb.ins().iconst(types::I64, 0)); + } + } then_args.reverse(); // Cond - let cond_val = if let Some(v) = self.value_stack.pop() { v } else { fb.ins().iconst(types::I64, 0) }; - let b1 = if fb.func.dfg.value_type(cond_val) == types::I64 { fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0) } else { fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0) }; + let cond_val = if let Some(v) = self.value_stack.pop() { + v + } else { + fb.ins().iconst(types::I64, 0) + }; + let b1 = if fb.func.dfg.value_type(cond_val) == types::I64 { + fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0) + } else { + fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0) + }; // Coerce args to i64 - for a in then_args.iter_mut() { if fb.func.dfg.value_type(*a) != types::I64 { *a = fb.ins().fcvt_to_sint(types::I64, *a); } } - for a in else_args.iter_mut() { if fb.func.dfg.value_type(*a) != types::I64 { *a = fb.ins().fcvt_to_sint(types::I64, *a); } } - fb.ins().brif(b1, self.blocks[then_index], &then_args, self.blocks[else_index], &else_args); + for a in then_args.iter_mut() { + if fb.func.dfg.value_type(*a) != types::I64 { + *a = fb.ins().fcvt_to_sint(types::I64, *a); + } + } + for a in else_args.iter_mut() { + if fb.func.dfg.value_type(*a) != types::I64 { + *a = fb.ins().fcvt_to_sint(types::I64, *a); + } + } + fb.ins().brif( + b1, + self.blocks[then_index], + &then_args, + self.blocks[else_index], + &else_args, + ); self.stats.3 += 1; } fn jump_with_args(&mut self, target_index: usize, n: usize) { - use cranelift_frontend::FunctionBuilder; use cranelift_codegen::ir::types; - if target_index >= self.blocks.len() { return; } + use cranelift_frontend::FunctionBuilder; + if target_index >= self.blocks.len() { + return; + } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } let mut args: Vec = Vec::new(); - for _ in 0..n { if let Some(v) = self.value_stack.pop() { args.push(v); let _ = self.value_tags.pop(); } else { args.push(fb.ins().iconst(types::I64, 0)); } } + for _ in 0..n { + if let Some(v) = self.value_stack.pop() { + args.push(v); + let _ = self.value_tags.pop(); + } else { + args.push(fb.ins().iconst(types::I64, 0)); + } + } args.reverse(); - for a in args.iter_mut() { if fb.func.dfg.value_type(*a) != types::I64 { *a = fb.ins().fcvt_to_sint(types::I64, *a); } } + for a in args.iter_mut() { + if fb.func.dfg.value_type(*a) != types::I64 { + *a = fb.ins().fcvt_to_sint(types::I64, *a); + } + } fb.ins().jump(self.blocks[target_index], &args); self.stats.3 += 1; } fn emit_plugin_invoke(&mut self, _type_id: u32, _method_id: u32, argc: usize, has_ret: bool) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; + use cranelift_codegen::ir::{types, AbiParam, Signature}; use cranelift_frontend::FunctionBuilder; // We import NyRT tagged invoke entry (by-id). Signature: // nyash_plugin_invoke3_tagged_i64(type_id, method_id, argc, a0, a1, tag1, a2, tag2, a3, tag3, a4, tag4) -> i64 let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } // Pop args in reverse: last pushed is top. Collect up to 4 (excluding recv) let mut arg_vals: Vec = Vec::new(); let mut arg_tags: Vec = Vec::new(); - for _ in 0..argc.saturating_sub(1) { // exclude receiver (first param) + for _ in 0..argc.saturating_sub(1) { + // exclude receiver (first param) if let Some(v) = self.value_stack.pop() { arg_vals.push(v); arg_tags.push(self.value_tags.pop().unwrap_or(ValueTag::Unknown)); } } // Receiver - let recv = if let Some(v) = self.value_stack.pop() { let _ = self.value_tags.pop(); v } else { fb.ins().iconst(types::I64, 0) }; + let recv = if let Some(v) = self.value_stack.pop() { + let _ = self.value_tags.pop(); + v + } else { + fb.ins().iconst(types::I64, 0) + }; arg_vals.reverse(); arg_tags.reverse(); - let mut tag_i64 = |t: ValueTag| -> i64 { match t { ValueTag::Handle => 8, ValueTag::F64 => 5, ValueTag::I64 => 3, ValueTag::Unknown => 3 } }; + let mut tag_i64 = |t: ValueTag| -> i64 { + match t { + ValueTag::Handle => 8, + ValueTag::F64 => 5, + ValueTag::I64 => 3, + ValueTag::Unknown => 3, + } + }; // Build signature and declare import let mut sig = Signature::new(self.module.isa().default_call_conv()); - for _ in 0..12 { sig.params.push(AbiParam::new(types::I64)); } + for _ in 0..12 { + sig.params.push(AbiParam::new(types::I64)); + } sig.returns.push(AbiParam::new(types::I64)); - let func_id = self.module.declare_function("nyash_plugin_invoke3_tagged_i64", cranelift_module::Linkage::Import, &sig).expect("declare plugin invoke tagged"); + let func_id = self + .module + .declare_function( + "nyash_plugin_invoke3_tagged_i64", + cranelift_module::Linkage::Import, + &sig, + ) + .expect("declare plugin invoke tagged"); let fref = self.module.declare_func_in_func(func_id, fb.func); // Prepare args array let mut args: Vec = Vec::with_capacity(12); let to_i64 = |fb: &mut FunctionBuilder, v: cranelift_codegen::ir::Value| { - if fb.func.dfg.value_type(v) != types::I64 { fb.ins().fcvt_to_sint(types::I64, v) } else { v } + if fb.func.dfg.value_type(v) != types::I64 { + fb.ins().fcvt_to_sint(types::I64, v) + } else { + v + } }; - let t_i64 = |_fb: &mut FunctionBuilder, x: i64| -> cranelift_codegen::ir::Value { _fb.ins().iconst(types::I64, x) }; + let t_i64 = |_fb: &mut FunctionBuilder, x: i64| -> cranelift_codegen::ir::Value { + _fb.ins().iconst(types::I64, x) + }; // Pass through type_id/method_id from lowering (method_id must match plugin vtable) args.push(t_i64(&mut fb, _type_id as i64)); // type_id (runtime may override with real_type_id) @@ -519,42 +921,90 @@ impl IRBuilder for ObjectBuilder { let call_inst = fb.ins().call(fref, &args); if has_ret { - if let Some(v) = fb.inst_results(call_inst).get(0).copied() { self.value_stack.push(v); self.value_tags.push(ValueTag::I64); } + if let Some(v) = fb.inst_results(call_inst).get(0).copied() { + self.value_stack.push(v); + self.value_tags.push(ValueTag::I64); + } } } fn emit_plugin_invoke_by_name(&mut self, _method: &str, argc: usize, has_ret: bool) { - use cranelift_codegen::ir::{AbiParam, Signature, types}; + use cranelift_codegen::ir::{types, AbiParam, Signature}; use cranelift_frontend::FunctionBuilder; // Use nyash.plugin.invoke_by_name_i64(recv_h, method_cstr, argc, a1, a2) // Limit: supports up to 2 args beyond receiver. let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); - if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } - else if let Some(b) = self.entry_block { fb.switch_to_block(b); } + if let Some(idx) = self.current_block_index { + fb.switch_to_block(self.blocks[idx]); + } else if let Some(b) = self.entry_block { + fb.switch_to_block(b); + } // Pop args and recv let mut arg_vals: Vec = Vec::new(); - for _ in 0..argc.saturating_sub(1) { if let Some(v) = self.value_stack.pop() { arg_vals.push(v); let _ = self.value_tags.pop(); } } - let recv = if let Some(v) = self.value_stack.pop() { let _ = self.value_tags.pop(); v } else { fb.ins().iconst(types::I64, 0) }; + for _ in 0..argc.saturating_sub(1) { + if let Some(v) = self.value_stack.pop() { + arg_vals.push(v); + let _ = self.value_tags.pop(); + } + } + let recv = if let Some(v) = self.value_stack.pop() { + let _ = self.value_tags.pop(); + v + } else { + fb.ins().iconst(types::I64, 0) + }; arg_vals.reverse(); let mut sig = Signature::new(self.module.isa().default_call_conv()); - for _ in 0..5 { sig.params.push(AbiParam::new(types::I64)); } + for _ in 0..5 { + sig.params.push(AbiParam::new(types::I64)); + } sig.returns.push(AbiParam::new(types::I64)); - let func_id = self.module.declare_function("nyash.plugin.invoke_by_name_i64", cranelift_module::Linkage::Import, &sig).expect("declare plugin invoke by-name"); + let func_id = self + .module + .declare_function( + "nyash.plugin.invoke_by_name_i64", + cranelift_module::Linkage::Import, + &sig, + ) + .expect("declare plugin invoke by-name"); let fref = self.module.declare_func_in_func(func_id, fb.func); - let to_i64 = |fb: &mut FunctionBuilder, v: cranelift_codegen::ir::Value| { if fb.func.dfg.value_type(v) != types::I64 { fb.ins().fcvt_to_sint(types::I64, v) } else { v } }; + let to_i64 = |fb: &mut FunctionBuilder, v: cranelift_codegen::ir::Value| { + if fb.func.dfg.value_type(v) != types::I64 { + fb.ins().fcvt_to_sint(types::I64, v) + } else { + v + } + }; let zero = fb.ins().iconst(types::I64, 0); let mut args: Vec = Vec::with_capacity(5); args.push(to_i64(&mut fb, recv)); // method ptr not supported in object builder (no easy CStr symbol payload); pass 0 to let runtime reject if mistakenly used. args.push(zero); args.push(fb.ins().iconst(types::I64, (argc as i64).saturating_sub(1))); - args.push(arg_vals.get(0).copied().map(|v| to_i64(&mut fb, v)).unwrap_or(zero)); - args.push(arg_vals.get(1).copied().map(|v| to_i64(&mut fb, v)).unwrap_or(zero)); + args.push( + arg_vals + .get(0) + .copied() + .map(|v| to_i64(&mut fb, v)) + .unwrap_or(zero), + ); + args.push( + arg_vals + .get(1) + .copied() + .map(|v| to_i64(&mut fb, v)) + .unwrap_or(zero), + ); let call_inst = fb.ins().call(fref, &args); - if has_ret { if let Some(v) = fb.inst_results(call_inst).get(0).copied() { self.value_stack.push(v); self.value_tags.push(ValueTag::I64); } } + if has_ret { + if let Some(v) = fb.inst_results(call_inst).get(0).copied() { + self.value_stack.push(v); + self.value_tags.push(ValueTag::I64); + } + } } } diff --git a/src/jit/lower/builder/rt_shims.rs b/src/jit/lower/builder/rt_shims.rs index a087a715..9529f5e5 100644 --- a/src/jit/lower/builder/rt_shims.rs +++ b/src/jit/lower/builder/rt_shims.rs @@ -2,7 +2,9 @@ // Runtime shims and helpers used by the Cranelift JIT backend -pub(crate) extern "C" fn nyash_host_stub0() -> i64 { 0 } +pub(crate) extern "C" fn nyash_host_stub0() -> i64 { + 0 +} pub(crate) extern "C" fn nyash_jit_dbg_i64(tag: i64, val: i64) -> i64 { eprintln!("[JIT-DBG] tag={} val={}", tag, val); @@ -13,15 +15,25 @@ pub(crate) extern "C" fn nyash_jit_block_enter(idx: i64) { eprintln!("[JIT-BLOCK] enter={}", idx); } -pub(crate) extern "C" fn nyash_plugin_invoke3_i64(type_id: i64, method_id: i64, argc: i64, a0: i64, a1: i64, a2: i64) -> i64 { +pub(crate) extern "C" fn nyash_plugin_invoke3_i64( + type_id: i64, + method_id: i64, + argc: i64, + a0: i64, + a1: i64, + a2: i64, +) -> i64 { use crate::runtime::plugin_loader_v2::PluginBoxV2; let trace = crate::jit::observe::trace_enabled(); crate::jit::events::emit_runtime( serde_json::json!({ "id": "shim.enter.i64", "type_id": type_id, "method_id": method_id, "argc": argc }), - "shim", "" + "shim", + "", ); let mut instance_id: u32 = 0; - let mut invoke: Optioni32> = None; + let mut invoke: Option< + unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32, + > = None; if a0 > 0 { if let Some(obj) = crate::jit::rt::handles::get(a0 as u64) { if let Some(p) = obj.as_any().downcast_ref::() { @@ -29,9 +41,17 @@ pub(crate) extern "C" fn nyash_plugin_invoke3_i64(type_id: i64, method_id: i64, invoke = Some(p.inner.invoke_fn); } else if method_id as u32 == 1 { if let Some(arr) = obj.as_any().downcast_ref::() { - if let Some(ib) = arr.length().as_any().downcast_ref::() { return ib.value; } + if let Some(ib) = arr + .length() + .as_any() + .downcast_ref::() + { + return ib.value; + } + } + if let Some(sb) = obj.as_any().downcast_ref::() { + return sb.value.len() as i64; } - if let Some(sb) = obj.as_any().downcast_ref::() { return sb.value.len() as i64; } } } } @@ -45,9 +65,17 @@ pub(crate) extern "C" fn nyash_plugin_invoke3_i64(type_id: i64, method_id: i64, if let Some(p) = b.as_any().downcast_ref::() { instance_id = p.instance_id(); invoke = Some(p.inner.invoke_fn); - } else if let Some(arr) = b.as_any().downcast_ref::() { + } else if let Some(arr) = + b.as_any().downcast_ref::() + { if method_id as u32 == 1 { - if let Some(ib) = arr.length().as_any().downcast_ref::() { native_array_len = Some(ib.value); } + if let Some(ib) = arr + .length() + .as_any() + .downcast_ref::() + { + native_array_len = Some(ib.value); + } } } } @@ -56,10 +84,13 @@ pub(crate) extern "C" fn nyash_plugin_invoke3_i64(type_id: i64, method_id: i64, } if invoke.is_none() { if let Some(v) = native_array_len { - if trace { eprintln!("[JIT-SHIM i64] native_fallback return {}", v); } + if trace { + eprintln!("[JIT-SHIM i64] native_fallback return {}", v); + } crate::jit::events::emit_runtime( serde_json::json!({ "id": "shim.native.i64", "type_id": type_id, "method_id": method_id, "argc": argc, "ret": v }), - "shim", "" + "shim", + "", ); return v; } @@ -78,26 +109,104 @@ pub(crate) extern "C" fn nyash_plugin_invoke3_i64(type_id: i64, method_id: i64, } }); } - if invoke.is_none() { return 0; } - let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header((argc.saturating_sub(1).max(0) as u16)); - let mut add_i64 = |v: i64| { crate::runtime::plugin_ffi_common::encode::i64(&mut buf, v); }; - if argc >= 2 { add_i64(a1); } - if argc >= 3 { add_i64(a2); } + if invoke.is_none() { + return 0; + } + let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header( + (argc.saturating_sub(1).max(0) as u16), + ); + let mut add_i64 = |v: i64| { + crate::runtime::plugin_ffi_common::encode::i64(&mut buf, v); + }; + if argc >= 2 { + add_i64(a1); + } + if argc >= 3 { + add_i64(a2); + } let mut out = vec![0xCDu8; 4096 + 32]; let canary_val = 0xABu8; let canary_len = 16usize; - for i in 0..canary_len { out[i] = canary_val; } - for i in 0..canary_len { out[4096 + canary_len + i] = canary_val; } + for i in 0..canary_len { + out[i] = canary_val; + } + for i in 0..canary_len { + out[4096 + canary_len + i] = canary_val; + } let mut out_len: usize = 0; - let ok = unsafe { (invoke.unwrap())(instance_id, type_id as u32, method_id as u32, buf.as_ptr(), buf.len(), out.as_mut_ptr().add(canary_len), &mut out_len as *mut usize) }; + let ok = unsafe { + (invoke.unwrap())( + instance_id, + type_id as u32, + method_id as u32, + buf.as_ptr(), + buf.len(), + out.as_mut_ptr().add(canary_len), + &mut out_len as *mut usize, + ) + }; if ok != 0 { let out_slice = &out[canary_len..(canary_len + out_len.min(4096))]; - if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) { + if let Some((tag, _sz, payload)) = + crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) + { match tag { - 3 => { if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { return v as i64; } if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } } - 8 => { if _sz == 8 { let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); let r_type = u32::from_le_bytes(t); let r_inst = u32::from_le_bytes(i); let meta_opt = crate::runtime::plugin_loader_v2::metadata_for_type_id(r_type); let (box_type_name, invoke_ptr) = if let Some(meta) = meta_opt { (meta.box_type.clone(), meta.invoke_fn) } else { ("PluginBox".to_string(), invoke.unwrap()) }; let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2(box_type_name, r_type, r_inst, invoke_ptr); let arc: std::sync::Arc = std::sync::Arc::new(pb); let h = crate::jit::rt::handles::to_handle(arc); return h as i64; } } - 1 => { return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; } - 5 => { if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") { if _sz == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); let f = f64::from_le_bytes(b); return f as i64; } } } + 3 => { + if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { + return v as i64; + } + if payload.len() == 8 { + let mut b = [0u8; 8]; + b.copy_from_slice(payload); + return i64::from_le_bytes(b); + } + } + 8 => { + if _sz == 8 { + let mut t = [0u8; 4]; + t.copy_from_slice(&payload[0..4]); + let mut i = [0u8; 4]; + i.copy_from_slice(&payload[4..8]); + let r_type = u32::from_le_bytes(t); + let r_inst = u32::from_le_bytes(i); + let meta_opt = + crate::runtime::plugin_loader_v2::metadata_for_type_id(r_type); + let (box_type_name, invoke_ptr) = if let Some(meta) = meta_opt { + (meta.box_type.clone(), meta.invoke_fn) + } else { + ("PluginBox".to_string(), invoke.unwrap()) + }; + let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2( + box_type_name, + r_type, + r_inst, + invoke_ptr, + ); + let arc: std::sync::Arc = + std::sync::Arc::new(pb); + let h = crate::jit::rt::handles::to_handle(arc); + return h as i64; + } + } + 1 => { + return if crate::runtime::plugin_ffi_common::decode::bool(payload) + .unwrap_or(false) + { + 1 + } else { + 0 + }; + } + 5 => { + if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") { + if _sz == 8 { + let mut b = [0u8; 8]; + b.copy_from_slice(payload); + let f = f64::from_le_bytes(b); + return f as i64; + } + } + } _ => {} } } @@ -105,13 +214,32 @@ pub(crate) extern "C" fn nyash_plugin_invoke3_i64(type_id: i64, method_id: i64, 0 } -pub(crate) extern "C" fn nyash_plugin_invoke3_f64(_type_id: i64, _method_id: i64, _argc: i64, _a0: i64, _a1: i64, _a2: i64) -> f64 { 0.0 } +pub(crate) extern "C" fn nyash_plugin_invoke3_f64( + _type_id: i64, + _method_id: i64, + _argc: i64, + _a0: i64, + _a1: i64, + _a2: i64, +) -> f64 { + 0.0 +} // === By-name plugin shims (i64) === -pub(crate) extern "C" fn nyash_plugin_invoke_name_getattr_i64(argc: i64, a0: i64, a1: i64, a2: i64) -> i64 { +pub(crate) extern "C" fn nyash_plugin_invoke_name_getattr_i64( + argc: i64, + a0: i64, + a1: i64, + a2: i64, +) -> i64 { nyash_plugin_invoke_name_common_i64("getattr", argc, a0, a1, a2) } -pub(crate) extern "C" fn nyash_plugin_invoke_name_call_i64(argc: i64, a0: i64, a1: i64, a2: i64) -> i64 { +pub(crate) extern "C" fn nyash_plugin_invoke_name_call_i64( + argc: i64, + a0: i64, + a1: i64, + a2: i64, +) -> i64 { nyash_plugin_invoke_name_common_i64("call", argc, a0, a1, a2) } @@ -120,17 +248,30 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64 let mut instance_id: u32 = 0; let mut type_id: u32 = 0; let mut box_type: Option = None; - let mut invoke: Optioni32> = None; + let mut invoke: Option< + unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32, + > = None; if a0 > 0 { if let Some(obj) = crate::jit::rt::handles::get(a0 as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { instance_id = p.instance_id(); type_id = p.inner.type_id; box_type = Some(p.box_type.clone()); invoke = Some(p.inner.invoke_fn); } + if let Some(p) = obj.as_any().downcast_ref::() { + instance_id = p.instance_id(); + type_id = p.inner.type_id; + box_type = Some(p.box_type.clone()); + invoke = Some(p.inner.invoke_fn); + } } } - if invoke.is_none() && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") { + if invoke.is_none() && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") + { crate::jit::rt::with_legacy_vm_args(|args| { let idx = a0.max(0) as usize; if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) { - if let Some(p) = b.as_any().downcast_ref::() { instance_id = p.instance_id(); type_id = p.inner.type_id; box_type = Some(p.box_type.clone()); invoke = Some(p.inner.invoke_fn); } + if let Some(p) = b.as_any().downcast_ref::() { + instance_id = p.instance_id(); + type_id = p.inner.type_id; + box_type = Some(p.box_type.clone()); + invoke = Some(p.inner.invoke_fn); + } } }); } @@ -138,42 +279,133 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64 crate::jit::rt::with_legacy_vm_args(|args| { for v in args.iter() { if let crate::backend::vm::VMValue::BoxRef(b) = v { - if let Some(p) = b.as_any().downcast_ref::() { instance_id = p.instance_id(); type_id = p.inner.type_id; box_type = Some(p.box_type.clone()); invoke = Some(p.inner.invoke_fn); break; } + if let Some(p) = b.as_any().downcast_ref::() { + instance_id = p.instance_id(); + type_id = p.inner.type_id; + box_type = Some(p.box_type.clone()); + invoke = Some(p.inner.invoke_fn); + break; + } } } }); } - if invoke.is_none() { return 0; } + if invoke.is_none() { + return 0; + } let box_type = box_type.unwrap_or_default(); - let mh = if let Ok(host) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() { host.resolve_method(&box_type, method) } else { return 0 }; - let method_id = match mh { Ok(h) => h.method_id, Err(_) => return 0 } as u32; - let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header((argc.saturating_sub(1).max(0) as u16)); + let mh = + if let Ok(host) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() { + host.resolve_method(&box_type, method) + } else { + return 0; + }; + let method_id = match mh { + Ok(h) => h.method_id, + Err(_) => return 0, + } as u32; + let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header( + (argc.saturating_sub(1).max(0) as u16), + ); let mut add_from_legacy = |pos: usize| { crate::jit::rt::with_legacy_vm_args(|args| { if let Some(v) = args.get(pos) { match v { - crate::backend::vm::VMValue::Integer(i) => crate::runtime::plugin_ffi_common::encode::i64(&mut buf, *i), - crate::backend::vm::VMValue::Float(f) => crate::runtime::plugin_ffi_common::encode::f64(&mut buf, *f), - crate::backend::vm::VMValue::Bool(b) => crate::runtime::plugin_ffi_common::encode::bool(&mut buf, *b), - crate::backend::vm::VMValue::BoxRef(_) => { crate::runtime::plugin_ffi_common::encode::i64(&mut buf, 0); }, + crate::backend::vm::VMValue::Integer(i) => { + crate::runtime::plugin_ffi_common::encode::i64(&mut buf, *i) + } + crate::backend::vm::VMValue::Float(f) => { + crate::runtime::plugin_ffi_common::encode::f64(&mut buf, *f) + } + crate::backend::vm::VMValue::Bool(b) => { + crate::runtime::plugin_ffi_common::encode::bool(&mut buf, *b) + } + crate::backend::vm::VMValue::BoxRef(_) => { + crate::runtime::plugin_ffi_common::encode::i64(&mut buf, 0); + } _ => {} } } }); }; - if argc >= 2 { add_from_legacy(1); } - if argc >= 3 { add_from_legacy(2); } + if argc >= 2 { + add_from_legacy(1); + } + if argc >= 3 { + add_from_legacy(2); + } let mut out = vec![0u8; 4096]; let mut out_len: usize = 0; - let ok = unsafe { (invoke.unwrap())(instance_id, type_id as u32, method_id as u32, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len as *mut usize) }; + let ok = unsafe { + (invoke.unwrap())( + instance_id, + type_id as u32, + method_id as u32, + buf.as_ptr(), + buf.len(), + out.as_mut_ptr(), + &mut out_len as *mut usize, + ) + }; if ok != 0 { let out_slice = &out[0..out_len.min(4096)]; - if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) { + if let Some((tag, _sz, payload)) = + crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) + { match tag { - 3 => { if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { return v as i64; } } - 8 => { if _sz == 8 { let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); let r_type = u32::from_le_bytes(t); let r_inst = u32::from_le_bytes(i); let meta_opt = crate::runtime::plugin_loader_v2::metadata_for_type_id(r_type); let (meta_box, invoke_ptr) = if let Some(meta) = meta_opt { (meta.box_type, meta.invoke_fn) } else { (box_type.clone(), invoke.unwrap()) }; let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2(meta_box, r_type, r_inst, invoke_ptr); let arc: std::sync::Arc = std::sync::Arc::new(pb); let h = crate::jit::rt::handles::to_handle(arc); return h as i64; } } - 1 => { return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; } - 5 => { if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") { if _sz == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); let f = f64::from_le_bytes(b); return f as i64; } } } + 3 => { + if payload.len() == 8 { + let mut b = [0u8; 8]; + b.copy_from_slice(payload); + return i64::from_le_bytes(b); + } + if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { + return v as i64; + } + } + 8 => { + if _sz == 8 { + let mut t = [0u8; 4]; + t.copy_from_slice(&payload[0..4]); + let mut i = [0u8; 4]; + i.copy_from_slice(&payload[4..8]); + let r_type = u32::from_le_bytes(t); + let r_inst = u32::from_le_bytes(i); + let meta_opt = + crate::runtime::plugin_loader_v2::metadata_for_type_id(r_type); + let (meta_box, invoke_ptr) = if let Some(meta) = meta_opt { + (meta.box_type, meta.invoke_fn) + } else { + (box_type.clone(), invoke.unwrap()) + }; + let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2( + meta_box, r_type, r_inst, invoke_ptr, + ); + let arc: std::sync::Arc = + std::sync::Arc::new(pb); + let h = crate::jit::rt::handles::to_handle(arc); + return h as i64; + } + } + 1 => { + return if crate::runtime::plugin_ffi_common::decode::bool(payload) + .unwrap_or(false) + { + 1 + } else { + 0 + }; + } + 5 => { + if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") { + if _sz == 8 { + let mut b = [0u8; 8]; + b.copy_from_slice(payload); + let f = f64::from_le_bytes(b); + return f as i64; + } + } + } _ => {} } } diff --git a/src/jit/lower/builder/tls.rs b/src/jit/lower/builder/tls.rs index e0184602..08360915 100644 --- a/src/jit/lower/builder/tls.rs +++ b/src/jit/lower/builder/tls.rs @@ -1,7 +1,7 @@ #![cfg(feature = "cranelift-jit")] -use cranelift_module::Module; use cranelift_codegen::ir::InstBuilder; +use cranelift_module::Module; // TLS: 単一関数あたり1つの FunctionBuilder を保持(jit-direct 専用) pub(crate) mod clif_tls { @@ -16,15 +16,25 @@ pub(crate) mod clif_tls { } impl TlsCtx { pub fn new() -> Self { - Self { ctx: Box::new(cranelift_codegen::Context::new()), fbc: Box::new(cranelift_frontend::FunctionBuilderContext::new()), fb: core::ptr::null_mut() } + Self { + ctx: Box::new(cranelift_codegen::Context::new()), + fbc: Box::new(cranelift_frontend::FunctionBuilderContext::new()), + fb: core::ptr::null_mut(), + } } pub unsafe fn create(&mut self) { let func_ptr: *mut cranelift_codegen::ir::Function = &mut self.ctx.func; let fbc_ptr: *mut cranelift_frontend::FunctionBuilderContext = &mut *self.fbc; - let fb = Box::new(cranelift_frontend::FunctionBuilder::new(&mut *func_ptr, &mut *fbc_ptr)); + let fb = Box::new(cranelift_frontend::FunctionBuilder::new( + &mut *func_ptr, + &mut *fbc_ptr, + )); self.fb = Box::into_raw(fb); } - pub fn with(&mut self, f: impl FnOnce(&mut cranelift_frontend::FunctionBuilder<'static>) -> R) -> R { + pub fn with( + &mut self, + f: impl FnOnce(&mut cranelift_frontend::FunctionBuilder<'static>) -> R, + ) -> R { unsafe { f(&mut *self.fb) } } pub unsafe fn finalize_drop(&mut self) { @@ -36,7 +46,9 @@ pub(crate) mod clif_tls { } /// Finalize the current FunctionBuilder and take ownership of the underlying Context. pub fn take_context(&mut self) -> cranelift_codegen::Context { - unsafe { self.finalize_drop(); } + unsafe { + self.finalize_drop(); + } // Move the current context out and replace with a fresh one let old = std::mem::replace(&mut self.ctx, Box::new(cranelift_codegen::Context::new())); *old @@ -69,7 +81,11 @@ pub(crate) fn tls_call_import_ret( } let fref = module.declare_func_in_func(func_id, fb.func); let call_inst = fb.ins().call(fref, args); - if has_ret { fb.inst_results(call_inst).get(0).copied() } else { None } + if has_ret { + fb.inst_results(call_inst).get(0).copied() + } else { + None + } }) }) } @@ -87,11 +103,17 @@ pub(crate) fn tls_call_import_with_iconsts( let tls = opt.as_mut().expect("FunctionBuilder TLS not initialized"); tls.with(|fb| { let mut all_args: Vec = Vec::new(); - for &c in iconsts { all_args.push(fb.ins().iconst(types::I64, c)); } + for &c in iconsts { + all_args.push(fb.ins().iconst(types::I64, c)); + } all_args.extend_from_slice(tail_args); let fref = module.declare_func_in_func(func_id, fb.func); let call_inst = fb.ins().call(fref, &all_args); - if has_ret { fb.inst_results(call_inst).get(0).copied() } else { None } + if has_ret { + fb.inst_results(call_inst).get(0).copied() + } else { + None + } }) }) } diff --git a/src/jit/lower/cfg_dot.rs b/src/jit/lower/cfg_dot.rs index e80cbb95..68abd9ef 100644 --- a/src/jit/lower/cfg_dot.rs +++ b/src/jit/lower/cfg_dot.rs @@ -1,15 +1,23 @@ -pub fn dump_cfg_dot(func: &crate::mir::MirFunction, path: &str, phi_min: bool) -> std::io::Result<()> { +pub fn dump_cfg_dot( + func: &crate::mir::MirFunction, + path: &str, + phi_min: bool, +) -> std::io::Result<()> { let mut out = String::new(); out.push_str(&format!("digraph \"{}\" {{\n", func.signature.name)); out.push_str(" node [shape=box, fontsize=10];\n"); // Derive simple bool sets: compare dsts are bool; phi of all-bool inputs are bool - let mut bool_values: std::collections::HashSet = std::collections::HashSet::new(); + let mut bool_values: std::collections::HashSet = + std::collections::HashSet::new(); for (_bb_id, bb) in func.blocks.iter() { for ins in bb.instructions.iter() { - if let crate::mir::MirInstruction::Compare { dst, .. } = ins { bool_values.insert(*dst); } + if let crate::mir::MirInstruction::Compare { dst, .. } = ins { + bool_values.insert(*dst); + } } } - let mut bool_phi: std::collections::HashSet = std::collections::HashSet::new(); + let mut bool_phi: std::collections::HashSet = + std::collections::HashSet::new(); if phi_min { for (_bb_id, bb) in func.blocks.iter() { for ins in bb.instructions.iter() { @@ -27,14 +35,30 @@ pub fn dump_cfg_dot(func: &crate::mir::MirFunction, path: &str, phi_min: bool) - // Emit nodes with labels for bb_id in bb_ids.iter() { let bb = func.blocks.get(bb_id).unwrap(); - let phi_count = bb.instructions.iter().filter(|ins| matches!(ins, crate::mir::MirInstruction::Phi { .. })).count(); - let phi_b1_count = bb.instructions.iter().filter(|ins| match ins { crate::mir::MirInstruction::Phi { dst, .. } => bool_phi.contains(dst), _ => false }).count(); + let phi_count = bb + .instructions + .iter() + .filter(|ins| matches!(ins, crate::mir::MirInstruction::Phi { .. })) + .count(); + let phi_b1_count = bb + .instructions + .iter() + .filter(|ins| match ins { + crate::mir::MirInstruction::Phi { dst, .. } => bool_phi.contains(dst), + _ => false, + }) + .count(); let mut label = format!("bb{}", bb_id.0); if phi_min && phi_count > 0 { - if phi_b1_count > 0 { label = format!("{}\\nphi:{} (b1:{})", label, phi_count, phi_b1_count); } - else { label = format!("{}\\nphi:{}", label, phi_count); } + if phi_b1_count > 0 { + label = format!("{}\\nphi:{} (b1:{})", label, phi_count, phi_b1_count); + } else { + label = format!("{}\\nphi:{}", label, phi_count); + } + } + if *bb_id == func.entry_block { + label = format!("{}\\nENTRY", label); } - if *bb_id == func.entry_block { label = format!("{}\\nENTRY", label); } out.push_str(&format!(" n{} [label=\"{}\"];\n", bb_id.0, label)); } // Emit edges based on terminators @@ -45,10 +69,18 @@ pub fn dump_cfg_dot(func: &crate::mir::MirFunction, path: &str, phi_min: bool) - crate::mir::MirInstruction::Jump { target } => { out.push_str(&format!(" n{} -> n{};\n", bb_id.0, target.0)); } - crate::mir::MirInstruction::Branch { then_bb, else_bb, .. } => { + crate::mir::MirInstruction::Branch { + then_bb, else_bb, .. + } => { // Branch condition is boolean (b1) - out.push_str(&format!(" n{} -> n{} [label=\"then cond:b1\"];\n", bb_id.0, then_bb.0)); - out.push_str(&format!(" n{} -> n{} [label=\"else cond:b1\"];\n", bb_id.0, else_bb.0)); + out.push_str(&format!( + " n{} -> n{} [label=\"then cond:b1\"];\n", + bb_id.0, then_bb.0 + )); + out.push_str(&format!( + " n{} -> n{} [label=\"else cond:b1\"];\n", + bb_id.0, else_bb.0 + )); } _ => {} } diff --git a/src/jit/lower/core.rs b/src/jit/lower/core.rs index b2e0a099..d6929646 100644 --- a/src/jit/lower/core.rs +++ b/src/jit/lower/core.rs @@ -1,5 +1,5 @@ -use crate::mir::{MirFunction, MirInstruction, ConstValue, ValueId}; -use super::builder::{IRBuilder, BinOpKind}; +use super::builder::{BinOpKind, IRBuilder}; +use crate::mir::{ConstValue, MirFunction, MirInstruction, ValueId}; mod analysis; mod cfg; @@ -22,7 +22,8 @@ pub struct LowerCore { /// Track values produced by Phi (for minimal PHI path) pub(super) phi_values: std::collections::HashSet, /// Map (block, phi dst) -> param index in that block (for multi-PHI) - pub(super) phi_param_index: std::collections::HashMap<(crate::mir::BasicBlockId, ValueId), usize>, + pub(super) phi_param_index: + std::collections::HashMap<(crate::mir::BasicBlockId, ValueId), usize>, /// Track values that are boolean (b1) results, e.g., Compare destinations pub(super) bool_values: std::collections::HashSet, /// Track PHI destinations that are boolean (all inputs derived from bool_values) @@ -45,17 +46,51 @@ pub struct LowerCore { } impl LowerCore { - pub fn new() -> Self { Self { unsupported: 0, covered: 0, known_i64: std::collections::HashMap::new(), known_f64: std::collections::HashMap::new(), known_str: std::collections::HashMap::new(), param_index: std::collections::HashMap::new(), phi_values: std::collections::HashSet::new(), phi_param_index: std::collections::HashMap::new(), bool_values: std::collections::HashSet::new(), bool_phi_values: std::collections::HashSet::new(), float_box_values: std::collections::HashSet::new(), handle_values: std::collections::HashSet::new(), last_phi_total: 0, last_phi_b1: 0, last_ret_bool_hint_used: false, local_index: std::collections::HashMap::new(), next_local: 0, box_type_map: std::collections::HashMap::new(), string_box_literal: std::collections::HashMap::new() } } + pub fn new() -> Self { + Self { + unsupported: 0, + covered: 0, + known_i64: std::collections::HashMap::new(), + known_f64: std::collections::HashMap::new(), + known_str: std::collections::HashMap::new(), + param_index: std::collections::HashMap::new(), + phi_values: std::collections::HashSet::new(), + phi_param_index: std::collections::HashMap::new(), + bool_values: std::collections::HashSet::new(), + bool_phi_values: std::collections::HashSet::new(), + float_box_values: std::collections::HashSet::new(), + handle_values: std::collections::HashSet::new(), + last_phi_total: 0, + last_phi_b1: 0, + last_ret_bool_hint_used: false, + local_index: std::collections::HashMap::new(), + next_local: 0, + box_type_map: std::collections::HashMap::new(), + string_box_literal: std::collections::HashMap::new(), + } + } /// Get statistics for the last lowered function - pub fn last_stats(&self) -> (u64, u64, bool) { (self.last_phi_total, self.last_phi_b1, self.last_ret_bool_hint_used) } + pub fn last_stats(&self) -> (u64, u64, bool) { + ( + self.last_phi_total, + self.last_phi_b1, + self.last_ret_bool_hint_used, + ) + } /// Walk the MIR function and count supported/unsupported instructions. /// In the future, this will build CLIF via Cranelift builders. - pub fn lower_function(&mut self, func: &MirFunction, builder: &mut dyn IRBuilder) -> Result<(), String> { + pub fn lower_function( + &mut self, + func: &MirFunction, + builder: &mut dyn IRBuilder, + ) -> Result<(), String> { // Prepare ABI based on MIR signature // Reset per-function stats - self.last_phi_total = 0; self.last_phi_b1 = 0; self.last_ret_bool_hint_used = false; + self.last_phi_total = 0; + self.last_phi_b1 = 0; + self.last_ret_bool_hint_used = false; // Build param index map self.param_index.clear(); for (i, v) in func.params.iter().copied().enumerate() { @@ -71,7 +106,9 @@ impl LowerCore { if let Some(block) = func.blocks.get(bb) { for ins in block.instructions.iter() { if let crate::mir::MirInstruction::Const { dst, value } = ins { - if let crate::mir::ConstValue::String(s) = value { self.known_str.insert(*dst, s.clone()); } + if let crate::mir::ConstValue::String(s) = value { + self.known_str.insert(*dst, s.clone()); + } } } } @@ -81,8 +118,10 @@ impl LowerCore { let cfg_now = crate::jit::config::current(); let enable_phi_min = cfg_now.phi_min; // Build successor → phi order and predeclare block params - let succ_phi_order: std::collections::HashMap> = - self.build_phi_succords(func, &bb_ids, builder, enable_phi_min); + let succ_phi_order: std::collections::HashMap< + crate::mir::BasicBlockId, + Vec, + > = self.build_phi_succords(func, &bb_ids, builder, enable_phi_min); // Decide ABI: typed or i64-only let native_f64 = cfg_now.native_f64; let native_bool = cfg_now.native_bool; @@ -90,16 +129,23 @@ impl LowerCore { let mut kinds: Vec = Vec::new(); for mt in func.signature.params.iter() { let k = match mt { - crate::mir::MirType::Float if native_f64 => { use_typed = true; super::builder::ParamKind::F64 } - crate::mir::MirType::Bool if native_bool => { use_typed = true; super::builder::ParamKind::B1 } + crate::mir::MirType::Float if native_f64 => { + use_typed = true; + super::builder::ParamKind::F64 + } + crate::mir::MirType::Bool if native_bool => { + use_typed = true; + super::builder::ParamKind::B1 + } _ => super::builder::ParamKind::I64, }; kinds.push(k); } - let ret_is_f64 = native_f64 && matches!(func.signature.return_type, crate::mir::MirType::Float); + let ret_is_f64 = + native_f64 && matches!(func.signature.return_type, crate::mir::MirType::Float); // Hint return bool footing (no-op in current backend; keeps switch point centralized) let ret_is_bool = matches!(func.signature.return_type, crate::mir::MirType::Bool); - if ret_is_bool { + if ret_is_bool { builder.hint_ret_bool(true); // Track how many functions are lowered with boolean return hint (for stats) crate::jit::rt::ret_bool_hint_inc(1); @@ -107,7 +153,10 @@ impl LowerCore { } let has_ret = !matches!(func.signature.return_type, crate::mir::MirType::Void); if std::env::var("NYASH_JIT_TRACE_SIG").ok().as_deref() == Some("1") { - eprintln!("[SIG-CORE] ret_type={:?} has_ret={} use_typed={} ret_is_f64={}", func.signature.return_type, has_ret, use_typed, ret_is_f64); + eprintln!( + "[SIG-CORE] ret_type={:?} has_ret={} use_typed={} ret_is_f64={}", + func.signature.return_type, has_ret, use_typed, ret_is_f64 + ); } if use_typed || ret_is_f64 { builder.prepare_signature_typed(&kinds, ret_is_f64 && has_ret); @@ -119,8 +168,16 @@ impl LowerCore { for bb in bb_ids.iter() { if let Some(block) = func.blocks.get(bb) { for ins in block.instructions.iter() { - if let crate::mir::MirInstruction::NewBox { dst, box_type, .. } = ins { if box_type == "FloatBox" { self.float_box_values.insert(*dst); } } - if let crate::mir::MirInstruction::Copy { dst, src } = ins { if self.float_box_values.contains(src) { self.float_box_values.insert(*dst); } } + if let crate::mir::MirInstruction::NewBox { dst, box_type, .. } = ins { + if box_type == "FloatBox" { + self.float_box_values.insert(*dst); + } + } + if let crate::mir::MirInstruction::Copy { dst, src } = ins { + if self.float_box_values.contains(src) { + self.float_box_values.insert(*dst); + } + } } } } @@ -134,15 +191,26 @@ impl LowerCore { if let crate::mir::MirInstruction::NewBox { dst, box_type, .. } = ins { self.box_type_map.insert(*dst, box_type.clone()); } - if let crate::mir::MirInstruction::NewBox { dst, box_type, args } = ins { + if let crate::mir::MirInstruction::NewBox { + dst, + box_type, + args, + } = ins + { if box_type == "StringBox" && args.len() == 1 { let src = args[0]; - if let Some(s) = self.known_str.get(&src).cloned() { self.string_box_literal.insert(*dst, s); } + if let Some(s) = self.known_str.get(&src).cloned() { + self.string_box_literal.insert(*dst, s); + } } } if let crate::mir::MirInstruction::Copy { dst, src } = ins { - if let Some(name) = self.box_type_map.get(src).cloned() { self.box_type_map.insert(*dst, name); } - if let Some(s) = self.string_box_literal.get(src).cloned() { self.string_box_literal.insert(*dst, s); } + if let Some(name) = self.box_type_map.get(src).cloned() { + self.box_type_map.insert(*dst, name); + } + if let Some(s) = self.string_box_literal.get(src).cloned() { + self.string_box_literal.insert(*dst, s); + } } } } @@ -165,7 +233,9 @@ impl LowerCore { if let crate::mir::MirInstruction::Phi { dst, inputs } = ins { local_phi_order.push(*dst); // decide if this phi is boolean - if inputs.iter().all(|(_, v)| self.bool_values.contains(v)) && !inputs.is_empty() { + if inputs.iter().all(|(_, v)| self.bool_values.contains(v)) + && !inputs.is_empty() + { self.bool_phi_values.insert(*dst); } } @@ -178,27 +248,61 @@ impl LowerCore { } } } - for instr in bb.instructions.iter() { + for instr in bb.instructions.iter() { self.cover_if_supported(instr); - if let Err(e) = self.try_emit(builder, instr, *bb_id, func) { return Err(e); } + if let Err(e) = self.try_emit(builder, instr, *bb_id, func) { + return Err(e); + } // Track FloatBox creations for later arg classification - if let crate::mir::MirInstruction::NewBox { dst, box_type, .. } = instr { if box_type == "FloatBox" { self.float_box_values.insert(*dst); } } - if let crate::mir::MirInstruction::Copy { dst, src } = instr { if self.float_box_values.contains(src) { self.float_box_values.insert(*dst); } } + if let crate::mir::MirInstruction::NewBox { dst, box_type, .. } = instr { + if box_type == "FloatBox" { + self.float_box_values.insert(*dst); + } + } + if let crate::mir::MirInstruction::Copy { dst, src } = instr { + if self.float_box_values.contains(src) { + self.float_box_values.insert(*dst); + } + } } if let Some(term) = &bb.terminator { self.cover_if_supported(term); // Branch/Jump need block mapping: pass indices match term { - crate::mir::MirInstruction::Branch { condition, then_bb, else_bb } => { - self.lower_branch_terminator(builder, func, &bb_ids, *bb_id, condition, then_bb, else_bb, &succ_phi_order, enable_phi_min); + crate::mir::MirInstruction::Branch { + condition, + then_bb, + else_bb, + } => { + self.lower_branch_terminator( + builder, + func, + &bb_ids, + *bb_id, + condition, + then_bb, + else_bb, + &succ_phi_order, + enable_phi_min, + ); } crate::mir::MirInstruction::Jump { target } => { - self.lower_jump_terminator(builder, func, &bb_ids, *bb_id, target, &succ_phi_order, enable_phi_min); + self.lower_jump_terminator( + builder, + func, + &bb_ids, + *bb_id, + target, + &succ_phi_order, + enable_phi_min, + ); } _ => { /* other terminators handled via generic emission below */ } } // Also allow other terminators to be emitted if needed - if let Err(e) = self.try_emit(builder, term, *bb_id, func) { return Err(e); } + if let Err(e) = self.try_emit(builder, term, *bb_id, func) { + return Err(e); + } } } builder.end_function(); @@ -209,11 +313,20 @@ impl LowerCore { // string_len helper moved to core/string_len.rs (no behavior change) - - fn try_emit(&mut self, b: &mut dyn IRBuilder, instr: &MirInstruction, cur_bb: crate::mir::BasicBlockId, func: &crate::mir::MirFunction) -> Result<(), String> { + fn try_emit( + &mut self, + b: &mut dyn IRBuilder, + instr: &MirInstruction, + cur_bb: crate::mir::BasicBlockId, + func: &crate::mir::MirFunction, + ) -> Result<(), String> { use crate::mir::MirInstruction as I; match instr { - I::NewBox { dst, box_type, args } => { + I::NewBox { + dst, + box_type, + args, + } => { // Materialize StringBox handle at lowering time when literal is known. // This enables subsequent BoxCall(len/length) to use a valid runtime handle. if box_type == "StringBox" && args.len() == 1 { @@ -221,7 +334,11 @@ impl LowerCore { // Try from pre-seeded known_str (scanned at function entry) if let Some(s) = self.known_str.get(&src).cloned() { b.emit_string_handle_from_literal(&s); - let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); self.handle_values.insert(*dst); } @@ -231,28 +348,45 @@ impl LowerCore { let name = box_type.as_str(); let bytes = name.as_bytes(); let take = core::cmp::min(16, bytes.len()); - let mut lo: u64 = 0; let mut hi: u64 = 0; - for i in 0..take.min(8) { lo |= (bytes[i] as u64) << (8 * i as u32); } - for i in 8..take { hi |= (bytes[i] as u64) << (8 * (i - 8) as u32); } + let mut lo: u64 = 0; + let mut hi: u64 = 0; + for i in 0..take.min(8) { + lo |= (bytes[i] as u64) << (8 * i as u32); + } + for i in 8..take { + hi |= (bytes[i] as u64) << (8 * (i - 8) as u32); + } // Push args and call import b.emit_const_i64(lo as i64); b.emit_const_i64(hi as i64); b.emit_const_i64(bytes.len() as i64); - b.emit_host_call(crate::jit::r#extern::birth::SYM_INSTANCE_BIRTH_NAME_U64X2, 3, true); + b.emit_host_call( + crate::jit::r#extern::birth::SYM_INSTANCE_BIRTH_NAME_U64X2, + 3, + true, + ); // Store handle to local slot - let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); self.handle_values.insert(*dst); // Track type for downstream boxcall routing self.box_type_map.insert(*dst, box_type.clone()); } } - I::Call { dst, func, args, .. } => { + I::Call { + dst, func, args, .. + } => { // FunctionBox call shim: emit hostcall nyash_fn_callN(func_h, args...) // Push function operand (param or known) self.push_value_if_known_or_param(b, func); // Push up to 4 args (unknown become iconst 0 via helper) - for a in args.iter() { self.push_value_if_known_or_param(b, a); } + for a in args.iter() { + self.push_value_if_known_or_param(b, a); + } // Choose symbol by arity let argc = args.len(); let sym = match argc { @@ -270,36 +404,62 @@ impl LowerCore { // Build param kinds vector: 1 (func) + argc (args) let mut params: Vec = Vec::new(); params.push(crate::jit::lower::builder::ParamKind::I64); - for _ in 0..core::cmp::min(argc, 8) { params.push(crate::jit::lower::builder::ParamKind::I64); } + for _ in 0..core::cmp::min(argc, 8) { + params.push(crate::jit::lower::builder::ParamKind::I64); + } b.emit_host_call_typed(sym, ¶ms, true, false); // Persist or discard the return to keep the stack balanced if let Some(d) = dst { self.handle_values.insert(*d); - let slot = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } else { // No destination: spill to scratch local to consume the value - let scratch = { let id = self.next_local; self.next_local += 1; id }; + let scratch = { + let id = self.next_local; + self.next_local += 1; + id + }; b.store_local_i64(scratch); } } I::Await { dst, future } => { // Push future param index when known; otherwise -1 to trigger legacy search in shim - if let Some(pidx) = self.param_index.get(future).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } + if let Some(pidx) = self.param_index.get(future).copied() { + b.emit_param_i64(pidx); + } else { + b.emit_const_i64(-1); + } // Call await_h to obtain a handle to the value (0 on timeout) b.emit_host_call(crate::jit::r#extern::r#async::SYM_FUTURE_AWAIT_H, 1, true); // Store the awaited handle temporarily - let hslot = { let id = self.next_local; self.next_local += 1; id }; + let hslot = { + let id = self.next_local; + self.next_local += 1; + id + }; b.store_local_i64(hslot); // Build Ok result: ok_h(handle) b.load_local_i64(hslot); b.emit_host_call(crate::jit::r#extern::result::SYM_RESULT_OK_H, 1, true); - let ok_slot = { let id = self.next_local; self.next_local += 1; id }; + let ok_slot = { + let id = self.next_local; + self.next_local += 1; + id + }; b.store_local_i64(ok_slot); // Build Err result: err_h(0) → Timeout b.emit_const_i64(0); b.emit_host_call(crate::jit::r#extern::result::SYM_RESULT_ERR_H, 1, true); - let err_slot = { let id = self.next_local; self.next_local += 1; id }; + let err_slot = { + let id = self.next_local; + self.next_local += 1; + id + }; b.store_local_i64(err_slot); // Cond: (handle == 0) b.load_local_i64(hslot); @@ -312,19 +472,31 @@ impl LowerCore { // Store selected Result handle to destination let d = *dst; self.handle_values.insert(d); - let slot = *self.local_index.entry(d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } I::Safepoint => { // Emit a runtime checkpoint (safepoint + scheduler poll via NyRT/JIT stubs) b.emit_host_call(crate::jit::r#extern::runtime::SYM_RT_CHECKPOINT, 0, false); } - I::RefGet { dst, reference: _, field } => { + I::RefGet { + dst, + reference: _, + field, + } => { // Minimal: env.console をハンドル化(hostcall) if field == "console" { // Emit hostcall to create/get ConsoleBox handle // Symbol exported by nyrt: nyash.console.birth_h - b.emit_host_call(crate::jit::r#extern::collections::SYM_CONSOLE_BIRTH_H, 0, true); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_CONSOLE_BIRTH_H, + 0, + true, + ); } else { // Unknown RefGet: treat as no-op const 0 to avoid strict fail for now b.emit_const_i64(0); @@ -332,7 +504,11 @@ impl LowerCore { // Record as covered; do not increment unsupported let _ = dst; // keep signature parity } - I::UnaryOp { dst: _, op, operand } => { + I::UnaryOp { + dst: _, + op, + operand, + } => { match op { crate::mir::UnaryOp::Neg => { // i64-only minimal: 0 - operand @@ -343,10 +519,16 @@ impl LowerCore { self.push_value_if_known_or_param(b, operand); b.emit_binop(BinOpKind::Sub); } - _ => { self.unsupported += 1; } + _ => { + self.unsupported += 1; + } } } - I::NewBox { dst, box_type, args } => { + I::NewBox { + dst, + box_type, + args, + } => { // 最適化は後段へ(現状は汎用・安全な実装に徹する) // 通常経路: // - 引数なし: 汎用 birth_h(type_idのみ)でハンドル生成 @@ -357,19 +539,37 @@ impl LowerCore { let name = box_type.clone(); { let name_bytes = name.as_bytes(); - let mut lo: u64 = 0; let mut hi: u64 = 0; + let mut lo: u64 = 0; + let mut hi: u64 = 0; let take = core::cmp::min(16, name_bytes.len()); - for i in 0..take.min(8) { lo |= (name_bytes[i] as u64) << (8 * i as u32); } - for i in 8..take { hi |= (name_bytes[i] as u64) << (8 * (i - 8) as u32); } + for i in 0..take.min(8) { + lo |= (name_bytes[i] as u64) << (8 * i as u32); + } + for i in 8..take { + hi |= (name_bytes[i] as u64) << (8 * (i - 8) as u32); + } // Push immediates b.emit_const_i64(lo as i64); b.emit_const_i64(hi as i64); b.emit_const_i64(name_bytes.len() as i64); // Call import (lo, hi, len) -> handle // Use typed hostcall (I64,I64,I64)->I64 - b.emit_host_call_typed(crate::jit::r#extern::birth::SYM_INSTANCE_BIRTH_NAME_U64X2, &[crate::jit::lower::builder::ParamKind::I64, crate::jit::lower::builder::ParamKind::I64, crate::jit::lower::builder::ParamKind::I64], true, false); + b.emit_host_call_typed( + crate::jit::r#extern::birth::SYM_INSTANCE_BIRTH_NAME_U64X2, + &[ + crate::jit::lower::builder::ParamKind::I64, + crate::jit::lower::builder::ParamKind::I64, + crate::jit::lower::builder::ParamKind::I64, + ], + true, + false, + ); self.handle_values.insert(*dst); - let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } } else { @@ -379,13 +579,22 @@ impl LowerCore { if let Some(src) = args.get(0) { if let Some(iv) = self.known_i64.get(src).copied() { // 汎用 birth_i64(type_id, argc=1, a1=iv) - if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, .. } = crate::jit::policy::invoke::decide_box_method(box_type, "birth", 1, true) { + if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { + type_id, + .. + } = crate::jit::policy::invoke::decide_box_method( + box_type, "birth", 1, true, + ) { b.emit_const_i64(type_id as i64); b.emit_const_i64(1); b.emit_const_i64(iv); b.emit_host_call("nyash.box.birth_i64", 3, true); self.handle_values.insert(*dst); - let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); // 値伝搬も継続 self.known_i64.insert(*dst, iv); @@ -401,19 +610,29 @@ impl LowerCore { let mut lit: Option = None; for (_bid, bb) in func.blocks.iter() { for ins in bb.instructions.iter() { - if let crate::mir::MirInstruction::Const { dst: cdst, value } = ins { + if let crate::mir::MirInstruction::Const { dst: cdst, value } = + ins + { if cdst == src { - if let crate::mir::ConstValue::String(s) = value { lit = Some(s.clone()); } + if let crate::mir::ConstValue::String(s) = value { + lit = Some(s.clone()); + } break; } } } - if lit.is_some() { break; } + if lit.is_some() { + break; + } } if let Some(s) = lit { b.emit_string_handle_from_literal(&s); self.handle_values.insert(*dst); - let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); return Ok(()); } @@ -421,21 +640,39 @@ impl LowerCore { } // 2) 引数がハンドル(StringBox等)で既に存在する場合(最大2引数) if args.len() <= 2 && args.iter().all(|a| self.handle_values.contains(a)) { - if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, .. } = crate::jit::policy::invoke::decide_box_method(box_type, "birth", args.len(), true) { + if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { + type_id, + .. + } = crate::jit::policy::invoke::decide_box_method( + box_type, + "birth", + args.len(), + true, + ) { b.emit_const_i64(type_id as i64); b.emit_const_i64(args.len() as i64); // a1, a2 を push(ローカルに保存済みのハンドルをロード) - for a in args.iter().take(2) { self.push_value_if_known_or_param(b, a); } + for a in args.iter().take(2) { + self.push_value_if_known_or_param(b, a); + } b.emit_host_call("nyash.box.birth_i64", 2 + args.len(), true); self.handle_values.insert(*dst); - let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); return Ok(()); } } // フォールバック: 既存チェーンに委譲(互換)+ 既知値伝搬のみ if box_type == "IntegerBox" { - if let Some(src) = args.get(0) { if let Some(iv) = self.known_i64.get(src).copied() { self.known_i64.insert(*dst, iv); } } + if let Some(src) = args.get(0) { + if let Some(iv) = self.known_i64.get(src).copied() { + self.known_i64.insert(*dst, iv); + } + } } } // Track boxed numeric literals to aid signature checks (FloatBox/IntegerBox) @@ -455,18 +692,43 @@ impl LowerCore { } } } - I::PluginInvoke { dst, box_val, method, args, .. } => { + I::PluginInvoke { + dst, + box_val, + method, + args, + .. + } => { self.lower_plugin_invoke(b, &dst, &box_val, method.as_str(), args, func)?; } - I::ExternCall { dst, iface_name, method_name, args, .. } => { - self.lower_extern_call(b, &dst, iface_name.as_str(), method_name.as_str(), args, func)?; + I::ExternCall { + dst, + iface_name, + method_name, + args, + .. + } => { + self.lower_extern_call( + b, + &dst, + iface_name.as_str(), + method_name.as_str(), + args, + func, + )?; } - I::Cast { dst, value, target_type } => { + I::Cast { + dst, + value, + target_type, + } => { // Minimal cast footing: materialize source when param/known // Bool→Int: rely on producers (compare) and branch/b1 loaders; here we just reuse integer path self.push_value_if_known_or_param(b, value); // Track known i64 if source known - if let Some(v) = self.known_i64.get(value).copied() { self.known_i64.insert(*dst, v); } + if let Some(v) = self.known_i64.get(value).copied() { + self.known_i64.insert(*dst, v); + } // Track known f64 for float casts if matches!(target_type, crate::mir::MirType::Float) { if let Some(iv) = self.known_i64.get(value).copied() { @@ -475,11 +737,14 @@ impl LowerCore { } } I::Const { dst, value } => match value { - ConstValue::Integer(i) => { + ConstValue::Integer(i) => { b.emit_const_i64(*i); self.known_i64.insert(*dst, *i); } - ConstValue::Float(f) => { b.emit_const_f64(*f); self.known_f64.insert(*dst, *f); } + ConstValue::Float(f) => { + b.emit_const_f64(*f); + self.known_f64.insert(*dst, *f); + } ConstValue::Bool(bv) => { let iv = if *bv { 1 } else { 0 }; b.emit_const_i64(iv); @@ -487,34 +752,60 @@ impl LowerCore { // Mark this value as boolean producer self.bool_values.insert(*dst); } - ConstValue::String(sv) => { self.known_str.insert(*dst, sv.clone()); } - ConstValue::Null | ConstValue::Void => { } + ConstValue::String(sv) => { + self.known_str.insert(*dst, sv.clone()); + } + ConstValue::Null | ConstValue::Void => {} }, - I::Copy { dst, src } => { - if let Some(v) = self.known_i64.get(src).copied() { self.known_i64.insert(*dst, v); } - if let Some(v) = self.known_f64.get(src).copied() { self.known_f64.insert(*dst, v); } - if let Some(v) = self.known_str.get(src).cloned() { self.known_str.insert(*dst, v); } + I::Copy { dst, src } => { + if let Some(v) = self.known_i64.get(src).copied() { + self.known_i64.insert(*dst, v); + } + if let Some(v) = self.known_f64.get(src).copied() { + self.known_f64.insert(*dst, v); + } + if let Some(v) = self.known_str.get(src).cloned() { + self.known_str.insert(*dst, v); + } // Propagate handle/type knowledge to keep BoxCall routing stable across copies - if self.handle_values.contains(src) { self.handle_values.insert(*dst); } - if let Some(bt) = self.box_type_map.get(src).cloned() { self.box_type_map.insert(*dst, bt); } + if self.handle_values.contains(src) { + self.handle_values.insert(*dst); + } + if let Some(bt) = self.box_type_map.get(src).cloned() { + self.box_type_map.insert(*dst, bt); + } // Propagate boolean classification through Copy - if self.bool_values.contains(src) { self.bool_values.insert(*dst); } + if self.bool_values.contains(src) { + self.bool_values.insert(*dst); + } // If source is a parameter, materialize it on the stack for downstream ops and persist into dst slot if let Some(pidx) = self.param_index.get(src).copied() { b.emit_param_i64(pidx); - let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.ensure_local_i64(slot); b.store_local_i64(slot); } else if let Some(src_slot) = self.local_index.get(src).copied() { // If source already has a local slot (e.g., a handle), copy into dst's slot b.load_local_i64(src_slot); - let dst_slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let dst_slot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.ensure_local_i64(dst_slot); b.store_local_i64(dst_slot); } } - I::BinOp { dst, op, lhs, rhs } => { self.lower_binop(b, op, lhs, rhs, dst, func); } - I::Compare { op, lhs, rhs, dst } => { self.lower_compare(b, op, lhs, rhs, dst, func); } + I::BinOp { dst, op, lhs, rhs } => { + self.lower_binop(b, op, lhs, rhs, dst, func); + } + I::Compare { op, lhs, rhs, dst } => { + self.lower_compare(b, op, lhs, rhs, dst, func); + } I::Jump { .. } => self.lower_jump(b), I::Branch { .. } => self.lower_branch(b), I::Return { value } => { @@ -536,20 +827,18 @@ impl LowerCore { // Emit the constant and also persist to a stable local slot for this value id, // then reload to ensure a value remains on the stack for emit_return. b.emit_const_i64(k); - let rslot = *self - .local_index - .entry(*v) - .or_insert_with(|| { - let id = self.next_local; - self.next_local += 1; - id - }); + let rslot = *self.local_index.entry(*v).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.ensure_local_i64(rslot); b.store_local_i64(rslot); b.load_local_i64(rslot); } // 2) Prefer existing locals/params - else if self.local_index.get(v).is_some() || self.param_index.get(v).is_some() { + else if self.local_index.get(v).is_some() || self.param_index.get(v).is_some() + { self.push_value_if_known_or_param(b, v); } else { // 3) Backward scan and minimal reconstruction for common producers @@ -559,29 +848,47 @@ impl LowerCore { let mut produced = false; for ins in bb.instructions.iter().rev() { match ins { - crate::mir::MirInstruction::Copy { dst, src } if dst == &want => { + crate::mir::MirInstruction::Copy { dst, src } + if dst == &want => + { want = *src; // Try early exit if known/local/param emerges if self.known_i64.get(&want).is_some() { b.emit_const_i64(*self.known_i64.get(&want).unwrap()); - produced = true; break; + produced = true; + break; } - if self.local_index.get(&want).is_some() || self.param_index.get(&want).is_some() { + if self.local_index.get(&want).is_some() + || self.param_index.get(&want).is_some() + { self.push_value_if_known_or_param(b, &want); - produced = true; break; + produced = true; + break; } } // StringBox.len/length: re-materialize robustly if not saved - crate::mir::MirInstruction::BoxCall { dst: Some(did), box_val, method, args, .. } if did == &want => { + crate::mir::MirInstruction::BoxCall { + dst: Some(did), + box_val, + method, + args, + .. + } if did == &want => { let m = method.as_str(); if m == "len" || m == "length" { // Prefer param/local handle, else reconstruct literal - if let Some(pidx) = self.param_index.get(box_val).copied() { + if let Some(pidx) = + self.param_index.get(box_val).copied() + { self.emit_len_with_fallback_param(b, pidx); - produced = true; break; - } else if let Some(slot) = self.local_index.get(box_val).copied() { + produced = true; + break; + } else if let Some(slot) = + self.local_index.get(box_val).copied() + { self.emit_len_with_fallback_local_handle(b, slot); - produced = true; break; + produced = true; + break; } else { // Try literal reconstruction via known_str map let mut lit: Option = None; @@ -594,21 +901,36 @@ impl LowerCore { } } } - if lit.is_some() { break; } + if lit.is_some() { + break; + } + } + if let Some(s) = lit { + self.emit_len_with_fallback_literal(b, &s); + produced = true; + break; } - if let Some(s) = lit { self.emit_len_with_fallback_literal(b, &s); produced = true; break; } } } } // Const producer as last resort - crate::mir::MirInstruction::Const { dst, value: cval } if dst == &want => { + crate::mir::MirInstruction::Const { dst, value: cval } + if dst == &want => + { match cval { - crate::mir::ConstValue::Integer(i) => { b.emit_const_i64(*i); } - crate::mir::ConstValue::Bool(bv) => { b.emit_const_i64(if *bv {1} else {0}); } - crate::mir::ConstValue::Float(f) => { b.emit_const_f64(*f); } + crate::mir::ConstValue::Integer(i) => { + b.emit_const_i64(*i); + } + crate::mir::ConstValue::Bool(bv) => { + b.emit_const_i64(if *bv { 1 } else { 0 }); + } + crate::mir::ConstValue::Float(f) => { + b.emit_const_f64(*f); + } _ => {} } - produced = true; break; + produced = true; + break; } _ => {} } @@ -625,93 +947,218 @@ impl LowerCore { I::Store { value, ptr } => { // Minimal lowering: materialize value if known/param and store to a local slot keyed by ptr self.push_value_if_known_or_param(b, value); - let slot = *self.local_index.entry(*ptr).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*ptr).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.ensure_local_i64(slot); b.store_local_i64(slot); } I::Load { dst, ptr } => { // Minimal lowering: load from local slot keyed by ptr, then materialize into dst's own slot - let src_slot = *self.local_index.entry(*ptr).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let src_slot = *self.local_index.entry(*ptr).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.ensure_local_i64(src_slot); b.load_local_i64(src_slot); // Persist into dst's slot to make subsequent uses find it via local_index - let dst_slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let dst_slot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.ensure_local_i64(dst_slot); b.store_local_i64(dst_slot); } I::Phi { dst, .. } => { // PHI をローカルに materialize して後続の Return で安定参照 - let pos = self.phi_param_index.get(&(cur_bb, *dst)).copied().unwrap_or(0); - if self.bool_phi_values.contains(dst) { b.push_block_param_b1_at(pos); } - else { b.push_block_param_i64_at(pos); } - let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let pos = self + .phi_param_index + .get(&(cur_bb, *dst)) + .copied() + .unwrap_or(0); + if self.bool_phi_values.contains(dst) { + b.push_block_param_b1_at(pos); + } else { + b.push_block_param_i64_at(pos); + } + let slot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.ensure_local_i64(slot); b.store_local_i64(slot); } I::ArrayGet { dst, array, index } => { // Prepare receiver + index on stack let argc = 2usize; - if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } - if let Some(iv) = self.known_i64.get(index).copied() { b.emit_const_i64(iv); } else { self.push_value_if_known_or_param(b, index); } + if let Some(pidx) = self.param_index.get(array).copied() { + b.emit_param_i64(pidx); + } else { + b.emit_const_i64(-1); + } + if let Some(iv) = self.known_i64.get(index).copied() { + b.emit_const_i64(iv); + } else { + self.push_value_if_known_or_param(b, index); + } // Decide policy - let decision = crate::jit::policy::invoke::decide_box_method("ArrayBox", "get", argc, true); + let decision = + crate::jit::policy::invoke::decide_box_method("ArrayBox", "get", argc, true); match decision { - crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { + crate::jit::policy::invoke::InvokeDecision::PluginInvoke { + type_id, + method_id, + box_type, + .. + } => { b.emit_plugin_invoke(type_id, method_id, argc, true); - crate::jit::observe::lower_plugin_invoke(&box_type, "get", type_id, method_id, argc); + crate::jit::observe::lower_plugin_invoke( + &box_type, "get", type_id, method_id, argc, + ); // Persist into dst's slot - let dslot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let dslot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(dslot); } crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { - crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64"], "allow", "mapped_symbol"); + crate::jit::observe::lower_hostcall( + &symbol, + argc, + &["Handle", "I64"], + "allow", + "mapped_symbol", + ); b.emit_host_call(&symbol, argc, true); - let dslot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let dslot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(dslot); } _ => { - super::core_hostcall::lower_array_get(b, &self.param_index, &self.known_i64, array, index); - let dslot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + super::core_hostcall::lower_array_get( + b, + &self.param_index, + &self.known_i64, + array, + index, + ); + let dslot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(dslot); - }, + } } } - I::ArraySet { array, index, value } => { + I::ArraySet { + array, + index, + value, + } => { let argc = 3usize; - if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } + if let Some(pidx) = self.param_index.get(array).copied() { + b.emit_param_i64(pidx); + } else { + b.emit_const_i64(-1); + } // GC write barrier hint for mutating array operations (pass receiver handle/index as site id: receiver preferred) - b.emit_host_call(crate::jit::r#extern::runtime::SYM_GC_BARRIER_WRITE, 1, false); - if let Some(iv) = self.known_i64.get(index).copied() { b.emit_const_i64(iv); } else { self.push_value_if_known_or_param(b, index); } - if let Some(vv) = self.known_i64.get(value).copied() { b.emit_const_i64(vv); } else { self.push_value_if_known_or_param(b, value); } - let decision = crate::jit::policy::invoke::decide_box_method("ArrayBox", "set", argc, false); + b.emit_host_call( + crate::jit::r#extern::runtime::SYM_GC_BARRIER_WRITE, + 1, + false, + ); + if let Some(iv) = self.known_i64.get(index).copied() { + b.emit_const_i64(iv); + } else { + self.push_value_if_known_or_param(b, index); + } + if let Some(vv) = self.known_i64.get(value).copied() { + b.emit_const_i64(vv); + } else { + self.push_value_if_known_or_param(b, value); + } + let decision = + crate::jit::policy::invoke::decide_box_method("ArrayBox", "set", argc, false); match decision { - crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { + crate::jit::policy::invoke::InvokeDecision::PluginInvoke { + type_id, + method_id, + box_type, + .. + } => { b.emit_plugin_invoke(type_id, method_id, argc, false); - crate::jit::observe::lower_plugin_invoke(&box_type, "set", type_id, method_id, argc); + crate::jit::observe::lower_plugin_invoke( + &box_type, "set", type_id, method_id, argc, + ); } crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { - crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64","I64"], "allow", "mapped_symbol"); + crate::jit::observe::lower_hostcall( + &symbol, + argc, + &["Handle", "I64", "I64"], + "allow", + "mapped_symbol", + ); b.emit_host_call(&symbol, argc, false); } - _ => super::core_hostcall::lower_array_set(b, &self.param_index, &self.known_i64, array, index, value), + _ => super::core_hostcall::lower_array_set( + b, + &self.param_index, + &self.known_i64, + array, + index, + value, + ), } } - I::BoxCall { box_val: array, method, args, dst, .. } => { + I::BoxCall { + box_val: array, + method, + args, + dst, + .. + } => { // Prefer ops_ext; if not handled, fall back to legacy path below let trace = std::env::var("NYASH_JIT_TRACE_LOWER").ok().as_deref() == Some("1"); // Handle ArrayBox.set with handle-valued value for literal strings - if method == "set" && self.box_type_map.get(&array).map(|s| s=="ArrayBox").unwrap_or(false) { + if method == "set" + && self + .box_type_map + .get(&array) + .map(|s| s == "ArrayBox") + .unwrap_or(false) + { // Expect args: [index, value] let argc = 3usize; // Receiver handle: prefer param or local slot; else -1 sentinel - if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } - else if let Some(slot) = self.local_index.get(&array).copied() { b.load_local_i64(slot); } - else { b.emit_const_i64(-1); } + if let Some(pidx) = self.param_index.get(array).copied() { + b.emit_param_i64(pidx); + } else if let Some(slot) = self.local_index.get(&array).copied() { + b.load_local_i64(slot); + } else { + b.emit_const_i64(-1); + } // Index as i64 if let Some(idx_v) = args.get(0) { - if let Some(iv) = self.known_i64.get(idx_v).copied() { b.emit_const_i64(iv); } - else { self.push_value_if_known_or_param(b, idx_v); } - } else { b.emit_const_i64(0); } + if let Some(iv) = self.known_i64.get(idx_v).copied() { + b.emit_const_i64(iv); + } else { + self.push_value_if_known_or_param(b, idx_v); + } + } else { + b.emit_const_i64(0); + } // Value as handle: for String literal, synthesize a handle; else prefer param/local handle if let Some(val_v) = args.get(1) { let mut emitted_val_handle = false; @@ -725,34 +1172,60 @@ impl LowerCore { b.emit_param_i64(pidx); emitted_val_handle = true; } - if !emitted_val_handle { b.emit_const_i64(0); } - } else { b.emit_const_i64(0); } + if !emitted_val_handle { + b.emit_const_i64(0); + } + } else { + b.emit_const_i64(0); + } // Emit handle-handle variant hostcall - b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_SET_HH, argc, false); - if trace { eprintln!("[LOWER] BoxCall(ArrayBox.set) → ARRAY_SET_HH"); } + b.emit_host_call( + crate::jit::r#extern::collections::SYM_ARRAY_SET_HH, + argc, + false, + ); + if trace { + eprintln!("[LOWER] BoxCall(ArrayBox.set) → ARRAY_SET_HH"); + } return Ok(()); } // Early constant fold: StringBox literal length/len (allow disabling via NYASH_JIT_DISABLE_LEN_CONST=1) if std::env::var("NYASH_JIT_DISABLE_LEN_CONST").ok().as_deref() != Some("1") && (method == "len" || method == "length") - && self.box_type_map.get(&array).map(|s| s=="StringBox").unwrap_or(false) { + && self + .box_type_map + .get(&array) + .map(|s| s == "StringBox") + .unwrap_or(false) + { if let Some(s) = self.string_box_literal.get(&array).cloned() { let n = s.len() as i64; b.emit_const_i64(n); if let Some(d) = dst { - let slot = *self.local_index.entry(*d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); + let slot = *self.local_index.entry(*d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); self.known_i64.insert(*d, n); } - if trace { eprintln!("[LOWER] early const-fold StringBox.{} = {}", method, n); } + if trace { + eprintln!("[LOWER] early const-fold StringBox.{} = {}", method, n); + } return Ok(()); } } - let handled = self.lower_box_call(func, b, &array, method.as_str(), args, dst.clone())?; + let handled = + self.lower_box_call(func, b, &array, method.as_str(), args, dst.clone())?; if trace { eprintln!( "[LOWER] BoxCall recv={:?} method={} handled={} box_type={:?} dst?={}", - array, method, handled, self.box_type_map.get(&array), dst.is_some() + array, + method, + handled, + self.box_type_map.get(&array), + dst.is_some() ); if !handled { let bt = self.box_type_map.get(&array).cloned().unwrap_or_default(); @@ -762,20 +1235,39 @@ impl LowerCore { // Classify up to first 3 args: i(known_i64) s(known_str) p(param) l(local) h(handle) -(unknown) let mut arg_kinds: Vec<&'static str> = Vec::new(); for a in args.iter().take(3) { - let k = if self.known_i64.contains_key(a) { "i" } - else if self.known_str.contains_key(a) { "s" } - else if self.param_index.contains_key(a) { "p" } - else if self.local_index.contains_key(a) { "l" } - else if self.handle_values.contains(a) { "h" } - else { "-" }; + let k = if self.known_i64.contains_key(a) { + "i" + } else if self.known_str.contains_key(a) { + "s" + } else if self.param_index.contains_key(a) { + "p" + } else if self.local_index.contains_key(a) { + "l" + } else if self.handle_values.contains(a) { + "h" + } else { + "-" + }; arg_kinds.push(k); } // Policy hint: whether a mapped HostCall exists for (box_type, method) - let policy = crate::jit::policy::invoke::decide_box_method(&bt, method.as_str(), 1 + args.len(), dst.is_some()); + let policy = crate::jit::policy::invoke::decide_box_method( + &bt, + method.as_str(), + 1 + args.len(), + dst.is_some(), + ); let policy_str = match policy { - crate::jit::policy::invoke::InvokeDecision::HostCall { ref symbol, .. } => format!("hostcall:{}", symbol), - crate::jit::policy::invoke::InvokeDecision::PluginInvoke { .. } => "plugin_invoke".to_string(), - crate::jit::policy::invoke::InvokeDecision::Fallback { ref reason } => format!("fallback:{}", reason), + crate::jit::policy::invoke::InvokeDecision::HostCall { + ref symbol, + .. + } => format!("hostcall:{}", symbol), + crate::jit::policy::invoke::InvokeDecision::PluginInvoke { .. } => { + "plugin_invoke".to_string() + } + crate::jit::policy::invoke::InvokeDecision::Fallback { ref reason } => { + format!("fallback:{}", reason) + } }; eprintln!( "[LOWER] fallback(reason=unhandled) box_type='{}' method='{}' recv[param?{} local?{} handle?{}] args={:?} policy={}", @@ -787,225 +1279,216 @@ impl LowerCore { return Ok(()); } } - /* legacy BoxCall branch removed (now handled in ops_ext) - // handled in helper (read-only simple methods) - } else if matches!(method.as_str(), "sin" | "cos" | "abs" | "min" | "max") { - super::core_hostcall::lower_math_call( - func, - b, - &self.known_i64, - &self.known_f64, - &self.float_box_values, - method.as_str(), - args, - dst.clone(), - ); - } else if false /* moved to ops_ext: NYASH_USE_PLUGIN_BUILTINS */ { - // StringBox(length/is_empty/charCodeAt): policy+observe経由に統一 - if matches!(method.as_str(), "length" | "is_empty" | "charCodeAt") { - // receiver - if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } - let mut argc = 1usize; - if method.as_str() == "charCodeAt" { - if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } - argc = 2; + /* legacy BoxCall branch removed (now handled in ops_ext) + // handled in helper (read-only simple methods) + } else if matches!(method.as_str(), "sin" | "cos" | "abs" | "min" | "max") { + super::core_hostcall::lower_math_call( + func, + b, + &self.known_i64, + &self.known_f64, + &self.float_box_values, + method.as_str(), + args, + dst.clone(), + ); + } else if false /* moved to ops_ext: NYASH_USE_PLUGIN_BUILTINS */ { + // StringBox(length/is_empty/charCodeAt): policy+observe経由に統一 + if matches!(method.as_str(), "length" | "is_empty" | "charCodeAt") { + // receiver + if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } + let mut argc = 1usize; + if method.as_str() == "charCodeAt" { + if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } + argc = 2; + } + if method.as_str() == "is_empty" { b.hint_ret_bool(true); } + let decision = crate::jit::policy::invoke::decide_box_method("StringBox", method.as_str(), argc, dst.is_some()); + match decision { + crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { + b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some()); + crate::jit::observe::lower_plugin_invoke(&box_type, method.as_str(), type_id, method_id, argc); + return Ok(()); } - if method.as_str() == "is_empty" { b.hint_ret_bool(true); } - let decision = crate::jit::policy::invoke::decide_box_method("StringBox", method.as_str(), argc, dst.is_some()); - match decision { - crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { - b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some()); - crate::jit::observe::lower_plugin_invoke(&box_type, method.as_str(), type_id, method_id, argc); + crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { + crate::jit::observe::lower_hostcall(&symbol, argc, &if argc==1 { ["Handle"][..].to_vec() } else { ["Handle","I64"][..].to_vec() }, "allow", "mapped_symbol"); + b.emit_host_call(&symbol, argc, dst.is_some()); + return Ok(()); + } + _ => {} + } + } + // Integer.get/set specialized when receiver is Integer (avoid Map collision) + if matches!(method.as_str(), "get" | "set") { + let recv_is_int = func.metadata.value_types.get(array).map(|mt| matches!(mt, crate::mir::MirType::Integer)).unwrap_or(false); + if recv_is_int { + if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() { + if let Ok(h) = ph.resolve_method("IntegerBox", method.as_str()) { + if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } + let mut argc = 1usize; + if method.as_str() == "set" { + if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } + argc = 2; + } + b.emit_plugin_invoke(h.type_id, h.method_id, argc, dst.is_some()); + crate::jit::events::emit_lower( + serde_json::json!({ + "id": format!("plugin:{}:{}", h.box_type, method.as_str()), + "decision":"allow","reason":"plugin_invoke","argc": argc, + "type_id": h.type_id, "method_id": h.method_id + }), + "plugin","" + ); return Ok(()); } - crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { - crate::jit::observe::lower_hostcall(&symbol, argc, &if argc==1 { ["Handle"][..].to_vec() } else { ["Handle","I64"][..].to_vec() }, "allow", "mapped_symbol"); - b.emit_host_call(&symbol, argc, dst.is_some()); - return Ok(()); - } - _ => {} } } - // Integer.get/set specialized when receiver is Integer (avoid Map collision) - if matches!(method.as_str(), "get" | "set") { - let recv_is_int = func.metadata.value_types.get(array).map(|mt| matches!(mt, crate::mir::MirType::Integer)).unwrap_or(false); - if recv_is_int { - if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() { - if let Ok(h) = ph.resolve_method("IntegerBox", method.as_str()) { - if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } - let mut argc = 1usize; - if method.as_str() == "set" { - if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } - argc = 2; - } - b.emit_plugin_invoke(h.type_id, h.method_id, argc, dst.is_some()); + } + match method.as_str() { + "len" | "length" => { + // Resolve ArrayBox plugin method and emit plugin_invoke (symbolic) + if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() { + let mname = "length"; + if let Ok(h) = ph.resolve_method("ArrayBox", mname) { + // Receiver + if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } + let mut argc = 1usize; + // length only + b.emit_plugin_invoke(h.type_id, h.method_id, argc, dst.is_some()); + crate::jit::events::emit_lower( + serde_json::json!({ + "id": format!("plugin:{}:{}", h.box_type, mname), + "decision":"allow","reason":"plugin_invoke","argc": argc, + "type_id": h.type_id, "method_id": h.method_id + }), + "plugin","" + ); + } + } + } + // Map: size/get/has (RO) and set (mutating; allowed only when policy.read_only=false) + "size" | "get" | "has" | "set" => { + if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() { + if let Ok(h) = ph.resolve_method("MapBox", method.as_str()) { + if method.as_str() == "set" && crate::jit::policy::current().read_only { + // Deny mutating under read-only policy crate::jit::events::emit_lower( serde_json::json!({ - "id": format!("plugin:{}:{}", h.box_type, method.as_str()), - "decision":"allow","reason":"plugin_invoke","argc": argc, - "type_id": h.type_id, "method_id": h.method_id + "id": format!("plugin:{}:{}", "MapBox", "set"), + "decision":"fallback","reason":"policy_denied_mutating" }), "plugin","" ); + // Do not emit plugin call; VM path will handle return Ok(()); } + if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } + // Insert GC write barrier before mutating Map.set + if method.as_str() == "set" { + b.emit_host_call(crate::jit::r#extern::runtime::SYM_GC_BARRIER_WRITE, 1, false); + } + let mut argc = 1usize; + if matches!(method.as_str(), "get" | "has") { + if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } + argc += 1; + } else if method.as_str() == "set" { + if let Some(k) = args.get(0) { self.push_value_if_known_or_param(b, k); } else { b.emit_const_i64(0); } + if let Some(v) = args.get(1) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } + argc += 2; + } + b.emit_plugin_invoke(h.type_id, h.method_id, argc, dst.is_some()); + crate::jit::events::emit_lower( + serde_json::json!({ + "id": format!("plugin:{}:{}", h.box_type, method.as_str()), + "decision":"allow","reason":"plugin_invoke","argc": argc, + "type_id": h.type_id, "method_id": h.method_id + }), + "plugin","" + ); } } } - match method.as_str() { - "len" | "length" => { - // Resolve ArrayBox plugin method and emit plugin_invoke (symbolic) - if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() { - let mname = "length"; - if let Ok(h) = ph.resolve_method("ArrayBox", mname) { - // Receiver - if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } - let mut argc = 1usize; - // length only - b.emit_plugin_invoke(h.type_id, h.method_id, argc, dst.is_some()); - crate::jit::events::emit_lower( - serde_json::json!({ - "id": format!("plugin:{}:{}", h.box_type, mname), - "decision":"allow","reason":"plugin_invoke","argc": argc, - "type_id": h.type_id, "method_id": h.method_id - }), - "plugin","" - ); + _ => { /* other BoxCalls handled below */ } + } + } else if crate::jit::config::current().hostcall { + match method.as_str() { + "len" | "length" => { + // Constant fold: if receiver is NewBox(StringBox, Const String), return its length directly + if let Some(did) = dst.as_ref() { + let mut lit_len: Option = None; + for (_bid, bb) in func.blocks.iter() { + for ins in bb.instructions.iter() { + if let crate::mir::MirInstruction::NewBox { dst: ndst, box_type, args } = ins { + if ndst == array && box_type == "StringBox" && args.len() == 1 { + let src = args[0]; + if let Some(s) = self.known_str.get(&src) { lit_len = Some(s.len() as i64); break; } + // scan Const directly + for (_b2, bb2) in func.blocks.iter() { + for ins2 in bb2.instructions.iter() { + if let crate::mir::MirInstruction::Const { dst: cdst, value } = ins2 { if *cdst == src { if let crate::mir::ConstValue::String(sv) = value { lit_len = Some(sv.len() as i64); break; } } } + } + if lit_len.is_some() { break; } + } + } + } } + if lit_len.is_some() { break; } + } + if let Some(n) = lit_len { + b.emit_const_i64(n); + self.known_i64.insert(*did, n); + return Ok(()); } } - // Map: size/get/has (RO) and set (mutating; allowed only when policy.read_only=false) - "size" | "get" | "has" | "set" => { - if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() { - if let Ok(h) = ph.resolve_method("MapBox", method.as_str()) { - if method.as_str() == "set" && crate::jit::policy::current().read_only { - // Deny mutating under read-only policy - crate::jit::events::emit_lower( - serde_json::json!({ - "id": format!("plugin:{}:{}", "MapBox", "set"), - "decision":"fallback","reason":"policy_denied_mutating" - }), - "plugin","" - ); - // Do not emit plugin call; VM path will handle - return Ok(()); - } - if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } - // Insert GC write barrier before mutating Map.set - if method.as_str() == "set" { - b.emit_host_call(crate::jit::r#extern::runtime::SYM_GC_BARRIER_WRITE, 1, false); - } - let mut argc = 1usize; - if matches!(method.as_str(), "get" | "has") { - if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } - argc += 1; - } else if method.as_str() == "set" { - if let Some(k) = args.get(0) { self.push_value_if_known_or_param(b, k); } else { b.emit_const_i64(0); } - if let Some(v) = args.get(1) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } - argc += 2; - } - b.emit_plugin_invoke(h.type_id, h.method_id, argc, dst.is_some()); - crate::jit::events::emit_lower( - serde_json::json!({ - "id": format!("plugin:{}:{}", h.box_type, method.as_str()), - "decision":"allow","reason":"plugin_invoke","argc": argc, - "type_id": h.type_id, "method_id": h.method_id - }), - "plugin","" - ); - } + if let Some(pidx) = self.param_index.get(array).copied() { + // Param 経路: string.len_h → 0 の場合 any.length_h へフォールバック + self.emit_len_with_fallback_param(b, pidx); + if let Some(d) = dst.as_ref() { + let slot = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + b.store_local_i64(slot); } - } - _ => { /* other BoxCalls handled below */ } - } - } else if crate::jit::config::current().hostcall { - match method.as_str() { - "len" | "length" => { - // Constant fold: if receiver is NewBox(StringBox, Const String), return its length directly - if let Some(did) = dst.as_ref() { - let mut lit_len: Option = None; + } else { + crate::jit::events::emit_lower( + serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_LEN_H, "decision":"fallback", "reason":"receiver_not_param", "argc":1, "arg_types":["Handle"]}), + "hostcall","" + ); + // Try local handle (AOT/JIT-AOT) before legacy index fallback + if let Some(slot) = self.local_index.get(array).copied() { + // ローカルハンドル: string.len_h → any.length_h フォールバック + self.emit_len_with_fallback_local_handle(b, slot); + if let Some(d) = dst.as_ref() { + let slotd = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + b.store_local_i64(slotd); + } + } else if self.box_type_map.get(array).map(|s| s == "StringBox").unwrap_or(false) { + // Attempt reconstruction for StringBox literal: scan NewBox(StringBox, Const String) + let mut lit: Option = None; for (_bid, bb) in func.blocks.iter() { for ins in bb.instructions.iter() { - if let crate::mir::MirInstruction::NewBox { dst: ndst, box_type, args } = ins { - if ndst == array && box_type == "StringBox" && args.len() == 1 { - let src = args[0]; - if let Some(s) = self.known_str.get(&src) { lit_len = Some(s.len() as i64); break; } - // scan Const directly - for (_b2, bb2) in func.blocks.iter() { - for ins2 in bb2.instructions.iter() { - if let crate::mir::MirInstruction::Const { dst: cdst, value } = ins2 { if *cdst == src { if let crate::mir::ConstValue::String(sv) = value { lit_len = Some(sv.len() as i64); break; } } } + if let crate::mir::MirInstruction::NewBox { dst, box_type, args } = ins { + if dst == array && box_type == "StringBox" && args.len() == 1 { + if let Some(src) = args.get(0) { + if let Some(s) = self.known_str.get(src).cloned() { lit = Some(s); break; } + // Also scan Const directly + for (_bid2, bb2) in func.blocks.iter() { + for ins2 in bb2.instructions.iter() { + if let crate::mir::MirInstruction::Const { dst: cdst, value } = ins2 { if cdst == src { if let crate::mir::ConstValue::String(sv) = value { lit = Some(sv.clone()); break; } } } + } + if lit.is_some() { break; } } - if lit_len.is_some() { break; } } } } } - if lit_len.is_some() { break; } + if lit.is_some() { break; } } - if let Some(n) = lit_len { - b.emit_const_i64(n); - self.known_i64.insert(*did, n); - return Ok(()); - } - } - if let Some(pidx) = self.param_index.get(array).copied() { - // Param 経路: string.len_h → 0 の場合 any.length_h へフォールバック - self.emit_len_with_fallback_param(b, pidx); - if let Some(d) = dst.as_ref() { - let slot = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); - b.store_local_i64(slot); - } - } else { - crate::jit::events::emit_lower( - serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_LEN_H, "decision":"fallback", "reason":"receiver_not_param", "argc":1, "arg_types":["Handle"]}), - "hostcall","" - ); - // Try local handle (AOT/JIT-AOT) before legacy index fallback - if let Some(slot) = self.local_index.get(array).copied() { - // ローカルハンドル: string.len_h → any.length_h フォールバック - self.emit_len_with_fallback_local_handle(b, slot); + if let Some(s) = lit { + // リテラル復元: string.len_h → any.length_h フォールバック + self.emit_len_with_fallback_literal(b, &s); if let Some(d) = dst.as_ref() { let slotd = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); b.store_local_i64(slotd); } - } else if self.box_type_map.get(array).map(|s| s == "StringBox").unwrap_or(false) { - // Attempt reconstruction for StringBox literal: scan NewBox(StringBox, Const String) - let mut lit: Option = None; - for (_bid, bb) in func.blocks.iter() { - for ins in bb.instructions.iter() { - if let crate::mir::MirInstruction::NewBox { dst, box_type, args } = ins { - if dst == array && box_type == "StringBox" && args.len() == 1 { - if let Some(src) = args.get(0) { - if let Some(s) = self.known_str.get(src).cloned() { lit = Some(s); break; } - // Also scan Const directly - for (_bid2, bb2) in func.blocks.iter() { - for ins2 in bb2.instructions.iter() { - if let crate::mir::MirInstruction::Const { dst: cdst, value } = ins2 { if cdst == src { if let crate::mir::ConstValue::String(sv) = value { lit = Some(sv.clone()); break; } } } - } - if lit.is_some() { break; } - } - } - } - } - } - if lit.is_some() { break; } - } - if let Some(s) = lit { - // リテラル復元: string.len_h → any.length_h フォールバック - self.emit_len_with_fallback_literal(b, &s); - if let Some(d) = dst.as_ref() { - let slotd = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); - b.store_local_i64(slotd); - } - } else { - let arr_idx = -1; - b.emit_const_i64(arr_idx); - b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some()); - if let Some(d) = dst.as_ref() { - let slotd = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); - b.store_local_i64(slotd); - } - } } else { let arr_idx = -1; b.emit_const_i64(arr_idx); @@ -1015,288 +1498,297 @@ impl LowerCore { b.store_local_i64(slotd); } } + } else { + let arr_idx = -1; + b.emit_const_i64(arr_idx); + b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some()); + if let Some(d) = dst.as_ref() { + let slotd = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + b.store_local_i64(slotd); + } } } - // math.* minimal boundary: use registry signature to decide allow/fallback (no actual hostcall yet) - "sin" | "cos" | "abs" | "min" | "max" => { - use crate::jit::hostcall_registry::{check_signature, ArgKind}; - // Build symbol and observed arg kinds (f64 if known float, else i64) - let sym = format!("nyash.math.{}", method); - let mut observed: Vec = Vec::new(); - for v in args.iter() { - if self.known_f64.contains_key(v) { observed.push(ArgKind::F64); } - else { observed.push(ArgKind::I64); } - } - // Prepare arg_types for event payload - // Classify argument kinds using TyEnv when available; fallback to known maps/FloatBox tracking - let mut observed_kinds: Vec = Vec::new(); - for v in args.iter() { - let kind = if let Some(mt) = func.metadata.value_types.get(v) { - match mt { - crate::mir::MirType::Float => crate::jit::hostcall_registry::ArgKind::F64, - crate::mir::MirType::Integer => crate::jit::hostcall_registry::ArgKind::I64, - crate::mir::MirType::Bool => crate::jit::hostcall_registry::ArgKind::I64, // b1はI64 0/1に正規化 - crate::mir::MirType::String | crate::mir::MirType::Box(_) => crate::jit::hostcall_registry::ArgKind::Handle, - _ => { - if self.known_f64.contains_key(v) || self.float_box_values.contains(v) { crate::jit::hostcall_registry::ArgKind::F64 } - else { crate::jit::hostcall_registry::ArgKind::I64 } - } + } + // math.* minimal boundary: use registry signature to decide allow/fallback (no actual hostcall yet) + "sin" | "cos" | "abs" | "min" | "max" => { + use crate::jit::hostcall_registry::{check_signature, ArgKind}; + // Build symbol and observed arg kinds (f64 if known float, else i64) + let sym = format!("nyash.math.{}", method); + let mut observed: Vec = Vec::new(); + for v in args.iter() { + if self.known_f64.contains_key(v) { observed.push(ArgKind::F64); } + else { observed.push(ArgKind::I64); } + } + // Prepare arg_types for event payload + // Classify argument kinds using TyEnv when available; fallback to known maps/FloatBox tracking + let mut observed_kinds: Vec = Vec::new(); + for v in args.iter() { + let kind = if let Some(mt) = func.metadata.value_types.get(v) { + match mt { + crate::mir::MirType::Float => crate::jit::hostcall_registry::ArgKind::F64, + crate::mir::MirType::Integer => crate::jit::hostcall_registry::ArgKind::I64, + crate::mir::MirType::Bool => crate::jit::hostcall_registry::ArgKind::I64, // b1はI64 0/1に正規化 + crate::mir::MirType::String | crate::mir::MirType::Box(_) => crate::jit::hostcall_registry::ArgKind::Handle, + _ => { + if self.known_f64.contains_key(v) || self.float_box_values.contains(v) { crate::jit::hostcall_registry::ArgKind::F64 } + else { crate::jit::hostcall_registry::ArgKind::I64 } } - } else { - if self.known_f64.contains_key(v) || self.float_box_values.contains(v) { crate::jit::hostcall_registry::ArgKind::F64 } - else { crate::jit::hostcall_registry::ArgKind::I64 } - }; - observed_kinds.push(kind); - } - let arg_types: Vec<&'static str> = observed_kinds.iter().map(|k| match k { crate::jit::hostcall_registry::ArgKind::I64 => "I64", crate::jit::hostcall_registry::ArgKind::F64 => "F64", crate::jit::hostcall_registry::ArgKind::Handle => "Handle" }).collect(); - match check_signature(&sym, &observed_kinds) { - Ok(()) => { - // allow: record decision; execution remains on VM for now (thin bridge) - crate::jit::events::emit_lower( - serde_json::json!({ - "id": sym, - "decision": "allow", - "reason": "sig_ok", - "argc": observed.len(), - "arg_types": arg_types - }), - "hostcall","" - ); - // If native f64 is enabled, emit a typed hostcall to math extern - if crate::jit::config::current().native_f64 { - let (symbol, arity) = match method.as_str() { - "sin" => ("nyash.math.sin_f64", 1), - "cos" => ("nyash.math.cos_f64", 1), - "abs" => ("nyash.math.abs_f64", 1), - "min" => ("nyash.math.min_f64", 2), - "max" => ("nyash.math.max_f64", 2), - _ => ("nyash.math.sin_f64", 1), - }; - // Push f64 args from known_f64 or coerce known_i64 - for i in 0..arity { - if let Some(v) = args.get(i) { - // Try direct known values - if let Some(fv) = self.known_f64.get(v).copied() { b.emit_const_f64(fv); continue; } - if let Some(iv) = self.known_i64.get(v).copied() { b.emit_const_f64(iv as f64); continue; } - // Try unwrap FloatBox: scan blocks to find NewBox FloatBox { args: [src] } and reuse src const - let mut emitted = false; - 'scan: for (_bb_id, bb) in func.blocks.iter() { - for ins in bb.instructions.iter() { - if let crate::mir::MirInstruction::NewBox { dst, box_type, args: nb_args } = ins { - if *dst == *v && box_type == "FloatBox" { - if let Some(srcv) = nb_args.get(0) { - if let Some(fv) = self.known_f64.get(srcv).copied() { b.emit_const_f64(fv); emitted = true; break 'scan; } - if let Some(iv) = self.known_i64.get(srcv).copied() { b.emit_const_f64(iv as f64); emitted = true; break 'scan; } - } + } + } else { + if self.known_f64.contains_key(v) || self.float_box_values.contains(v) { crate::jit::hostcall_registry::ArgKind::F64 } + else { crate::jit::hostcall_registry::ArgKind::I64 } + }; + observed_kinds.push(kind); + } + let arg_types: Vec<&'static str> = observed_kinds.iter().map(|k| match k { crate::jit::hostcall_registry::ArgKind::I64 => "I64", crate::jit::hostcall_registry::ArgKind::F64 => "F64", crate::jit::hostcall_registry::ArgKind::Handle => "Handle" }).collect(); + match check_signature(&sym, &observed_kinds) { + Ok(()) => { + // allow: record decision; execution remains on VM for now (thin bridge) + crate::jit::events::emit_lower( + serde_json::json!({ + "id": sym, + "decision": "allow", + "reason": "sig_ok", + "argc": observed.len(), + "arg_types": arg_types + }), + "hostcall","" + ); + // If native f64 is enabled, emit a typed hostcall to math extern + if crate::jit::config::current().native_f64 { + let (symbol, arity) = match method.as_str() { + "sin" => ("nyash.math.sin_f64", 1), + "cos" => ("nyash.math.cos_f64", 1), + "abs" => ("nyash.math.abs_f64", 1), + "min" => ("nyash.math.min_f64", 2), + "max" => ("nyash.math.max_f64", 2), + _ => ("nyash.math.sin_f64", 1), + }; + // Push f64 args from known_f64 or coerce known_i64 + for i in 0..arity { + if let Some(v) = args.get(i) { + // Try direct known values + if let Some(fv) = self.known_f64.get(v).copied() { b.emit_const_f64(fv); continue; } + if let Some(iv) = self.known_i64.get(v).copied() { b.emit_const_f64(iv as f64); continue; } + // Try unwrap FloatBox: scan blocks to find NewBox FloatBox { args: [src] } and reuse src const + let mut emitted = false; + 'scan: for (_bb_id, bb) in func.blocks.iter() { + for ins in bb.instructions.iter() { + if let crate::mir::MirInstruction::NewBox { dst, box_type, args: nb_args } = ins { + if *dst == *v && box_type == "FloatBox" { + if let Some(srcv) = nb_args.get(0) { + if let Some(fv) = self.known_f64.get(srcv).copied() { b.emit_const_f64(fv); emitted = true; break 'scan; } + if let Some(iv) = self.known_i64.get(srcv).copied() { b.emit_const_f64(iv as f64); emitted = true; break 'scan; } } } } } - if !emitted { b.emit_const_f64(0.0); } - } else { b.emit_const_f64(0.0); } - } - let kinds: Vec = (0..arity).map(|_| super::builder::ParamKind::F64).collect(); - b.emit_host_call_typed(symbol, &kinds, dst.is_some(), true); + } + if !emitted { b.emit_const_f64(0.0); } + } else { b.emit_const_f64(0.0); } } - } - Err(reason) => { - crate::jit::events::emit_lower( - serde_json::json!({ - "id": sym, - "decision": "fallback", - "reason": reason, - "argc": observed.len(), - "arg_types": arg_types - }), - "hostcall", - "" - ); + let kinds: Vec = (0..arity).map(|_| super::builder::ParamKind::F64).collect(); + b.emit_host_call_typed(symbol, &kinds, dst.is_some(), true); } } - // no-op: VM側で実行される - } - "isEmpty" | "empty" => { - if let Some(pidx) = self.param_index.get(array).copied() { - crate::jit::events::emit( - "hostcall","",None,None, - serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, "decision":"allow", "reason":"sig_ok", "argc":1, "arg_types":["Handle"]}) - ); - b.emit_param_i64(pidx); - // returns i64 0/1 - b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, 1, dst.is_some()); - } else { - crate::jit::events::emit_lower( - serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, "decision":"fallback", "reason":"receiver_not_param", "argc":1, "arg_types":["Handle"]}), - "hostcall","" - ); - } - } - "push" => { - // argc=2: (array, value) - let argc = 2usize; - let val = args.get(0).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0); - if let Some(pidx) = self.param_index.get(array).copied() { - // Prepare args - b.emit_param_i64(pidx); - b.emit_const_i64(val); - // Decide policy - let decision = crate::jit::policy::invoke::decide_box_method("ArrayBox", "push", argc, false); - match decision { - crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { - b.emit_plugin_invoke(type_id, method_id, argc, false); - crate::jit::observe::lower_plugin_invoke(&box_type, "push", type_id, method_id, argc); - } - crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { - crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64"], "allow", "mapped_symbol"); - b.emit_host_call(&symbol, argc, false); - } - _ => { - // Fallback to existing hostcall path - let sym = crate::jit::r#extern::collections::SYM_ARRAY_PUSH_H; - crate::jit::observe::lower_hostcall(sym, argc, &["Handle","I64"], "fallback", "policy_or_unknown"); - b.emit_host_call(sym, argc, false); - } - } - } else { - // No receiver param index - let arr_idx = -1; - b.emit_const_i64(arr_idx); - b.emit_const_i64(val); - let sym = crate::jit::r#extern::collections::SYM_ARRAY_PUSH; - crate::jit::observe::lower_hostcall(sym, argc, &["I64","I64"], "fallback", "receiver_not_param"); - b.emit_host_call(sym, argc, false); - } - } - "size" => { - let argc = 1usize; - if let Some(pidx) = self.param_index.get(array).copied() { - b.emit_param_i64(pidx); - let decision = crate::jit::policy::invoke::decide_box_method("MapBox", "size", argc, dst.is_some()); - match decision { - crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { - b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some()); - crate::jit::observe::lower_plugin_invoke(&box_type, "size", type_id, method_id, argc); - } - crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { - crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle"], "allow", "mapped_symbol"); - b.emit_host_call(&symbol, argc, dst.is_some()); - } - _ => { - let sym = crate::jit::r#extern::collections::SYM_MAP_SIZE_H; - crate::jit::observe::lower_hostcall(sym, argc, &["Handle"], "fallback", "policy_or_unknown"); - b.emit_host_call(sym, argc, dst.is_some()); - } - } - } else { - let map_idx = -1; - b.emit_const_i64(map_idx); - let sym = crate::jit::r#extern::collections::SYM_MAP_SIZE; - crate::jit::observe::lower_hostcall(sym, argc, &["I64"], "fallback", "receiver_not_param"); - b.emit_host_call(sym, argc, dst.is_some()); - } - } - "get" => { - let argc = 2usize; - if let Some(pidx) = self.param_index.get(array).copied() { - b.emit_param_i64(pidx); - if let Some(k) = args.get(0).and_then(|v| self.known_i64.get(v)).copied() { b.emit_const_i64(k); } else if let Some(kvid) = args.get(0) { self.push_value_if_known_or_param(b, kvid); } else { b.emit_const_i64(0); } - let decision = crate::jit::policy::invoke::decide_box_method("MapBox", "get", argc, dst.is_some()); - match decision { - crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { - b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some()); - crate::jit::observe::lower_plugin_invoke(&box_type, "get", type_id, method_id, argc); - } - crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { - crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64"], "allow", "mapped_symbol"); - b.emit_host_call(&symbol, argc, dst.is_some()); - } - _ => { - let sym = crate::jit::r#extern::collections::SYM_MAP_GET_H; - crate::jit::observe::lower_hostcall(sym, argc, &["Handle","I64"], "fallback", "policy_or_unknown"); - b.emit_host_call(sym, argc, dst.is_some()); - } - } - } else { - let sym = crate::jit::r#extern::collections::SYM_MAP_GET_H; - crate::jit::observe::lower_hostcall(sym, argc, &["I64","I64"], "fallback", "receiver_not_param"); - b.emit_host_call(sym, argc, dst.is_some()); - } - } - "set" => { - let argc = 3usize; - if let Some(pidx) = self.param_index.get(array).copied() { - let key = args.get(0).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0); - let val = args.get(1).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0); - b.emit_param_i64(pidx); - b.emit_const_i64(key); - b.emit_const_i64(val); - let decision = crate::jit::policy::invoke::decide_box_method("MapBox", "set", argc, false); - match decision { - crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { - b.emit_plugin_invoke(type_id, method_id, argc, false); - crate::jit::observe::lower_plugin_invoke(&box_type, "set", type_id, method_id, argc); - } - crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { - crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64","I64"], "allow", "mapped_symbol"); - b.emit_host_call(&symbol, argc, false); - } - _ => { - let sym = crate::jit::r#extern::collections::SYM_MAP_SET_H; - crate::jit::observe::lower_hostcall(sym, argc, &["Handle","I64","I64"], "fallback", "policy_or_unknown"); - b.emit_host_call(sym, argc, false); - } - } - } else { - let sym = crate::jit::r#extern::collections::SYM_MAP_SET; - crate::jit::observe::lower_hostcall(sym, argc, &["I64","I64","I64"], "fallback", "receiver_not_param"); - b.emit_host_call(sym, argc, false); - } - } - "charCodeAt" => { - // String.charCodeAt(index) - if let Some(pidx) = self.param_index.get(array).copied() { - let idx = args.get(0).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0); + Err(reason) => { crate::jit::events::emit_lower( - serde_json::json!({"id": crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, "decision":"allow", "reason":"sig_ok", "argc":2, "arg_types":["Handle","I64"]}), - "hostcall","" - ); - b.emit_param_i64(pidx); - b.emit_const_i64(idx); - b.emit_host_call(crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, 2, dst.is_some()); - } else { - crate::jit::events::emit_lower( - serde_json::json!({"id": crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, "decision":"fallback", "reason":"receiver_not_param", "argc":2, "arg_types":["Handle","I64"]}), - "hostcall","" + serde_json::json!({ + "id": sym, + "decision": "fallback", + "reason": reason, + "argc": observed.len(), + "arg_types": arg_types + }), + "hostcall", + "" ); } } - "has" => { - let argc = 2usize; - if let Some(pidx) = self.param_index.get(array).copied() { - let key = args.get(0).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0); - b.emit_param_i64(pidx); - b.emit_const_i64(key); - let decision = crate::jit::policy::invoke::decide_box_method("MapBox", "has", argc, dst.is_some()); - match decision { - crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { - b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some()); - crate::jit::observe::lower_plugin_invoke(&box_type, "has", type_id, method_id, argc); - } - crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { - crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64"], "allow", "mapped_symbol"); - b.emit_host_call(&symbol, argc, dst.is_some()); - } - _ => { - let sym = crate::jit::r#extern::collections::SYM_MAP_HAS_H; - crate::jit::observe::lower_hostcall(sym, argc, &["Handle","I64"], "fallback", "policy_or_unknown"); - b.emit_host_call(sym, argc, dst.is_some()); - } - } - } - } - _ => {} + // no-op: VM側で実行される } - */ + "isEmpty" | "empty" => { + if let Some(pidx) = self.param_index.get(array).copied() { + crate::jit::events::emit( + "hostcall","",None,None, + serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, "decision":"allow", "reason":"sig_ok", "argc":1, "arg_types":["Handle"]}) + ); + b.emit_param_i64(pidx); + // returns i64 0/1 + b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, 1, dst.is_some()); + } else { + crate::jit::events::emit_lower( + serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, "decision":"fallback", "reason":"receiver_not_param", "argc":1, "arg_types":["Handle"]}), + "hostcall","" + ); + } + } + "push" => { + // argc=2: (array, value) + let argc = 2usize; + let val = args.get(0).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0); + if let Some(pidx) = self.param_index.get(array).copied() { + // Prepare args + b.emit_param_i64(pidx); + b.emit_const_i64(val); + // Decide policy + let decision = crate::jit::policy::invoke::decide_box_method("ArrayBox", "push", argc, false); + match decision { + crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { + b.emit_plugin_invoke(type_id, method_id, argc, false); + crate::jit::observe::lower_plugin_invoke(&box_type, "push", type_id, method_id, argc); + } + crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { + crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64"], "allow", "mapped_symbol"); + b.emit_host_call(&symbol, argc, false); + } + _ => { + // Fallback to existing hostcall path + let sym = crate::jit::r#extern::collections::SYM_ARRAY_PUSH_H; + crate::jit::observe::lower_hostcall(sym, argc, &["Handle","I64"], "fallback", "policy_or_unknown"); + b.emit_host_call(sym, argc, false); + } + } + } else { + // No receiver param index + let arr_idx = -1; + b.emit_const_i64(arr_idx); + b.emit_const_i64(val); + let sym = crate::jit::r#extern::collections::SYM_ARRAY_PUSH; + crate::jit::observe::lower_hostcall(sym, argc, &["I64","I64"], "fallback", "receiver_not_param"); + b.emit_host_call(sym, argc, false); + } + } + "size" => { + let argc = 1usize; + if let Some(pidx) = self.param_index.get(array).copied() { + b.emit_param_i64(pidx); + let decision = crate::jit::policy::invoke::decide_box_method("MapBox", "size", argc, dst.is_some()); + match decision { + crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { + b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some()); + crate::jit::observe::lower_plugin_invoke(&box_type, "size", type_id, method_id, argc); + } + crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { + crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle"], "allow", "mapped_symbol"); + b.emit_host_call(&symbol, argc, dst.is_some()); + } + _ => { + let sym = crate::jit::r#extern::collections::SYM_MAP_SIZE_H; + crate::jit::observe::lower_hostcall(sym, argc, &["Handle"], "fallback", "policy_or_unknown"); + b.emit_host_call(sym, argc, dst.is_some()); + } + } + } else { + let map_idx = -1; + b.emit_const_i64(map_idx); + let sym = crate::jit::r#extern::collections::SYM_MAP_SIZE; + crate::jit::observe::lower_hostcall(sym, argc, &["I64"], "fallback", "receiver_not_param"); + b.emit_host_call(sym, argc, dst.is_some()); + } + } + "get" => { + let argc = 2usize; + if let Some(pidx) = self.param_index.get(array).copied() { + b.emit_param_i64(pidx); + if let Some(k) = args.get(0).and_then(|v| self.known_i64.get(v)).copied() { b.emit_const_i64(k); } else if let Some(kvid) = args.get(0) { self.push_value_if_known_or_param(b, kvid); } else { b.emit_const_i64(0); } + let decision = crate::jit::policy::invoke::decide_box_method("MapBox", "get", argc, dst.is_some()); + match decision { + crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { + b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some()); + crate::jit::observe::lower_plugin_invoke(&box_type, "get", type_id, method_id, argc); + } + crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { + crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64"], "allow", "mapped_symbol"); + b.emit_host_call(&symbol, argc, dst.is_some()); + } + _ => { + let sym = crate::jit::r#extern::collections::SYM_MAP_GET_H; + crate::jit::observe::lower_hostcall(sym, argc, &["Handle","I64"], "fallback", "policy_or_unknown"); + b.emit_host_call(sym, argc, dst.is_some()); + } + } + } else { + let sym = crate::jit::r#extern::collections::SYM_MAP_GET_H; + crate::jit::observe::lower_hostcall(sym, argc, &["I64","I64"], "fallback", "receiver_not_param"); + b.emit_host_call(sym, argc, dst.is_some()); + } + } + "set" => { + let argc = 3usize; + if let Some(pidx) = self.param_index.get(array).copied() { + let key = args.get(0).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0); + let val = args.get(1).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0); + b.emit_param_i64(pidx); + b.emit_const_i64(key); + b.emit_const_i64(val); + let decision = crate::jit::policy::invoke::decide_box_method("MapBox", "set", argc, false); + match decision { + crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { + b.emit_plugin_invoke(type_id, method_id, argc, false); + crate::jit::observe::lower_plugin_invoke(&box_type, "set", type_id, method_id, argc); + } + crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { + crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64","I64"], "allow", "mapped_symbol"); + b.emit_host_call(&symbol, argc, false); + } + _ => { + let sym = crate::jit::r#extern::collections::SYM_MAP_SET_H; + crate::jit::observe::lower_hostcall(sym, argc, &["Handle","I64","I64"], "fallback", "policy_or_unknown"); + b.emit_host_call(sym, argc, false); + } + } + } else { + let sym = crate::jit::r#extern::collections::SYM_MAP_SET; + crate::jit::observe::lower_hostcall(sym, argc, &["I64","I64","I64"], "fallback", "receiver_not_param"); + b.emit_host_call(sym, argc, false); + } + } + "charCodeAt" => { + // String.charCodeAt(index) + if let Some(pidx) = self.param_index.get(array).copied() { + let idx = args.get(0).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0); + crate::jit::events::emit_lower( + serde_json::json!({"id": crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, "decision":"allow", "reason":"sig_ok", "argc":2, "arg_types":["Handle","I64"]}), + "hostcall","" + ); + b.emit_param_i64(pidx); + b.emit_const_i64(idx); + b.emit_host_call(crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, 2, dst.is_some()); + } else { + crate::jit::events::emit_lower( + serde_json::json!({"id": crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, "decision":"fallback", "reason":"receiver_not_param", "argc":2, "arg_types":["Handle","I64"]}), + "hostcall","" + ); + } + } + "has" => { + let argc = 2usize; + if let Some(pidx) = self.param_index.get(array).copied() { + let key = args.get(0).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0); + b.emit_param_i64(pidx); + b.emit_const_i64(key); + let decision = crate::jit::policy::invoke::decide_box_method("MapBox", "has", argc, dst.is_some()); + match decision { + crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { + b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some()); + crate::jit::observe::lower_plugin_invoke(&box_type, "has", type_id, method_id, argc); + } + crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { + crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64"], "allow", "mapped_symbol"); + b.emit_host_call(&symbol, argc, dst.is_some()); + } + _ => { + let sym = crate::jit::r#extern::collections::SYM_MAP_HAS_H; + crate::jit::observe::lower_hostcall(sym, argc, &["Handle","I64"], "fallback", "policy_or_unknown"); + b.emit_host_call(sym, argc, dst.is_some()); + } + } + } + } + _ => {} + } + */ _ => {} } Ok(()) diff --git a/src/jit/lower/core/analysis.rs b/src/jit/lower/core/analysis.rs index 3998b569..bb4bfdd1 100644 --- a/src/jit/lower/core/analysis.rs +++ b/src/jit/lower/core/analysis.rs @@ -27,18 +27,28 @@ impl LowerCore { if let Some(block) = func.blocks.get(bb) { for ins in block.instructions.iter() { match ins { - MirInstruction::Compare { dst, .. } => { self.bool_values.insert(*dst); } - MirInstruction::Const { dst, value } => { - if let crate::mir::ConstValue::Bool(_) = value { self.bool_values.insert(*dst); } + MirInstruction::Compare { dst, .. } => { + self.bool_values.insert(*dst); + } + MirInstruction::Const { dst, value } => { + if let crate::mir::ConstValue::Bool(_) = value { + self.bool_values.insert(*dst); + } + } + MirInstruction::Copy { dst, src } => { + copy_edges.push((*dst, *src)); } - MirInstruction::Copy { dst, src } => { copy_edges.push((*dst, *src)); } MirInstruction::Phi { dst, inputs } => { self.phi_values.insert(*dst); let ins: Vec = inputs.iter().map(|(_, v)| *v).collect(); phi_defs.push((*dst, ins)); } - MirInstruction::Store { ptr, value } => { stores.push((*ptr, *value)); } - MirInstruction::Load { dst, ptr } => { loads.push((*dst, *ptr)); } + MirInstruction::Store { ptr, value } => { + stores.push((*ptr, *value)); + } + MirInstruction::Load { dst, ptr } => { + loads.push((*dst, *ptr)); + } _ => {} } } @@ -76,7 +86,9 @@ impl LowerCore { } // PHI closure for value booleans for (dst, inputs) in phi_defs.iter() { - if inputs.iter().all(|v| self.bool_values.contains(v)) && !self.bool_values.contains(dst) { + if inputs.iter().all(|v| self.bool_values.contains(v)) + && !self.bool_values.contains(dst) + { self.bool_values.insert(*dst); self.bool_phi_values.insert(*dst); changed = true; @@ -84,7 +96,9 @@ impl LowerCore { } // PHI closure for pointer aliases for (dst, inputs) in phi_defs.iter() { - if inputs.iter().all(|v| store_bool_ptrs.contains(v)) && !store_bool_ptrs.contains(dst) { + if inputs.iter().all(|v| store_bool_ptrs.contains(v)) + && !store_bool_ptrs.contains(dst) + { store_bool_ptrs.insert(*dst); changed = true; } @@ -96,14 +110,25 @@ impl LowerCore { for (dst, inputs) in phi_defs.iter() { total_phi_slots += 1; let used_as_branch = func.blocks.values().any(|bbx| { - if let Some(MirInstruction::Branch { condition, .. }) = &bbx.terminator { condition == dst } else { false } + if let Some(MirInstruction::Branch { condition, .. }) = &bbx.terminator { + condition == dst + } else { + false + } }); let is_b1 = self.bool_phi_values.contains(dst) || inputs.iter().all(|v| { - self.bool_values.contains(v) || self.known_i64.get(v).map(|&iv| iv == 0 || iv == 1).unwrap_or(false) + self.bool_values.contains(v) + || self + .known_i64 + .get(v) + .map(|&iv| iv == 0 || iv == 1) + .unwrap_or(false) }) || used_as_branch; - if is_b1 { total_phi_b1_slots += 1; } + if is_b1 { + total_phi_b1_slots += 1; + } } if total_phi_slots > 0 { crate::jit::rt::phi_total_inc(total_phi_slots as u64); diff --git a/src/jit/lower/core/cfg.rs b/src/jit/lower/core/cfg.rs index 7caf26c9..8c6cc750 100644 --- a/src/jit/lower/core/cfg.rs +++ b/src/jit/lower/core/cfg.rs @@ -1,6 +1,6 @@ -use crate::mir::{BasicBlockId, MirFunction, MirInstruction}; use super::super::builder::IRBuilder; use super::LowerCore; +use crate::mir::{BasicBlockId, MirFunction, MirInstruction}; use std::collections::HashMap; impl LowerCore { @@ -12,13 +12,19 @@ impl LowerCore { enable_phi_min: bool, ) -> HashMap> { let mut succ_phi_order: HashMap> = HashMap::new(); - if !enable_phi_min { return succ_phi_order; } + if !enable_phi_min { + return succ_phi_order; + } for (bb_id, bb) in func.blocks.iter() { let mut order: Vec = Vec::new(); for ins in bb.instructions.iter() { - if let MirInstruction::Phi { dst, .. } = ins { order.push(*dst); } + if let MirInstruction::Phi { dst, .. } = ins { + order.push(*dst); + } + } + if !order.is_empty() { + succ_phi_order.insert(*bb_id, order); } - if !order.is_empty() { succ_phi_order.insert(*bb_id, order); } } // Pre-declare block parameter counts per successor to avoid late appends for (succ, order) in succ_phi_order.iter() { @@ -36,41 +42,80 @@ impl LowerCore { blocks_len: usize, enable_phi_min: bool, ) { - if std::env::var("NYASH_JIT_DUMP").ok().as_deref() != Some("1") { return; } + if std::env::var("NYASH_JIT_DUMP").ok().as_deref() != Some("1") { + return; + } let succs = succ_phi_order.len(); - eprintln!("[JIT] cfg: blocks={} phi_succ={} (phi_min={})", blocks_len, succs, enable_phi_min); + eprintln!( + "[JIT] cfg: blocks={} phi_succ={} (phi_min={})", + blocks_len, succs, enable_phi_min + ); if enable_phi_min { let mut total_phi_slots: usize = 0; let mut total_phi_b1_slots: usize = 0; for (succ, order) in succ_phi_order.iter() { - let mut preds_set: std::collections::BTreeSet = std::collections::BTreeSet::new(); + let mut preds_set: std::collections::BTreeSet = + std::collections::BTreeSet::new(); let mut phi_lines: Vec = Vec::new(); if let Some(bb_succ) = func.blocks.get(succ) { for ins in bb_succ.instructions.iter() { if let MirInstruction::Phi { dst, inputs } = ins { - for (pred, _) in inputs.iter() { preds_set.insert(pred.0 as i64); } + for (pred, _) in inputs.iter() { + preds_set.insert(pred.0 as i64); + } let mut pairs: Vec = Vec::new(); - for (pred, val) in inputs.iter() { pairs.push(format!("{}:{}", pred.0, val.0)); } + for (pred, val) in inputs.iter() { + pairs.push(format!("{}:{}", pred.0, val.0)); + } let used_as_branch = func.blocks.values().any(|bbx| { - if let Some(MirInstruction::Branch { condition, .. }) = &bbx.terminator { condition == dst } else { false } + if let Some(MirInstruction::Branch { condition, .. }) = + &bbx.terminator + { + condition == dst + } else { + false + } }); let is_b1 = self.bool_phi_values.contains(dst) || inputs.iter().all(|(_, v)| { - self.bool_values.contains(v) || self.known_i64.get(v).map(|&iv| iv == 0 || iv == 1).unwrap_or(false) + self.bool_values.contains(v) + || self + .known_i64 + .get(v) + .map(|&iv| iv == 0 || iv == 1) + .unwrap_or(false) }) || used_as_branch; - if is_b1 { total_phi_b1_slots += 1; } + if is_b1 { + total_phi_b1_slots += 1; + } total_phi_slots += 1; - phi_lines.push(format!(" phi: bb={} dst={} inputs=[{}] (b1={})", - succ.0, dst.0, pairs.join(","), is_b1)); + phi_lines.push(format!( + " phi: bb={} dst={} inputs=[{}] (b1={})", + succ.0, + dst.0, + pairs.join(","), + is_b1 + )); } } } - let preds_list: Vec = preds_set.into_iter().map(|p| p.to_string()).collect(); - eprintln!("[JIT] phi: bb={} slots={} preds={}", succ.0, order.len(), preds_list.join("|")); - for ln in phi_lines { eprintln!("[JIT]{}", ln); } + let preds_list: Vec = + preds_set.into_iter().map(|p| p.to_string()).collect(); + eprintln!( + "[JIT] phi: bb={} slots={} preds={}", + succ.0, + order.len(), + preds_list.join("|") + ); + for ln in phi_lines { + eprintln!("[JIT]{}", ln); + } } - eprintln!("[JIT] phi_summary: total_slots={} b1_slots={}", total_phi_slots, total_phi_b1_slots); + eprintln!( + "[JIT] phi_summary: total_slots={} b1_slots={}", + total_phi_slots, total_phi_b1_slots + ); } } } @@ -97,15 +142,20 @@ impl LowerCore { for ins in succ.instructions.iter() { if let I::Const { dst, value } = ins { if dst == v { - if let crate::mir::ConstValue::Integer(k) = value { return Some(*k); } + if let crate::mir::ConstValue::Integer(k) = value { + return Some(*k); + } } } } } None }; - if let (Some(bb_then), Some(bb_else)) = (func.blocks.get(then_bb), func.blocks.get(else_bb)) { - if let (Some(k_then), Some(k_else)) = (succ_returns_const(bb_then), succ_returns_const(bb_else)) { + if let (Some(bb_then), Some(bb_else)) = (func.blocks.get(then_bb), func.blocks.get(else_bb)) + { + if let (Some(k_then), Some(k_else)) = + (succ_returns_const(bb_then), succ_returns_const(bb_else)) + { self.push_value_if_known_or_param(builder, condition); builder.emit_const_i64(k_then); builder.emit_const_i64(k_else); @@ -114,17 +164,23 @@ impl LowerCore { fastpath_done = true; } } - if fastpath_done { return; } + if fastpath_done { + return; + } // Otherwise, emit CFG branch with optional PHI(min) argument wiring self.push_value_if_known_or_param(builder, condition); let then_index = bb_ids.iter().position(|x| x == then_bb).unwrap_or(0); let else_index = bb_ids.iter().position(|x| x == else_bb).unwrap_or(0); if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") { - eprintln!("[LowerCore] br_if: cur_bb={} then_idx={} else_idx={}", bb_id.0, then_index, else_index); + eprintln!( + "[LowerCore] br_if: cur_bb={} then_idx={} else_idx={}", + bb_id.0, then_index, else_index + ); } if enable_phi_min { - let mut then_n = 0usize; let mut else_n = 0usize; + let mut then_n = 0usize; + let mut else_n = 0usize; if let Some(order) = succ_phi_order.get(then_bb) { let mut cnt = 0usize; if let Some(bb_succ) = func.blocks.get(then_bb) { @@ -132,7 +188,9 @@ impl LowerCore { for ins in bb_succ.instructions.iter() { if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins { if d2 == dst { - if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == &bb_id) { + if let Some((_, val)) = + inputs.iter().find(|(pred, _)| pred == &bb_id) + { self.push_value_if_known_or_param(builder, val); cnt += 1; } @@ -141,7 +199,9 @@ impl LowerCore { } } } - if cnt > 0 { builder.ensure_block_params_i64(then_index, cnt); } + if cnt > 0 { + builder.ensure_block_params_i64(then_index, cnt); + } then_n = cnt; } if let Some(order) = succ_phi_order.get(else_bb) { @@ -151,7 +211,9 @@ impl LowerCore { for ins in bb_succ.instructions.iter() { if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins { if d2 == dst { - if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == &bb_id) { + if let Some((_, val)) = + inputs.iter().find(|(pred, _)| pred == &bb_id) + { self.push_value_if_known_or_param(builder, val); cnt += 1; } @@ -160,7 +222,9 @@ impl LowerCore { } } } - if cnt > 0 { builder.ensure_block_params_i64(else_index, cnt); } + if cnt > 0 { + builder.ensure_block_params_i64(else_index, cnt); + } else_n = cnt; } builder.br_if_with_args(then_index, else_index, then_n, else_n); @@ -182,7 +246,10 @@ impl LowerCore { ) { let target_index = bb_ids.iter().position(|x| x == target).unwrap_or(0); if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") { - eprintln!("[LowerCore] jump: cur_bb={} target_idx={}", bb_id.0, target_index); + eprintln!( + "[LowerCore] jump: cur_bb={} target_idx={}", + bb_id.0, target_index + ); } if enable_phi_min { let mut n = 0usize; @@ -193,7 +260,9 @@ impl LowerCore { for ins in bb_succ.instructions.iter() { if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins { if d2 == dst { - if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == &bb_id) { + if let Some((_, val)) = + inputs.iter().find(|(pred, _)| pred == &bb_id) + { self.push_value_if_known_or_param(builder, val); cnt += 1; } @@ -202,7 +271,9 @@ impl LowerCore { } } } - if cnt > 0 { builder.ensure_block_params_i64(target_index, cnt); } + if cnt > 0 { + builder.ensure_block_params_i64(target_index, cnt); + } n = cnt; } builder.jump_with_args(target_index, n); diff --git a/src/jit/lower/core/ops_ext.rs b/src/jit/lower/core/ops_ext.rs index e044da5f..7c8823dd 100644 --- a/src/jit/lower/core/ops_ext.rs +++ b/src/jit/lower/core/ops_ext.rs @@ -17,38 +17,78 @@ impl LowerCore { let m = method; if bt == "PyRuntimeBox" && (m == "import") { let argc = 1 + args.len(); - if let Some(pidx) = self.param_index.get(box_val).copied() { b.emit_param_i64(pidx); } else { self.push_value_if_known_or_param(b, box_val); } - let decision = crate::jit::policy::invoke::decide_box_method(&bt, m, argc, dst.is_some()); - if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } = decision { + if let Some(pidx) = self.param_index.get(box_val).copied() { + b.emit_param_i64(pidx); + } else { + self.push_value_if_known_or_param(b, box_val); + } + let decision = + crate::jit::policy::invoke::decide_box_method(&bt, m, argc, dst.is_some()); + if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { + type_id, + method_id, + box_type, + .. + } = decision + { b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some()); crate::jit::observe::lower_plugin_invoke(&box_type, m, type_id, method_id, argc); - if let Some(d) = dst { self.handle_values.insert(*d); } - } else { if dst.is_some() { b.emit_const_i64(0); } } + if let Some(d) = dst { + self.handle_values.insert(*d); + } + } else { + if dst.is_some() { + b.emit_const_i64(0); + } + } } else if bt == "PyRuntimeBox" && (m == "getattr" || m == "call") { let argc = 1 + args.len(); - if let Some(pidx) = self.param_index.get(box_val).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } - for a in args.iter() { self.push_value_if_known_or_param(b, a); } + if let Some(pidx) = self.param_index.get(box_val).copied() { + b.emit_param_i64(pidx); + } else { + b.emit_const_i64(-1); + } + for a in args.iter() { + self.push_value_if_known_or_param(b, a); + } b.emit_plugin_invoke_by_name(m, argc, dst.is_some()); if let Some(d) = dst { self.handle_values.insert(*d); - let slot = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } } else if self.handle_values.contains(box_val) && (m == "getattr" || m == "call") { let argc = 1 + args.len(); - if let Some(slot) = self.local_index.get(box_val).copied() { b.load_local_i64(slot); } else { b.emit_const_i64(-1); } - for a in args.iter() { self.push_value_if_known_or_param(b, a); } + if let Some(slot) = self.local_index.get(box_val).copied() { + b.load_local_i64(slot); + } else { + b.emit_const_i64(-1); + } + for a in args.iter() { + self.push_value_if_known_or_param(b, a); + } b.emit_plugin_invoke_by_name(m, argc, dst.is_some()); if let Some(d) = dst { self.handle_values.insert(*d); - let slot = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } } else if (bt == "PyRuntimeBox" && (m == "birth" || m == "eval")) || (bt == "IntegerBox" && m == "birth") || (bt == "StringBox" && m == "birth") - || (bt == "ConsoleBox" && m == "birth") { - if dst.is_some() { b.emit_const_i64(0); } + || (bt == "ConsoleBox" && m == "birth") + { + if dst.is_some() { + b.emit_const_i64(0); + } } else { self.unsupported += 1; } @@ -65,10 +105,19 @@ impl LowerCore { _func: &MirFunction, ) -> Result<(), String> { // env.console.log/warn/error/println → ConsoleBox に委譲(host-bridge有効時は直接ログ) - if iface_name == "env.console" && (method_name == "log" || method_name == "println" || method_name == "warn" || method_name == "error") { + if iface_name == "env.console" + && (method_name == "log" + || method_name == "println" + || method_name == "warn" + || method_name == "error") + { if std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1") { // a0: 先頭引数を最小限で積む - if let Some(arg0) = args.get(0) { self.push_value_if_known_or_param(b, arg0); } else { b.emit_const_i64(0); } + if let Some(arg0) = args.get(0) { + self.push_value_if_known_or_param(b, arg0); + } else { + b.emit_const_i64(0); + } let sym = match method_name { "warn" => crate::jit::r#extern::host_bridge::SYM_HOST_CONSOLE_WARN, "error" => crate::jit::r#extern::host_bridge::SYM_HOST_CONSOLE_ERROR, @@ -78,38 +127,82 @@ impl LowerCore { return Ok(()); } // Ensure we have a Console handle (hostcall birth shim) - b.emit_host_call(crate::jit::r#extern::collections::SYM_CONSOLE_BIRTH_H, 0, true); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_CONSOLE_BIRTH_H, + 0, + true, + ); // a1: first argument best-effort - if let Some(arg0) = args.get(0) { self.push_value_if_known_or_param(b, arg0); } + if let Some(arg0) = args.get(0) { + self.push_value_if_known_or_param(b, arg0); + } // Resolve plugin invoke for ConsoleBox.method - let decision = crate::jit::policy::invoke::decide_box_method("ConsoleBox", method_name, 2, dst.is_some()); - if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } = decision { + let decision = crate::jit::policy::invoke::decide_box_method( + "ConsoleBox", + method_name, + 2, + dst.is_some(), + ); + if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { + type_id, + method_id, + box_type, + .. + } = decision + { b.emit_plugin_invoke(type_id, method_id, 2, dst.is_some()); - crate::jit::observe::lower_plugin_invoke(&box_type, method_name, type_id, method_id, 2); - } else if dst.is_some() { b.emit_const_i64(0); } + crate::jit::observe::lower_plugin_invoke( + &box_type, + method_name, + type_id, + method_id, + 2, + ); + } else if dst.is_some() { + b.emit_const_i64(0); + } return Ok(()); } // env.future.await(fut) → await_h + ok_h/err_h select if iface_name == "env.future" && method_name == "await" { if let Some(arg0) = args.get(0) { - if let Some(pidx) = self.param_index.get(arg0).copied() { b.emit_param_i64(pidx); } - else if let Some(slot) = self.local_index.get(arg0).copied() { b.load_local_i64(slot); } - else if let Some(v) = self.known_i64.get(arg0).copied() { b.emit_const_i64(v); } - else { b.emit_const_i64(-1); } - } else { b.emit_const_i64(-1); } + if let Some(pidx) = self.param_index.get(arg0).copied() { + b.emit_param_i64(pidx); + } else if let Some(slot) = self.local_index.get(arg0).copied() { + b.load_local_i64(slot); + } else if let Some(v) = self.known_i64.get(arg0).copied() { + b.emit_const_i64(v); + } else { + b.emit_const_i64(-1); + } + } else { + b.emit_const_i64(-1); + } // await_h → handle(0 timeout) b.emit_host_call(crate::jit::r#extern::r#async::SYM_FUTURE_AWAIT_H, 1, true); - let hslot = { let id = self.next_local; self.next_local += 1; id }; + let hslot = { + let id = self.next_local; + self.next_local += 1; + id + }; b.store_local_i64(hslot); // ok_h(handle) b.load_local_i64(hslot); b.emit_host_call(crate::jit::r#extern::result::SYM_RESULT_OK_H, 1, true); - let ok_slot = { let id = self.next_local; self.next_local += 1; id }; + let ok_slot = { + let id = self.next_local; + self.next_local += 1; + id + }; b.store_local_i64(ok_slot); // err_h(0) b.emit_const_i64(0); b.emit_host_call(crate::jit::r#extern::result::SYM_RESULT_ERR_H, 1, true); - let err_slot = { let id = self.next_local; self.next_local += 1; id }; + let err_slot = { + let id = self.next_local; + self.next_local += 1; + id + }; b.store_local_i64(err_slot); // select(handle==0 ? err : ok) b.load_local_i64(hslot); @@ -120,7 +213,11 @@ impl LowerCore { b.emit_select_i64(); if let Some(d) = dst { self.handle_values.insert(*d); - let slot = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } return Ok(()); @@ -129,20 +226,42 @@ impl LowerCore { if iface_name == "env.future" && method_name == "spawn_instance" { // a0 receiver if let Some(recv) = args.get(0) { - if let Some(pidx) = self.param_index.get(recv).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } - } else { b.emit_const_i64(-1); } + if let Some(pidx) = self.param_index.get(recv).copied() { + b.emit_param_i64(pidx); + } else { + b.emit_const_i64(-1); + } + } else { + b.emit_const_i64(-1); + } // a1 method name (best-effort) - if let Some(meth) = args.get(1) { self.push_value_if_known_or_param(b, meth); } else { b.emit_const_i64(0); } + if let Some(meth) = args.get(1) { + self.push_value_if_known_or_param(b, meth); + } else { + b.emit_const_i64(0); + } // a2 first payload (optional) - if let Some(a2) = args.get(2) { self.push_value_if_known_or_param(b, a2); } else { b.emit_const_i64(0); } + if let Some(a2) = args.get(2) { + self.push_value_if_known_or_param(b, a2); + } else { + b.emit_const_i64(0); + } // argc_total = explicit args including method name and payload (exclude receiver) let argc_total = args.len().saturating_sub(1).max(0); b.emit_const_i64(argc_total as i64); // call spawn shim → Future handle - b.emit_host_call(crate::jit::r#extern::r#async::SYM_FUTURE_SPAWN_INSTANCE3_I64, 4, true); + b.emit_host_call( + crate::jit::r#extern::r#async::SYM_FUTURE_SPAWN_INSTANCE3_I64, + 4, + true, + ); if let Some(d) = dst { self.handle_values.insert(*d); - let slot = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } return Ok(()); @@ -175,10 +294,11 @@ impl LowerCore { ); return Ok(true); } - // 非コアBox(例: EguiBox など)は共通処理として名前ベースの plugin_invoke にフォールバック + // 非コアBox(例: EguiBox など)は共通処理として名前ベースの plugin_invoke にフォールバック // コアBoxの目安: StringBox/ArrayBox/MapBox(この後の分岐で処理)と PyRuntimeBox(専用分岐済) if let Some(bt) = self.box_type_map.get(array).cloned() { - let is_core = bt == "StringBox" || bt == "ArrayBox" || bt == "MapBox" || bt == "PyRuntimeBox"; + let is_core = + bt == "StringBox" || bt == "ArrayBox" || bt == "MapBox" || bt == "PyRuntimeBox"; if !is_core { // receiver: prefer existing local slot/param; ensure a valid runtime handle if let Some(slot) = self.local_index.get(array).copied() { @@ -192,7 +312,11 @@ impl LowerCore { } // push up to 2 args (name-shim supports at most 2 positional args beyond receiver) let take_n = core::cmp::min(args.len(), 2); - for i in 0..take_n { if let Some(v) = args.get(i) { self.push_value_if_known_or_param(b, v); } } + for i in 0..take_n { + if let Some(v) = args.get(i) { + self.push_value_if_known_or_param(b, v); + } + } let argc = 1 + take_n; b.emit_plugin_invoke_by_name(method, argc, dst.is_some()); if std::env::var("NYASH_JIT_TRACE_LOWER").ok().as_deref() == Some("1") { @@ -203,12 +327,17 @@ impl LowerCore { "reason": "plugin_invoke_by_name", "argc": argc }), - "plugin","" + "plugin", + "", ); } if let Some(d) = dst { self.handle_values.insert(d); - let slot = *self.local_index.entry(d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } if std::env::var("NYASH_JIT_TRACE_LOWER").ok().as_deref() == Some("1") { @@ -218,7 +347,7 @@ impl LowerCore { } } // Builtins-to-plugin path (subset for String/Array/Map critical ops) -// Builtins-to-plugin path (subset for String/Array/Map critical ops) + // Builtins-to-plugin path (subset for String/Array/Map critical ops) if std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1") { // StringBox (length/is_empty/charCodeAt) if matches!(method, "length" | "is_empty" | "charCodeAt") { @@ -226,44 +355,137 @@ impl LowerCore { // Prefer robust fallback path (param/local/literal/handle.of) if let Some(pidx) = self.param_index.get(array).copied() { self.emit_len_with_fallback_param(b, pidx); - if let Some(d) = dst { let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); b.store_local_i64(slot); } + if let Some(d) = dst { + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); + b.store_local_i64(slot); + } return Ok(true); } if let Some(slot) = self.local_index.get(array).copied() { self.emit_len_with_fallback_local_handle(b, slot); - if let Some(d) = dst { let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); b.store_local_i64(slot); } + if let Some(d) = dst { + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); + b.store_local_i64(slot); + } return Ok(true); } // literal? let mut lit: Option = None; - for (_bid, bb) in func.blocks.iter() { for ins in bb.instructions.iter() { if let crate::mir::MirInstruction::NewBox { dst, box_type, args } = ins { if dst == array && box_type == "StringBox" && args.len() == 1 { if let Some(src) = args.get(0) { if let Some(s) = self.known_str.get(src).cloned() { lit = Some(s); break; } } } } } if lit.is_some() { break; } } + for (_bid, bb) in func.blocks.iter() { + for ins in bb.instructions.iter() { + if let crate::mir::MirInstruction::NewBox { + dst, + box_type, + args, + } = ins + { + if dst == array && box_type == "StringBox" && args.len() == 1 { + if let Some(src) = args.get(0) { + if let Some(s) = self.known_str.get(src).cloned() { + lit = Some(s); + break; + } + } + } + } + } + if lit.is_some() { + break; + } + } if let Some(s) = lit { let n = s.len() as i64; b.emit_const_i64(n); if let Some(d) = dst { self.known_i64.insert(d, n); - let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } return Ok(true); } // last resort: handle.of + any.length_h - self.push_value_if_known_or_param(b, array); b.emit_host_call(crate::jit::r#extern::handles::SYM_HANDLE_OF, 1, true); b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true); - if let Some(d) = dst { let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); b.store_local_i64(slot); } + self.push_value_if_known_or_param(b, array); + b.emit_host_call(crate::jit::r#extern::handles::SYM_HANDLE_OF, 1, true); + b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true); + if let Some(d) = dst { + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); + b.store_local_i64(slot); + } return Ok(true); } // is_empty / charCodeAt: keep mapped hostcall path // Ensure receiver is a valid runtime handle (param or materialized via handle.of) - if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); b.emit_host_call(crate::jit::r#extern::handles::SYM_HANDLE_OF, 1, true); } - else if let Some(slot) = self.local_index.get(array).copied() { b.load_local_i64(slot); } - else { self.push_value_if_known_or_param(b, array); b.emit_host_call(crate::jit::r#extern::handles::SYM_HANDLE_OF, 1, true); } + if let Some(pidx) = self.param_index.get(array).copied() { + b.emit_param_i64(pidx); + b.emit_host_call(crate::jit::r#extern::handles::SYM_HANDLE_OF, 1, true); + } else if let Some(slot) = self.local_index.get(array).copied() { + b.load_local_i64(slot); + } else { + self.push_value_if_known_or_param(b, array); + b.emit_host_call(crate::jit::r#extern::handles::SYM_HANDLE_OF, 1, true); + } let mut argc = 1usize; - if method == "charCodeAt" { if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } argc = 2; } - if method == "is_empty" { b.hint_ret_bool(true); } - let decision = crate::jit::policy::invoke::decide_box_method("StringBox", method, argc, dst.is_some()); + if method == "charCodeAt" { + if let Some(v) = args.get(0) { + self.push_value_if_known_or_param(b, v); + } else { + b.emit_const_i64(0); + } + argc = 2; + } + if method == "is_empty" { + b.hint_ret_bool(true); + } + let decision = crate::jit::policy::invoke::decide_box_method( + "StringBox", + method, + argc, + dst.is_some(), + ); match decision { - crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { crate::jit::observe::lower_hostcall(&symbol, argc, &if argc==1 { ["Handle"][..].to_vec() } else { ["Handle","I64"][..].to_vec() }, "allow", "mapped_symbol"); b.emit_host_call(&symbol, argc, dst.is_some()); return Ok(true); } - crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some()); crate::jit::observe::lower_plugin_invoke(&box_type, method, type_id, method_id, argc); return Ok(true); } + crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { + crate::jit::observe::lower_hostcall( + &symbol, + argc, + &if argc == 1 { + ["Handle"][..].to_vec() + } else { + ["Handle", "I64"][..].to_vec() + }, + "allow", + "mapped_symbol", + ); + b.emit_host_call(&symbol, argc, dst.is_some()); + return Ok(true); + } + crate::jit::policy::invoke::InvokeDecision::PluginInvoke { + type_id, + method_id, + box_type, + .. + } => { + b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some()); + crate::jit::observe::lower_plugin_invoke( + &box_type, method, type_id, method_id, argc, + ); + return Ok(true); + } _ => {} } } @@ -274,24 +496,40 @@ impl LowerCore { "getField" | "setField" => { if std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1") { // receiver: allow param/local/phi/known - if let Some(v) = args.get(0) { let _ = v; } // keep args in scope + if let Some(v) = args.get(0) { + let _ = v; + } // keep args in scope self.push_value_if_known_or_param(b, array); // name: if const string, build a StringBox handle from literal; else best-effort push if let Some(name_id) = args.get(0) { - if let Some(s) = self.known_str.get(name_id).cloned() { b.emit_string_handle_from_literal(&s); } - else { b.emit_const_i64(0); } - } else { b.emit_const_i64(0); } + if let Some(s) = self.known_str.get(name_id).cloned() { + b.emit_string_handle_from_literal(&s); + } else { + b.emit_const_i64(0); + } + } else { + b.emit_const_i64(0); + } // value for setField let argc = if method == "setField" { if let Some(val_id) = args.get(1) { - if let Some(s) = self.known_str.get(val_id).cloned() { b.emit_string_handle_from_literal(&s); } - else { self.push_value_if_known_or_param(b, val_id); } - } else { b.emit_const_i64(0); } + if let Some(s) = self.known_str.get(val_id).cloned() { + b.emit_string_handle_from_literal(&s); + } else { + self.push_value_if_known_or_param(b, val_id); + } + } else { + b.emit_const_i64(0); + } 3 - } else { 2 }; + } else { + 2 + }; // Unified 3-arity call: getField uses val=-1 sentinel let sym = crate::jit::r#extern::host_bridge::SYM_HOST_INSTANCE_FIELD3; - if method == "getField" { b.emit_const_i64(-1); } + if method == "getField" { + b.emit_const_i64(-1); + } b.emit_host_call_fixed3(sym, dst.is_some()); return Ok(true); } @@ -305,67 +543,134 @@ impl LowerCore { for ins in bb.instructions.iter() { if let crate::mir::MirInstruction::Const { dst, value } = ins { if dst == array { - if let crate::mir::ConstValue::String(s) = value { lit_len = Some(s.len() as i64); } + if let crate::mir::ConstValue::String(s) = value { + lit_len = Some(s.len() as i64); + } break; } } } - if lit_len.is_some() { break; } + if lit_len.is_some() { + break; + } } if let Some(n) = lit_len { - if trace { eprintln!("[LOWER] StringBox.len: literal length={} (dst?={})", n, dst.is_some()); } + if trace { + eprintln!( + "[LOWER] StringBox.len: literal length={} (dst?={})", + n, + dst.is_some() + ); + } b.emit_const_i64(n); if let Some(d) = dst { // Persist literal length so Return can reliably load - let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } return Ok(true); } // (2) prefer host-bridge when enabled if std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1") { - if self.box_type_map.get(array).map(|s| s == "StringBox").unwrap_or(false) { - if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") { eprintln!("[LOWER]string.len via host-bridge"); } - if trace { eprintln!("[LOWER] StringBox.len via host-bridge (dst?={})", dst.is_some()); } + if self + .box_type_map + .get(array) + .map(|s| s == "StringBox") + .unwrap_or(false) + { + if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") { + eprintln!("[LOWER]string.len via host-bridge"); + } + if trace { + eprintln!( + "[LOWER] StringBox.len via host-bridge (dst?={})", + dst.is_some() + ); + } self.push_value_if_known_or_param(b, array); - b.emit_host_call(crate::jit::r#extern::host_bridge::SYM_HOST_STRING_LEN, 1, dst.is_some()); + b.emit_host_call( + crate::jit::r#extern::host_bridge::SYM_HOST_STRING_LEN, + 1, + dst.is_some(), + ); if let Some(d) = dst { - let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } return Ok(true); } } // (3) Fallback: emit string.len_h with Any.length_h guard - if self.box_type_map.get(array).map(|s| s == "StringBox").unwrap_or(false) { + if self + .box_type_map + .get(array) + .map(|s| s == "StringBox") + .unwrap_or(false) + { // Strong constant fold when literal mapping is known if let Some(s) = self.string_box_literal.get(array).cloned() { let n = s.len() as i64; b.emit_const_i64(n); - if let Some(d) = dst { let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); b.store_local_i64(slot); self.known_i64.insert(d, n); } + if let Some(d) = dst { + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); + b.store_local_i64(slot); + self.known_i64.insert(d, n); + } return Ok(true); } // Prefer literal reconstruction so JIT-AOT path is deterministic let mut lit: Option = None; for (_bid, bb) in func.blocks.iter() { for ins in bb.instructions.iter() { - if let crate::mir::MirInstruction::NewBox { dst, box_type, args } = ins { + if let crate::mir::MirInstruction::NewBox { + dst, + box_type, + args, + } = ins + { if dst == array && box_type == "StringBox" && args.len() == 1 { if let Some(src) = args.get(0) { - if let Some(s) = self.known_str.get(src).cloned() { lit = Some(s); break; } + if let Some(s) = self.known_str.get(src).cloned() { + lit = Some(s); + break; + } } } } } - if lit.is_some() { break; } + if lit.is_some() { + break; + } } if let Some(s) = lit { - if trace { eprintln!("[LOWER] StringBox.len reconstructed literal '{}' (dst?={})", s, dst.is_some()); } + if trace { + eprintln!( + "[LOWER] StringBox.len reconstructed literal '{}' (dst?={})", + s, + dst.is_some() + ); + } // Const fold: use literal length directly to avoid hostcall dependence let n = s.len() as i64; b.emit_const_i64(n); if let Some(d) = dst { - let dslot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); + let dslot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(dslot); self.known_i64.insert(d, n); } @@ -373,71 +678,145 @@ impl LowerCore { } // Param/local fallback when not a reconstructable literal if let Some(pidx) = self.param_index.get(array).copied() { - if trace { eprintln!("[LOWER] StringBox.len param p{} (dst?={})", pidx, dst.is_some()); } + if trace { + eprintln!( + "[LOWER] StringBox.len param p{} (dst?={})", + pidx, + dst.is_some() + ); + } self.emit_len_with_fallback_param(b, pidx); if let Some(d) = dst { - let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } return Ok(true); } if let Some(slot) = self.local_index.get(array).copied() { - if trace { eprintln!("[LOWER] StringBox.len local slot#{} (dst?={})", slot, dst.is_some()); } + if trace { + eprintln!( + "[LOWER] StringBox.len local slot#{} (dst?={})", + slot, + dst.is_some() + ); + } self.emit_len_with_fallback_local_handle(b, slot); if let Some(d) = dst { - let dslot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); + let dslot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(dslot); } return Ok(true); } // As a last resort, convert receiver to handle via nyash.handle.of and apply fallback on temp slot - if trace { eprintln!("[LOWER] StringBox.len last-resort handle.of + fallback (dst?={})", dst.is_some()); } + if trace { + eprintln!( + "[LOWER] StringBox.len last-resort handle.of + fallback (dst?={})", + dst.is_some() + ); + } self.push_value_if_known_or_param(b, array); b.emit_host_call(crate::jit::r#extern::handles::SYM_HANDLE_OF, 1, true); - let t_recv = { let id = self.next_local; self.next_local += 1; id }; + let t_recv = { + let id = self.next_local; + self.next_local += 1; + id + }; b.store_local_i64(t_recv); self.emit_len_with_fallback_local_handle(b, t_recv); if let Some(d) = dst { - let dslot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); + let dslot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(dslot); } return Ok(true); } // Not a StringBox: let other branches handle - if trace { eprintln!("[LOWER] StringBox.len not handled (box_type={:?})", self.box_type_map.get(array)); } + if trace { + eprintln!( + "[LOWER] StringBox.len not handled (box_type={:?})", + self.box_type_map.get(array) + ); + } return Ok(false); } // Alias: String.length → same as len "length" => { let trace = std::env::var("NYASH_JIT_TRACE_LOWER_LEN").ok().as_deref() == Some("1"); - if self.box_type_map.get(array).map(|s| s == "StringBox").unwrap_or(false) { + if self + .box_type_map + .get(array) + .map(|s| s == "StringBox") + .unwrap_or(false) + { // Try literal constant fold first for stability let mut lit: Option = None; for (_bid, bb) in func.blocks.iter() { for ins in bb.instructions.iter() { - if let crate::mir::MirInstruction::NewBox { dst, box_type, args } = ins { + if let crate::mir::MirInstruction::NewBox { + dst, + box_type, + args, + } = ins + { if dst == array && box_type == "StringBox" && args.len() == 1 { if let Some(src) = args.get(0) { - if let Some(s) = self.known_str.get(src).cloned() { lit = Some(s); break; } + if let Some(s) = self.known_str.get(src).cloned() { + lit = Some(s); + break; + } // Fallback: scan Const directly for (_b2, bb2) in func.blocks.iter() { for ins2 in bb2.instructions.iter() { - if let crate::mir::MirInstruction::Const { dst: cdst, value } = ins2 { - if cdst == src { if let crate::mir::ConstValue::String(sv) = value { lit = Some(sv.clone()); break; } } + if let crate::mir::MirInstruction::Const { + dst: cdst, + value, + } = ins2 + { + if cdst == src { + if let crate::mir::ConstValue::String(sv) = + value + { + lit = Some(sv.clone()); + break; + } + } } } - if lit.is_some() { break; } + if lit.is_some() { + break; + } } } } } } - if lit.is_some() { break; } + if lit.is_some() { + break; + } } if let Some(s) = lit { let n = s.len() as i64; b.emit_const_i64(n); - if let Some(d) = dst { let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); b.store_local_i64(slot); self.known_i64.insert(d, n); } + if let Some(d) = dst { + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); + b.store_local_i64(slot); + self.known_i64.insert(d, n); + } return Ok(true); } // Reuse len handler, but ensure dst persistence if len handler did not handle @@ -447,12 +826,25 @@ impl LowerCore { return Ok(true); } // As a conservative fallback, try direct any.length_h on handle.of - if trace { eprintln!("[LOWER] StringBox.length fallback any.length_h on handle.of (dst?={})", dst.is_some()); } + if trace { + eprintln!( + "[LOWER] StringBox.length fallback any.length_h on handle.of (dst?={})", + dst.is_some() + ); + } self.push_value_if_known_or_param(b, array); b.emit_host_call(crate::jit::r#extern::handles::SYM_HANDLE_OF, 1, true); - b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, dst.is_some()); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_ANY_LEN_H, + 1, + dst.is_some(), + ); if let Some(d) = dst { - let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } return Ok(true); @@ -466,25 +858,31 @@ impl LowerCore { Some("StringBox") => { // Strong constant fold when literal mapping is known (allow disabling via NYASH_JIT_DISABLE_LEN_CONST=1) if std::env::var("NYASH_JIT_DISABLE_LEN_CONST").ok().as_deref() != Some("1") - && self.string_box_literal.get(array).is_some() { + && self.string_box_literal.get(array).is_some() + { let s = self.string_box_literal.get(array).cloned().unwrap(); let n = s.len() as i64; b.emit_const_i64(n); - if let Some(d) = dst { let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); b.store_local_i64(slot); self.known_i64.insert(d, n); } + if let Some(d) = dst { + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); + b.store_local_i64(slot); + self.known_i64.insert(d, n); + } return Ok(true); } if let Some(pidx) = self.param_index.get(array).copied() { self.emit_len_with_fallback_param(b, pidx); // Persist into dst local so Return can reliably pick it up if let Some(d) = dst { - let slot = *self - .local_index - .entry(d) - .or_insert_with(|| { - let id = self.next_local; - self.next_local += 1; - id - }); + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } return Ok(true); @@ -493,14 +891,11 @@ impl LowerCore { self.emit_len_with_fallback_local_handle(b, slot); // Persist into dst local so Return can reliably pick it up if let Some(d) = dst { - let slot = *self - .local_index - .entry(d) - .or_insert_with(|| { - let id = self.next_local; - self.next_local += 1; - id - }); + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } return Ok(true); @@ -509,77 +904,143 @@ impl LowerCore { let mut lit: Option = None; for (_bid, bb) in func.blocks.iter() { for ins in bb.instructions.iter() { - if let crate::mir::MirInstruction::NewBox { dst, box_type, args } = ins { + if let crate::mir::MirInstruction::NewBox { + dst, + box_type, + args, + } = ins + { if dst == array && box_type == "StringBox" && args.len() == 1 { if let Some(src) = args.get(0) { - if let Some(s) = self.known_str.get(src).cloned() { lit = Some(s); break; } + if let Some(s) = self.known_str.get(src).cloned() { + lit = Some(s); + break; + } // Fallback: scan Const directly for (_b2, bb2) in func.blocks.iter() { for ins2 in bb2.instructions.iter() { - if let crate::mir::MirInstruction::Const { dst: cdst, value } = ins2 { - if cdst == src { if let crate::mir::ConstValue::String(sv) = value { lit = Some(sv.clone()); break; } } + if let crate::mir::MirInstruction::Const { + dst: cdst, + value, + } = ins2 + { + if cdst == src { + if let crate::mir::ConstValue::String( + sv, + ) = value + { + lit = Some(sv.clone()); + break; + } + } } } - if lit.is_some() { break; } + if lit.is_some() { + break; + } } } } } } - if lit.is_some() { break; } + if lit.is_some() { + break; + } } if let Some(s) = lit { let n = s.len() as i64; b.emit_const_i64(n); - if let Some(d) = dst { let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); b.store_local_i64(slot); self.known_i64.insert(d, n); } + if let Some(d) = dst { + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); + b.store_local_i64(slot); + self.known_i64.insert(d, n); + } return Ok(true); } // Last resort: handle.of self.push_value_if_known_or_param(b, array); b.emit_host_call(crate::jit::r#extern::handles::SYM_HANDLE_OF, 1, true); - let slot = { let id = self.next_local; self.next_local += 1; id }; + let slot = { + let id = self.next_local; + self.next_local += 1; + id + }; b.store_local_i64(slot); self.emit_len_with_fallback_local_handle(b, slot); // Persist into dst local so Return can reliably pick it up if let Some(d) = dst { - let dslot = *self - .local_index - .entry(d) - .or_insert_with(|| { - let id = self.next_local; - self.next_local += 1; - id - }); + let dslot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(dslot); } return Ok(true); } - Some("ArrayBox") => {}, + Some("ArrayBox") => {} _ => { // Unknown receiver type: generic Any.length_h on a handle self.push_value_if_known_or_param(b, array); b.emit_host_call(crate::jit::r#extern::handles::SYM_HANDLE_OF, 1, true); b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true); - if let Some(d) = dst { let slot = *self.local_index.entry(d).or_insert_with(|| { let id=self.next_local; self.next_local+=1; id }); b.store_local_i64(slot); } + if let Some(d) = dst { + let slot = *self.local_index.entry(d).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); + b.store_local_i64(slot); + } return Ok(true); } } - if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() { + if let Ok(ph) = + crate::runtime::plugin_loader_unified::get_global_plugin_host().read() + { if let Ok(h) = ph.resolve_method("ArrayBox", "length") { - if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } + if let Some(pidx) = self.param_index.get(array).copied() { + b.emit_param_i64(pidx); + } else { + b.emit_const_i64(-1); + } b.emit_plugin_invoke(h.type_id, h.method_id, 1, dst.is_some()); return Ok(true); } } // Hostcall fallback if let Some(pidx) = self.param_index.get(array).copied() { - crate::jit::observe::lower_hostcall(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, &["Handle"], "allow", "mapped_symbol"); + crate::jit::observe::lower_hostcall( + crate::jit::r#extern::collections::SYM_ANY_LEN_H, + 1, + &["Handle"], + "allow", + "mapped_symbol", + ); b.emit_param_i64(pidx); - b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, dst.is_some()); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_ANY_LEN_H, + 1, + dst.is_some(), + ); } else { - crate::jit::observe::lower_hostcall(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, &["I64"], "fallback", "receiver_not_param"); + crate::jit::observe::lower_hostcall( + crate::jit::r#extern::collections::SYM_ARRAY_LEN, + 1, + &["I64"], + "fallback", + "receiver_not_param", + ); b.emit_const_i64(-1); - b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some()); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_ARRAY_LEN, + 1, + dst.is_some(), + ); } return Ok(true); } @@ -587,26 +1048,63 @@ impl LowerCore { "push" => { let argc = 2usize; // receiver - if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } + if let Some(pidx) = self.param_index.get(array).copied() { + b.emit_param_i64(pidx); + } else { + b.emit_const_i64(-1); + } // value - if let Some(v) = args.get(0).and_then(|vid| self.known_i64.get(vid)).copied() { b.emit_const_i64(v); } - else if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } + if let Some(v) = args.get(0).and_then(|vid| self.known_i64.get(vid)).copied() { + b.emit_const_i64(v); + } else if let Some(v) = args.get(0) { + self.push_value_if_known_or_param(b, v); + } else { + b.emit_const_i64(0); + } // policy decide → plugin / hostcall fallback - let decision = crate::jit::policy::invoke::decide_box_method("ArrayBox", "push", argc, false); + let decision = + crate::jit::policy::invoke::decide_box_method("ArrayBox", "push", argc, false); match decision { - crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { + crate::jit::policy::invoke::InvokeDecision::PluginInvoke { + type_id, + method_id, + box_type, + .. + } => { b.emit_plugin_invoke(type_id, method_id, argc, false); - crate::jit::observe::lower_plugin_invoke(&box_type, "push", type_id, method_id, argc); + crate::jit::observe::lower_plugin_invoke( + &box_type, "push", type_id, method_id, argc, + ); } crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { - crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64"], "allow", "mapped_symbol"); + crate::jit::observe::lower_hostcall( + &symbol, + argc, + &["Handle", "I64"], + "allow", + "mapped_symbol", + ); b.emit_host_call(&symbol, argc, false); } _ => { // Fallback hostcall - let sym = if self.param_index.get(array).is_some() { crate::jit::r#extern::collections::SYM_ARRAY_PUSH_H } else { crate::jit::r#extern::collections::SYM_ARRAY_PUSH }; - let arg_types = if self.param_index.get(array).is_some() { &["Handle","I64"][..] } else { &["I64","I64"][..] }; - crate::jit::observe::lower_hostcall(sym, argc, arg_types, "fallback", "policy_or_unknown"); + let sym = if self.param_index.get(array).is_some() { + crate::jit::r#extern::collections::SYM_ARRAY_PUSH_H + } else { + crate::jit::r#extern::collections::SYM_ARRAY_PUSH + }; + let arg_types = if self.param_index.get(array).is_some() { + &["Handle", "I64"][..] + } else { + &["I64", "I64"][..] + }; + crate::jit::observe::lower_hostcall( + sym, + argc, + arg_types, + "fallback", + "policy_or_unknown", + ); b.emit_host_call(sym, argc, false); } } @@ -615,36 +1113,104 @@ impl LowerCore { // Map ops "size" | "get" | "has" | "set" => { let is_set = method == "set"; - if is_set && crate::jit::policy::current().read_only { // deny under read-only policy - if let Some(_) = dst { b.emit_const_i64(0); } + if is_set && crate::jit::policy::current().read_only { + // deny under read-only policy + if let Some(_) = dst { + b.emit_const_i64(0); + } return Ok(true); } - let argc = match method { "size" => 1, "get" | "has" => 2, "set" => 3, _ => 1 }; + let argc = match method { + "size" => 1, + "get" | "has" => 2, + "set" => 3, + _ => 1, + }; // If receiver is a local handle (AOT/JIT-AOT), prefer handle-based hostcalls directly if self.handle_values.contains(array) { self.push_value_if_known_or_param(b, array); match method { - "size" => b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_SIZE_H, argc, dst.is_some()), - "get" => { if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_GET_H, argc, dst.is_some()) } - "has" => { if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_HAS_H, argc, dst.is_some()) } - "set" => { if let Some(k) = args.get(0) { self.push_value_if_known_or_param(b, k); } else { b.emit_const_i64(0); } if let Some(v) = args.get(1) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_SET_H, argc, dst.is_some()) } + "size" => b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_SIZE_H, + argc, + dst.is_some(), + ), + "get" => { + if let Some(v) = args.get(0) { + self.push_value_if_known_or_param(b, v); + } else { + b.emit_const_i64(0); + } + b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_GET_H, + argc, + dst.is_some(), + ) + } + "has" => { + if let Some(v) = args.get(0) { + self.push_value_if_known_or_param(b, v); + } else { + b.emit_const_i64(0); + } + b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_HAS_H, + argc, + dst.is_some(), + ) + } + "set" => { + if let Some(k) = args.get(0) { + self.push_value_if_known_or_param(b, k); + } else { + b.emit_const_i64(0); + } + if let Some(v) = args.get(1) { + self.push_value_if_known_or_param(b, v); + } else { + b.emit_const_i64(0); + } + b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_SET_H, + argc, + dst.is_some(), + ) + } _ => {} } return Ok(true); } - if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() { + if let Ok(ph) = + crate::runtime::plugin_loader_unified::get_global_plugin_host().read() + { if let Ok(h) = ph.resolve_method("MapBox", method) { // receiver - if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } + if let Some(pidx) = self.param_index.get(array).copied() { + b.emit_param_i64(pidx); + } else { + b.emit_const_i64(-1); + } // args match method { "size" => {} "get" | "has" => { - if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } + if let Some(v) = args.get(0) { + self.push_value_if_known_or_param(b, v); + } else { + b.emit_const_i64(0); + } } "set" => { - if let Some(k) = args.get(0) { self.push_value_if_known_or_param(b, k); } else { b.emit_const_i64(0); } - if let Some(v) = args.get(1) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } + if let Some(k) = args.get(0) { + self.push_value_if_known_or_param(b, k); + } else { + b.emit_const_i64(0); + } + if let Some(v) = args.get(1) { + self.push_value_if_known_or_param(b, v); + } else { + b.emit_const_i64(0); + } } _ => {} } @@ -655,7 +1221,8 @@ impl LowerCore { "decision":"allow","reason":"plugin_invoke","argc": argc, "type_id": h.type_id, "method_id": h.method_id }), - "plugin","" + "plugin", + "", ); return Ok(true); } @@ -664,19 +1231,51 @@ impl LowerCore { if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); match method { - "size" => b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_SIZE_H, argc, dst.is_some()), + "size" => b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_SIZE_H, + argc, + dst.is_some(), + ), "get" => { - if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } - b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_GET_H, argc, dst.is_some()) + if let Some(v) = args.get(0) { + self.push_value_if_known_or_param(b, v); + } else { + b.emit_const_i64(0); + } + b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_GET_H, + argc, + dst.is_some(), + ) } "has" => { - if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } - b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_HAS_H, argc, dst.is_some()) + if let Some(v) = args.get(0) { + self.push_value_if_known_or_param(b, v); + } else { + b.emit_const_i64(0); + } + b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_HAS_H, + argc, + dst.is_some(), + ) } "set" => { - if let Some(k) = args.get(0) { self.push_value_if_known_or_param(b, k); } else { b.emit_const_i64(0); } - if let Some(v) = args.get(1) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } - b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_SET_H, argc, dst.is_some()) + if let Some(k) = args.get(0) { + self.push_value_if_known_or_param(b, k); + } else { + b.emit_const_i64(0); + } + if let Some(v) = args.get(1) { + self.push_value_if_known_or_param(b, v); + } else { + b.emit_const_i64(0); + } + b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_SET_H, + argc, + dst.is_some(), + ) } _ => {} } @@ -684,19 +1283,51 @@ impl LowerCore { // receiver unknown: try local handle (AOT/JIT-AOT) self.push_value_if_known_or_param(b, array); match method { - "size" => b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_SIZE_H, argc, dst.is_some()), + "size" => b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_SIZE_H, + argc, + dst.is_some(), + ), "get" => { - if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } - b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_GET_H, argc, dst.is_some()) + if let Some(v) = args.get(0) { + self.push_value_if_known_or_param(b, v); + } else { + b.emit_const_i64(0); + } + b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_GET_H, + argc, + dst.is_some(), + ) } "has" => { - if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } - b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_HAS_H, argc, dst.is_some()) + if let Some(v) = args.get(0) { + self.push_value_if_known_or_param(b, v); + } else { + b.emit_const_i64(0); + } + b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_HAS_H, + argc, + dst.is_some(), + ) } "set" => { - if let Some(k) = args.get(0) { self.push_value_if_known_or_param(b, k); } else { b.emit_const_i64(0); } - if let Some(v) = args.get(1) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } - b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_SET_H, argc, dst.is_some()) + if let Some(k) = args.get(0) { + self.push_value_if_known_or_param(b, k); + } else { + b.emit_const_i64(0); + } + if let Some(v) = args.get(1) { + self.push_value_if_known_or_param(b, v); + } else { + b.emit_const_i64(0); + } + b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_SET_H, + argc, + dst.is_some(), + ) } _ => {} } @@ -713,19 +1344,37 @@ impl LowerCore { let is_handle = self.handle_values.contains(array); let mut arg_kinds: Vec<&'static str> = Vec::new(); for a in args.iter().take(3) { - let k = if self.known_i64.contains_key(a) { "i" } - else if self.known_str.contains_key(a) { "s" } - else if self.param_index.contains_key(a) { "p" } - else if self.local_index.contains_key(a) { "l" } - else if self.handle_values.contains(a) { "h" } - else { "-" }; + let k = if self.known_i64.contains_key(a) { + "i" + } else if self.known_str.contains_key(a) { + "s" + } else if self.param_index.contains_key(a) { + "p" + } else if self.local_index.contains_key(a) { + "l" + } else if self.handle_values.contains(a) { + "h" + } else { + "-" + }; arg_kinds.push(k); } - let policy = crate::jit::policy::invoke::decide_box_method(&bt, method, 1 + args.len(), dst.is_some()); + let policy = crate::jit::policy::invoke::decide_box_method( + &bt, + method, + 1 + args.len(), + dst.is_some(), + ); let policy_str = match policy { - crate::jit::policy::invoke::InvokeDecision::HostCall { ref symbol, .. } => format!("hostcall:{}", symbol), - crate::jit::policy::invoke::InvokeDecision::PluginInvoke { .. } => "plugin_invoke".to_string(), - crate::jit::policy::invoke::InvokeDecision::Fallback { ref reason } => format!("fallback:{}", reason), + crate::jit::policy::invoke::InvokeDecision::HostCall { ref symbol, .. } => { + format!("hostcall:{}", symbol) + } + crate::jit::policy::invoke::InvokeDecision::PluginInvoke { .. } => { + "plugin_invoke".to_string() + } + crate::jit::policy::invoke::InvokeDecision::Fallback { ref reason } => { + format!("fallback:{}", reason) + } }; eprintln!( "[LOWER] unhandled BoxCall: box_type='{}' method='{}' recv[param?{} local?{} handle?{}] args={:?} policy={}", diff --git a/src/jit/lower/core/string_len.rs b/src/jit/lower/core/string_len.rs index 83197729..2efe3928 100644 --- a/src/jit/lower/core/string_len.rs +++ b/src/jit/lower/core/string_len.rs @@ -9,10 +9,14 @@ impl LowerCore { pub(super) fn emit_len_with_fallback_param(&mut self, b: &mut dyn IRBuilder, pidx: usize) { use super::super::builder::CmpKind; // Temp locals - let hslot = self.next_local; self.next_local += 1; // receiver handle slot - let t_string = self.next_local; self.next_local += 1; - let t_any = self.next_local; self.next_local += 1; - let t_cond = self.next_local; self.next_local += 1; + let hslot = self.next_local; + self.next_local += 1; // receiver handle slot + let t_string = self.next_local; + self.next_local += 1; + let t_any = self.next_local; + self.next_local += 1; + let t_cond = self.next_local; + self.next_local += 1; // Materialize receiver handle from param index b.emit_param_i64(pidx); b.emit_host_call(crate::jit::r#extern::handles::SYM_HANDLE_OF, 1, true); @@ -23,7 +27,7 @@ impl LowerCore { 1, &["Handle"], "allow", - "core_len_param" + "core_len_param", ); b.load_local_i64(hslot); b.emit_host_call(crate::jit::r#extern::collections::SYM_STRING_LEN_H, 1, true); @@ -36,7 +40,7 @@ impl LowerCore { 1, &["Handle"], "allow", - "core_len_param" + "core_len_param", ); b.load_local_i64(hslot); b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true); @@ -51,24 +55,31 @@ impl LowerCore { // debug: observe condition b.emit_debug_i64_local(1102, t_cond); // select(cond ? any_len : string_len) - b.load_local_i64(t_cond); // cond (bottom) - b.load_local_i64(t_any); // then + b.load_local_i64(t_cond); // cond (bottom) + b.load_local_i64(t_any); // then b.load_local_i64(t_string); // else b.emit_select_i64(); } - pub(super) fn emit_len_with_fallback_local_handle(&mut self, b: &mut dyn IRBuilder, slot: usize) { + pub(super) fn emit_len_with_fallback_local_handle( + &mut self, + b: &mut dyn IRBuilder, + slot: usize, + ) { use super::super::builder::CmpKind; - let t_string = self.next_local; self.next_local += 1; - let t_any = self.next_local; self.next_local += 1; - let t_cond = self.next_local; self.next_local += 1; + let t_string = self.next_local; + self.next_local += 1; + let t_any = self.next_local; + self.next_local += 1; + let t_cond = self.next_local; + self.next_local += 1; // String.len_h crate::jit::observe::lower_hostcall( crate::jit::r#extern::collections::SYM_STRING_LEN_H, 1, &["Handle"], "allow", - "core_len_local" + "core_len_local", ); b.load_local_i64(slot); b.emit_host_call(crate::jit::r#extern::collections::SYM_STRING_LEN_H, 1, true); @@ -80,7 +91,7 @@ impl LowerCore { 1, &["Handle"], "allow", - "core_len_local" + "core_len_local", ); b.load_local_i64(slot); b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true); @@ -101,16 +112,19 @@ impl LowerCore { pub(super) fn emit_len_with_fallback_literal(&mut self, b: &mut dyn IRBuilder, s: &str) { use super::super::builder::CmpKind; - let t_string = self.next_local; self.next_local += 1; - let t_any = self.next_local; self.next_local += 1; - let t_cond = self.next_local; self.next_local += 1; + let t_string = self.next_local; + self.next_local += 1; + let t_any = self.next_local; + self.next_local += 1; + let t_cond = self.next_local; + self.next_local += 1; // String.len_h on literal handle crate::jit::observe::lower_hostcall( crate::jit::r#extern::collections::SYM_STRING_LEN_H, 1, &["Handle"], "allow", - "core_len_lit" + "core_len_lit", ); b.emit_string_handle_from_literal(s); b.emit_host_call(crate::jit::r#extern::collections::SYM_STRING_LEN_H, 1, true); @@ -122,7 +136,7 @@ impl LowerCore { 1, &["Handle"], "allow", - "core_len_lit" + "core_len_lit", ); b.emit_string_handle_from_literal(s); b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true); diff --git a/src/jit/lower/core_hostcall.rs b/src/jit/lower/core_hostcall.rs index a49d8efb..1ee498c2 100644 --- a/src/jit/lower/core_hostcall.rs +++ b/src/jit/lower/core_hostcall.rs @@ -1,6 +1,6 @@ //! HostCall-related lowering helpers split from core.rs (no behavior change) -use crate::mir::{MirFunction, ValueId}; use super::builder::IRBuilder; +use crate::mir::{MirFunction, ValueId}; use std::collections::HashMap; pub fn lower_array_get( @@ -16,13 +16,21 @@ pub fn lower_array_get( if let Some(pidx) = param_index.get(array).copied() { b.emit_param_i64(pidx); b.emit_const_i64(idx); - let sym = if use_bridge { crate::jit::r#extern::host_bridge::SYM_HOST_ARRAY_GET } else { crate::jit::r#extern::collections::SYM_ARRAY_GET_H }; + let sym = if use_bridge { + crate::jit::r#extern::host_bridge::SYM_HOST_ARRAY_GET + } else { + crate::jit::r#extern::collections::SYM_ARRAY_GET_H + }; b.emit_host_call(sym, 2, true); } else { let arr_idx = -1; b.emit_const_i64(arr_idx); b.emit_const_i64(idx); - let sym = if use_bridge { crate::jit::r#extern::host_bridge::SYM_HOST_ARRAY_GET } else { crate::jit::r#extern::collections::SYM_ARRAY_GET }; + let sym = if use_bridge { + crate::jit::r#extern::host_bridge::SYM_HOST_ARRAY_GET + } else { + crate::jit::r#extern::collections::SYM_ARRAY_GET + }; b.emit_host_call(sym, 2, true); } } @@ -37,7 +45,11 @@ pub fn lower_map_size_simple( let use_bridge = std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1"); if let Some(pidx) = param_index.get(recv).copied() { b.emit_param_i64(pidx); - let sym = if use_bridge { crate::jit::r#extern::host_bridge::SYM_HOST_MAP_SIZE } else { crate::jit::r#extern::collections::SYM_MAP_SIZE_H }; + let sym = if use_bridge { + crate::jit::r#extern::host_bridge::SYM_HOST_MAP_SIZE + } else { + crate::jit::r#extern::collections::SYM_MAP_SIZE_H + }; b.emit_host_call(sym, 1, dst_is_some); } } @@ -53,8 +65,18 @@ pub fn lower_map_get_simple( let use_bridge = std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1"); if let Some(pidx) = param_index.get(recv).copied() { b.emit_param_i64(pidx); - if let Some(i) = known_i64.get(key).copied() { b.emit_const_i64(i); } else if let Some(kp) = param_index.get(key).copied() { b.emit_param_i64(kp); } else { b.emit_const_i64(0); } - let sym = if use_bridge { crate::jit::r#extern::host_bridge::SYM_HOST_MAP_GET } else { crate::jit::r#extern::collections::SYM_MAP_GET_H }; + if let Some(i) = known_i64.get(key).copied() { + b.emit_const_i64(i); + } else if let Some(kp) = param_index.get(key).copied() { + b.emit_param_i64(kp); + } else { + b.emit_const_i64(0); + } + let sym = if use_bridge { + crate::jit::r#extern::host_bridge::SYM_HOST_MAP_GET + } else { + crate::jit::r#extern::collections::SYM_MAP_GET_H + }; b.emit_host_call(sym, 2, dst_is_some); } } @@ -70,8 +92,18 @@ pub fn lower_map_has_simple( let use_bridge = std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1"); if let Some(pidx) = param_index.get(recv).copied() { b.emit_param_i64(pidx); - if let Some(i) = known_i64.get(key).copied() { b.emit_const_i64(i); } else if let Some(kp) = param_index.get(key).copied() { b.emit_param_i64(kp); } else { b.emit_const_i64(0); } - let sym = if use_bridge { crate::jit::r#extern::host_bridge::SYM_HOST_MAP_HAS } else { crate::jit::r#extern::collections::SYM_MAP_HAS_H }; + if let Some(i) = known_i64.get(key).copied() { + b.emit_const_i64(i); + } else if let Some(kp) = param_index.get(key).copied() { + b.emit_param_i64(kp); + } else { + b.emit_const_i64(0); + } + let sym = if use_bridge { + crate::jit::r#extern::host_bridge::SYM_HOST_MAP_HAS + } else { + crate::jit::r#extern::collections::SYM_MAP_HAS_H + }; b.emit_host_call(sym, 2, dst_is_some); } } @@ -87,9 +119,25 @@ pub fn lower_map_set_simple( let use_bridge = std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1"); if let Some(pidx) = param_index.get(recv).copied() { b.emit_param_i64(pidx); - if let Some(i) = known_i64.get(key).copied() { b.emit_const_i64(i); } else if let Some(kp) = param_index.get(key).copied() { b.emit_param_i64(kp); } else { b.emit_const_i64(0); } - if let Some(i) = known_i64.get(value).copied() { b.emit_const_i64(i); } else if let Some(vp) = param_index.get(value).copied() { b.emit_param_i64(vp); } else { b.emit_const_i64(0); } - let sym = if use_bridge { crate::jit::r#extern::host_bridge::SYM_HOST_MAP_SET } else { crate::jit::r#extern::collections::SYM_MAP_SET_H }; + if let Some(i) = known_i64.get(key).copied() { + b.emit_const_i64(i); + } else if let Some(kp) = param_index.get(key).copied() { + b.emit_param_i64(kp); + } else { + b.emit_const_i64(0); + } + if let Some(i) = known_i64.get(value).copied() { + b.emit_const_i64(i); + } else if let Some(vp) = param_index.get(value).copied() { + b.emit_param_i64(vp); + } else { + b.emit_const_i64(0); + } + let sym = if use_bridge { + crate::jit::r#extern::host_bridge::SYM_HOST_MAP_SET + } else { + crate::jit::r#extern::collections::SYM_MAP_SET_H + }; b.emit_host_call(sym, 3, false); } } @@ -109,14 +157,22 @@ pub fn lower_array_set( b.emit_param_i64(pidx); b.emit_const_i64(idx); b.emit_const_i64(val); - let sym = if use_bridge { crate::jit::r#extern::host_bridge::SYM_HOST_ARRAY_SET } else { crate::jit::r#extern::collections::SYM_ARRAY_SET_H }; + let sym = if use_bridge { + crate::jit::r#extern::host_bridge::SYM_HOST_ARRAY_SET + } else { + crate::jit::r#extern::collections::SYM_ARRAY_SET_H + }; b.emit_host_call(sym, 3, false); } else { let arr_idx = -1; b.emit_const_i64(arr_idx); b.emit_const_i64(idx); b.emit_const_i64(val); - let sym = if use_bridge { crate::jit::r#extern::host_bridge::SYM_HOST_ARRAY_SET } else { crate::jit::r#extern::collections::SYM_ARRAY_SET }; + let sym = if use_bridge { + crate::jit::r#extern::host_bridge::SYM_HOST_ARRAY_SET + } else { + crate::jit::r#extern::collections::SYM_ARRAY_SET + }; b.emit_host_call(sym, 3, false); } } @@ -134,196 +190,308 @@ pub fn lower_box_call( args: &Vec, dst: Option, ) { - if !crate::jit::config::current().hostcall { return; } - match method { - "len" | "length" => { - if let Some(pidx) = param_index.get(recv).copied() { - crate::jit::events::emit_lower( - serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ARRAY_LEN, "decision":"allow", "reason":"sig_ok", "argc":1, "arg_types":["I64(index)"]}), - "hostcall","" - ); - // Pass parameter index directly (JIT thunks read legacy VM args by index) - b.emit_param_i64(pidx as i64 as usize); - b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some()); - } else { - crate::jit::events::emit_lower( - serde_json::json!({ - "id": crate::jit::r#extern::collections::SYM_ARRAY_LEN, - "decision": "fallback", "reason": "receiver_not_param", - "argc": 1, "arg_types": ["I64(index)"] - }), - "hostcall","" - ); - b.emit_const_i64(-1); - b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some()); - } - } - "isEmpty" | "is_empty" => { + if !crate::jit::config::current().hostcall { + return; + } + match method { + "len" | "length" => { + if let Some(pidx) = param_index.get(recv).copied() { crate::jit::events::emit_lower( - serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, "decision":"allow", "reason":"sig_ok", "argc":1, "arg_types":["Handle"]}), - "hostcall","" + serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ARRAY_LEN, "decision":"allow", "reason":"sig_ok", "argc":1, "arg_types":["I64(index)"]}), + "hostcall", + "", + ); + // Pass parameter index directly (JIT thunks read legacy VM args by index) + b.emit_param_i64(pidx as i64 as usize); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_ARRAY_LEN, + 1, + dst.is_some(), + ); + } else { + crate::jit::events::emit_lower( + serde_json::json!({ + "id": crate::jit::r#extern::collections::SYM_ARRAY_LEN, + "decision": "fallback", "reason": "receiver_not_param", + "argc": 1, "arg_types": ["I64(index)"] + }), + "hostcall", + "", + ); + b.emit_const_i64(-1); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_ARRAY_LEN, + 1, + dst.is_some(), ); - if let Some(pidx) = param_index.get(recv).copied() { - b.emit_param_i64(pidx); - b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, 1, dst.is_some()); - } } - // math.* family (read-only) - m if m.starts_with("math.") => { - let sym = format!("nyash.{}", m); - use crate::jit::hostcall_registry::{check_signature, ArgKind}; - let mut observed: Vec = Vec::new(); - for (i, v) in args.iter().enumerate() { - let kind = if let Some(mt) = func.signature.params.get(i) { - match mt { - crate::mir::MirType::Float => ArgKind::F64, - crate::mir::MirType::Integer => ArgKind::I64, - crate::mir::MirType::Bool => ArgKind::I64, - crate::mir::MirType::String | crate::mir::MirType::Box(_) => ArgKind::Handle, - _ => { - if known_f64.contains_key(v) || float_box_values.contains(v) { ArgKind::F64 } else { ArgKind::I64 } + } + "isEmpty" | "is_empty" => { + crate::jit::events::emit_lower( + serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, "decision":"allow", "reason":"sig_ok", "argc":1, "arg_types":["Handle"]}), + "hostcall", + "", + ); + if let Some(pidx) = param_index.get(recv).copied() { + b.emit_param_i64(pidx); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, + 1, + dst.is_some(), + ); + } + } + // math.* family (read-only) + m if m.starts_with("math.") => { + let sym = format!("nyash.{}", m); + use crate::jit::hostcall_registry::{check_signature, ArgKind}; + let mut observed: Vec = Vec::new(); + for (i, v) in args.iter().enumerate() { + let kind = if let Some(mt) = func.signature.params.get(i) { + match mt { + crate::mir::MirType::Float => ArgKind::F64, + crate::mir::MirType::Integer => ArgKind::I64, + crate::mir::MirType::Bool => ArgKind::I64, + crate::mir::MirType::String | crate::mir::MirType::Box(_) => { + ArgKind::Handle + } + _ => { + if known_f64.contains_key(v) || float_box_values.contains(v) { + ArgKind::F64 + } else { + ArgKind::I64 } } + } + } else { + if known_f64.contains_key(v) || float_box_values.contains(v) { + ArgKind::F64 } else { - if known_f64.contains_key(v) || float_box_values.contains(v) { ArgKind::F64 } else { ArgKind::I64 } - }; - observed.push(kind); - } - let arg_types: Vec<&'static str> = observed.iter().map(|k| match k { ArgKind::I64 => "I64", ArgKind::F64 => "F64", ArgKind::Handle => "Handle" }).collect(); - match check_signature(&sym, &observed) { - Ok(()) => { - crate::jit::events::emit_lower( - serde_json::json!({"id": sym, "decision":"allow", "reason":"sig_ok", "argc": observed.len(), "arg_types": arg_types}), - "hostcall","" - ); - if crate::jit::config::current().native_f64 { - let (symbol, arity) = match method { - "math.sin" => ("nyash.math.sin_f64", 1), - "math.cos" => ("nyash.math.cos_f64", 1), - "math.abs" => ("nyash.math.abs_f64", 1), - "math.min" => ("nyash.math.min_f64", 2), - "math.max" => ("nyash.math.max_f64", 2), - _ => ("nyash.math.sin_f64", 1), - }; - for i in 0..arity { - if let Some(v) = args.get(i) { - if let Some(fv) = known_f64.get(v).copied() { b.emit_const_f64(fv); continue; } - if let Some(iv) = known_i64.get(v).copied() { b.emit_const_f64(iv as f64); continue; } - let mut emitted = false; - 'scan: for (_bb_id, bb) in func.blocks.iter() { - for ins in bb.instructions.iter() { - if let crate::mir::MirInstruction::NewBox { dst, box_type, args: nb_args } = ins { - if *dst == *v && box_type == "FloatBox" { - if let Some(srcv) = nb_args.get(0) { - if let Some(fv) = known_f64.get(srcv).copied() { b.emit_const_f64(fv); emitted = true; break 'scan; } - if let Some(iv) = known_i64.get(srcv).copied() { b.emit_const_f64(iv as f64); emitted = true; break 'scan; } + ArgKind::I64 + } + }; + observed.push(kind); + } + let arg_types: Vec<&'static str> = observed + .iter() + .map(|k| match k { + ArgKind::I64 => "I64", + ArgKind::F64 => "F64", + ArgKind::Handle => "Handle", + }) + .collect(); + match check_signature(&sym, &observed) { + Ok(()) => { + crate::jit::events::emit_lower( + serde_json::json!({"id": sym, "decision":"allow", "reason":"sig_ok", "argc": observed.len(), "arg_types": arg_types}), + "hostcall", + "", + ); + if crate::jit::config::current().native_f64 { + let (symbol, arity) = match method { + "math.sin" => ("nyash.math.sin_f64", 1), + "math.cos" => ("nyash.math.cos_f64", 1), + "math.abs" => ("nyash.math.abs_f64", 1), + "math.min" => ("nyash.math.min_f64", 2), + "math.max" => ("nyash.math.max_f64", 2), + _ => ("nyash.math.sin_f64", 1), + }; + for i in 0..arity { + if let Some(v) = args.get(i) { + if let Some(fv) = known_f64.get(v).copied() { + b.emit_const_f64(fv); + continue; + } + if let Some(iv) = known_i64.get(v).copied() { + b.emit_const_f64(iv as f64); + continue; + } + let mut emitted = false; + 'scan: for (_bb_id, bb) in func.blocks.iter() { + for ins in bb.instructions.iter() { + if let crate::mir::MirInstruction::NewBox { + dst, + box_type, + args: nb_args, + } = ins + { + if *dst == *v && box_type == "FloatBox" { + if let Some(srcv) = nb_args.get(0) { + if let Some(fv) = known_f64.get(srcv).copied() { + b.emit_const_f64(fv); + emitted = true; + break 'scan; + } + if let Some(iv) = known_i64.get(srcv).copied() { + b.emit_const_f64(iv as f64); + emitted = true; + break 'scan; } } } } } - if !emitted { b.emit_const_f64(0.0); } - } else { b.emit_const_f64(0.0); } + } + if !emitted { + b.emit_const_f64(0.0); + } + } else { + b.emit_const_f64(0.0); } - let kinds: Vec = (0..arity).map(|_| super::builder::ParamKind::F64).collect(); - b.emit_host_call_typed(symbol, &kinds, dst.is_some(), true); } - } - Err(reason) => { - crate::jit::events::emit_lower( - serde_json::json!({"id": sym, "decision":"fallback", "reason": reason, "argc": observed.len(), "arg_types": arg_types}), - "hostcall","" - ); + let kinds: Vec = + (0..arity).map(|_| super::builder::ParamKind::F64).collect(); + b.emit_host_call_typed(symbol, &kinds, dst.is_some(), true); } } - } - // Map/String/Array read methods and limited mutating (whitelist) - _ => { - // Whitelist-driven - let pol = crate::jit::policy::current(); - match method { - // String - "charCodeAt" => { - crate::jit::events::emit_lower( - serde_json::json!({"id": crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, "decision":"allow", "reason":"sig_ok", "argc":2, "arg_types":["Handle","I64"]}), - "hostcall","" - ); - // recvはHandle (param) を期待。indexはknown_i64でcoerce - if let Some(pidx) = param_index.get(recv).copied() { - b.emit_param_i64(pidx); - let idx = args.get(0).and_then(|v| known_i64.get(v).copied()).unwrap_or(0); - b.emit_const_i64(idx); - b.emit_host_call(crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, 2, dst.is_some()); - } - } - // Map - "size" => { lower_map_size_simple(b, param_index, recv, dst.is_some()); } - "get" => { if let Some(k) = args.get(0) { lower_map_get_simple(b, param_index, known_i64, recv, k, dst.is_some()); } } - "has" => { if let Some(k) = args.get(0) { lower_map_has_simple(b, param_index, known_i64, recv, k, dst.is_some()); } } - "set" => { if args.len() >= 2 { lower_map_set_simple(b, param_index, known_i64, recv, &args[0], &args[1]); } } - "has" => { - // Decide on key kind via registry and known values - use crate::jit::hostcall_registry::{check_signature, ArgKind}; - let canonical = "nyash.map.has".to_string(); - let mut observed_kinds: Vec = Vec::new(); - observed_kinds.push(ArgKind::Handle); - let key_vid = args.get(0).copied(); - let key_kind = if let Some(kv) = key_vid { - if let Some(mt) = func.signature.params.iter().find(|_| true) { // heuristic; signature may not align - match mt { - crate::mir::MirType::Float => ArgKind::I64, - crate::mir::MirType::Integer => ArgKind::I64, - crate::mir::MirType::Bool => ArgKind::I64, - crate::mir::MirType::String | crate::mir::MirType::Box(_) => ArgKind::Handle, - _ => ArgKind::Handle, - } - } else if let Some(_) = known_i64.get(&kv) { ArgKind::I64 } else { ArgKind::Handle } - } else { ArgKind::Handle }; - observed_kinds.push(key_kind); - let arg_types: Vec<&'static str> = observed_kinds.iter().map(|k| match k { ArgKind::I64 => "I64", ArgKind::F64 => "F64", ArgKind::Handle => "Handle" }).collect(); - let _ = check_signature(&canonical, &observed_kinds); - // HH fast-path if key is a Handle and also a param; otherwise H/I64 - if let Some(pidx) = param_index.get(recv).copied() { - b.emit_param_i64(pidx); - if let Some(kv) = key_vid { - match key_kind { - ArgKind::I64 => { - let kval = known_i64.get(&kv).copied().unwrap_or(0); - b.emit_const_i64(kval); - b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_GET_H, 2, dst.is_some()); - } - ArgKind::Handle => { - if let Some(kp) = param_index.get(&kv).copied() { - b.emit_param_i64(kp); - b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_GET_HH, 2, dst.is_some()); - } - } - _ => {} - } - } - } - } - // Array (mutating) - "push" | "set" => { - let wh = &pol.hostcall_whitelist; - let sym = if method == "push" { crate::jit::r#extern::collections::SYM_ARRAY_PUSH } else { crate::jit::r#extern::collections::SYM_ARRAY_SET }; - if wh.iter().any(|s| s == sym) { - crate::jit::events::emit_lower( - serde_json::json!({"id": sym, "decision":"allow", "reason":"whitelist", "argc": args.len()}), - "hostcall","" - ); - b.emit_host_call(sym, 2, false); - } else { - crate::jit::events::emit_lower( - serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating", "argc": args.len()}), - "hostcall","" - ); - } - } - _ => {} + Err(reason) => { + crate::jit::events::emit_lower( + serde_json::json!({"id": sym, "decision":"fallback", "reason": reason, "argc": observed.len(), "arg_types": arg_types}), + "hostcall", + "", + ); } } } + // Map/String/Array read methods and limited mutating (whitelist) + _ => { + // Whitelist-driven + let pol = crate::jit::policy::current(); + match method { + // String + "charCodeAt" => { + crate::jit::events::emit_lower( + serde_json::json!({"id": crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, "decision":"allow", "reason":"sig_ok", "argc":2, "arg_types":["Handle","I64"]}), + "hostcall", + "", + ); + // recvはHandle (param) を期待。indexはknown_i64でcoerce + if let Some(pidx) = param_index.get(recv).copied() { + b.emit_param_i64(pidx); + let idx = args + .get(0) + .and_then(|v| known_i64.get(v).copied()) + .unwrap_or(0); + b.emit_const_i64(idx); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, + 2, + dst.is_some(), + ); + } + } + // Map + "size" => { + lower_map_size_simple(b, param_index, recv, dst.is_some()); + } + "get" => { + if let Some(k) = args.get(0) { + lower_map_get_simple(b, param_index, known_i64, recv, k, dst.is_some()); + } + } + "has" => { + if let Some(k) = args.get(0) { + lower_map_has_simple(b, param_index, known_i64, recv, k, dst.is_some()); + } + } + "set" => { + if args.len() >= 2 { + lower_map_set_simple(b, param_index, known_i64, recv, &args[0], &args[1]); + } + } + "has" => { + // Decide on key kind via registry and known values + use crate::jit::hostcall_registry::{check_signature, ArgKind}; + let canonical = "nyash.map.has".to_string(); + let mut observed_kinds: Vec = Vec::new(); + observed_kinds.push(ArgKind::Handle); + let key_vid = args.get(0).copied(); + let key_kind = if let Some(kv) = key_vid { + if let Some(mt) = func.signature.params.iter().find(|_| true) { + // heuristic; signature may not align + match mt { + crate::mir::MirType::Float => ArgKind::I64, + crate::mir::MirType::Integer => ArgKind::I64, + crate::mir::MirType::Bool => ArgKind::I64, + crate::mir::MirType::String | crate::mir::MirType::Box(_) => { + ArgKind::Handle + } + _ => ArgKind::Handle, + } + } else if let Some(_) = known_i64.get(&kv) { + ArgKind::I64 + } else { + ArgKind::Handle + } + } else { + ArgKind::Handle + }; + observed_kinds.push(key_kind); + let arg_types: Vec<&'static str> = observed_kinds + .iter() + .map(|k| match k { + ArgKind::I64 => "I64", + ArgKind::F64 => "F64", + ArgKind::Handle => "Handle", + }) + .collect(); + let _ = check_signature(&canonical, &observed_kinds); + // HH fast-path if key is a Handle and also a param; otherwise H/I64 + if let Some(pidx) = param_index.get(recv).copied() { + b.emit_param_i64(pidx); + if let Some(kv) = key_vid { + match key_kind { + ArgKind::I64 => { + let kval = known_i64.get(&kv).copied().unwrap_or(0); + b.emit_const_i64(kval); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_GET_H, + 2, + dst.is_some(), + ); + } + ArgKind::Handle => { + if let Some(kp) = param_index.get(&kv).copied() { + b.emit_param_i64(kp); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_GET_HH, + 2, + dst.is_some(), + ); + } + } + _ => {} + } + } + } + } + // Array (mutating) + "push" | "set" => { + let wh = &pol.hostcall_whitelist; + let sym = if method == "push" { + crate::jit::r#extern::collections::SYM_ARRAY_PUSH + } else { + crate::jit::r#extern::collections::SYM_ARRAY_SET + }; + if wh.iter().any(|s| s == sym) { + crate::jit::events::emit_lower( + serde_json::json!({"id": sym, "decision":"allow", "reason":"whitelist", "argc": args.len()}), + "hostcall", + "", + ); + b.emit_host_call(sym, 2, false); + } else { + crate::jit::events::emit_lower( + serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating", "argc": args.len()}), + "hostcall", + "", + ); + } + } + _ => {} + } + } + } } // (was: lower_boxcall_simple_reads) Removed; logic consolidated in core.rs length/charCodeAt handlers. @@ -348,10 +516,15 @@ pub fn lower_map_get( crate::mir::MirType::Float => crate::jit::hostcall_registry::ArgKind::I64, // coerced via VM path crate::mir::MirType::Integer => crate::jit::hostcall_registry::ArgKind::I64, crate::mir::MirType::Bool => crate::jit::hostcall_registry::ArgKind::I64, - crate::mir::MirType::String | crate::mir::MirType::Box(_) => crate::jit::hostcall_registry::ArgKind::Handle, + crate::mir::MirType::String | crate::mir::MirType::Box(_) => { + crate::jit::hostcall_registry::ArgKind::Handle + } _ => { - if let Some(_) = args.get(0).and_then(|v| known_i64.get(v)) { crate::jit::hostcall_registry::ArgKind::I64 } - else { crate::jit::hostcall_registry::ArgKind::Handle } + if let Some(_) = args.get(0).and_then(|v| known_i64.get(v)) { + crate::jit::hostcall_registry::ArgKind::I64 + } else { + crate::jit::hostcall_registry::ArgKind::Handle + } } } } else if let Some(_) = args.get(0).and_then(|v| known_i64.get(v)) { @@ -359,17 +532,29 @@ pub fn lower_map_get( } else { crate::jit::hostcall_registry::ArgKind::Handle } - } else { crate::jit::hostcall_registry::ArgKind::I64 }; + } else { + crate::jit::hostcall_registry::ArgKind::I64 + }; observed_kinds.push(key_kind); - let arg_types: Vec<&'static str> = observed_kinds.iter().map(|k| match k { crate::jit::hostcall_registry::ArgKind::I64 => "I64", crate::jit::hostcall_registry::ArgKind::F64 => "F64", crate::jit::hostcall_registry::ArgKind::Handle => "Handle" }).collect(); + let arg_types: Vec<&'static str> = observed_kinds + .iter() + .map(|k| match k { + crate::jit::hostcall_registry::ArgKind::I64 => "I64", + crate::jit::hostcall_registry::ArgKind::F64 => "F64", + crate::jit::hostcall_registry::ArgKind::Handle => "Handle", + }) + .collect(); let canonical = "nyash.map.get_h"; match crate::jit::hostcall_registry::check_signature(canonical, &observed_kinds) { Ok(()) => { let event_id = if matches!(key_kind, crate::jit::hostcall_registry::ArgKind::Handle) - && args.get(0).and_then(|v| param_index.get(v)).is_some() { + && args.get(0).and_then(|v| param_index.get(v)).is_some() + { crate::jit::r#extern::collections::SYM_MAP_GET_HH - } else { crate::jit::r#extern::collections::SYM_MAP_GET_H }; + } else { + crate::jit::r#extern::collections::SYM_MAP_GET_H + }; crate::jit::events::emit_lower( serde_json::json!({ "id": event_id, @@ -378,17 +563,30 @@ pub fn lower_map_get( "argc": observed_kinds.len(), "arg_types": arg_types }), - "hostcall","" + "hostcall", + "", ); if matches!(key_kind, crate::jit::hostcall_registry::ArgKind::I64) { - let key_i = args.get(0).and_then(|v| known_i64.get(v)).copied().unwrap_or(0); + let key_i = args + .get(0) + .and_then(|v| known_i64.get(v)) + .copied() + .unwrap_or(0); b.emit_param_i64(pidx); b.emit_const_i64(key_i); - b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_GET_H, 2, dst.is_some()); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_GET_H, + 2, + dst.is_some(), + ); } else if let Some(kp) = args.get(0).and_then(|v| param_index.get(v)).copied() { b.emit_param_i64(pidx); b.emit_param_i64(kp); - b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_GET_HH, 2, dst.is_some()); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_GET_HH, + 2, + dst.is_some(), + ); } else { // Not a param: fall back (receiver_not_param or key_not_param already logged) } @@ -402,7 +600,8 @@ pub fn lower_map_get( "argc": observed_kinds.len(), "arg_types": arg_types }), - "hostcall","" + "hostcall", + "", ); } } @@ -416,15 +615,31 @@ pub fn lower_map_get( crate::mir::MirType::Integer => crate::jit::hostcall_registry::ArgKind::I64, crate::mir::MirType::Float => crate::jit::hostcall_registry::ArgKind::I64, crate::mir::MirType::Bool => crate::jit::hostcall_registry::ArgKind::I64, - crate::mir::MirType::String | crate::mir::MirType::Box(_) => crate::jit::hostcall_registry::ArgKind::Handle, + crate::mir::MirType::String | crate::mir::MirType::Box(_) => { + crate::jit::hostcall_registry::ArgKind::Handle + } _ => crate::jit::hostcall_registry::ArgKind::Handle, } - } else { crate::jit::hostcall_registry::ArgKind::Handle } - } else { crate::jit::hostcall_registry::ArgKind::Handle }; + } else { + crate::jit::hostcall_registry::ArgKind::Handle + } + } else { + crate::jit::hostcall_registry::ArgKind::Handle + }; observed_kinds.push(key_kind); - let arg_types: Vec<&'static str> = observed_kinds.iter().map(|k| match k { crate::jit::hostcall_registry::ArgKind::I64 => "I64", crate::jit::hostcall_registry::ArgKind::F64 => "F64", crate::jit::hostcall_registry::ArgKind::Handle => "Handle" }).collect(); + let arg_types: Vec<&'static str> = observed_kinds + .iter() + .map(|k| match k { + crate::jit::hostcall_registry::ArgKind::I64 => "I64", + crate::jit::hostcall_registry::ArgKind::F64 => "F64", + crate::jit::hostcall_registry::ArgKind::Handle => "Handle", + }) + .collect(); let sym = "nyash.map.get_h"; - let decision = match crate::jit::hostcall_registry::check_signature(sym, &observed_kinds) { Ok(()) => ("fallback", "receiver_not_param"), Err(reason) => ("fallback", reason) }; + let decision = match crate::jit::hostcall_registry::check_signature(sym, &observed_kinds) { + Ok(()) => ("fallback", "receiver_not_param"), + Err(reason) => ("fallback", reason), + }; crate::jit::events::emit_lower( serde_json::json!({ "id": sym, @@ -433,7 +648,8 @@ pub fn lower_map_get( "argc": observed_kinds.len(), "arg_types": arg_types }), - "hostcall","" + "hostcall", + "", ); } } @@ -447,10 +663,18 @@ pub fn lower_map_has( dst: Option, ) { if let Some(pidx) = param_index.get(recv).copied() { - let key = args.get(0).and_then(|v| known_i64.get(v)).copied().unwrap_or(0); + let key = args + .get(0) + .and_then(|v| known_i64.get(v)) + .copied() + .unwrap_or(0); b.emit_param_i64(pidx); b.emit_const_i64(key); - b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_HAS_H, 2, dst.is_some()); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_MAP_HAS_H, + 2, + dst.is_some(), + ); } } @@ -478,21 +702,37 @@ pub fn lower_math_call( crate::mir::MirType::Bool => ArgKind::I64, crate::mir::MirType::String | crate::mir::MirType::Box(_) => ArgKind::Handle, _ => { - if known_f64.contains_key(v) || float_box_values.contains(v) { ArgKind::F64 } else { ArgKind::I64 } + if known_f64.contains_key(v) || float_box_values.contains(v) { + ArgKind::F64 + } else { + ArgKind::I64 + } } } } else { - if known_f64.contains_key(v) || float_box_values.contains(v) { ArgKind::F64 } else { ArgKind::I64 } + if known_f64.contains_key(v) || float_box_values.contains(v) { + ArgKind::F64 + } else { + ArgKind::I64 + } }; observed_kinds.push(kind); } - let arg_types: Vec<&'static str> = observed_kinds.iter().map(|k| match k { ArgKind::I64 => "I64", ArgKind::F64 => "F64", ArgKind::Handle => "Handle" }).collect(); + let arg_types: Vec<&'static str> = observed_kinds + .iter() + .map(|k| match k { + ArgKind::I64 => "I64", + ArgKind::F64 => "F64", + ArgKind::Handle => "Handle", + }) + .collect(); match check_signature(&sym, &observed_kinds) { Ok(()) => { crate::jit::events::emit_lower( serde_json::json!({"id": sym, "decision":"allow", "reason":"sig_ok", "argc": observed_kinds.len(), "arg_types": arg_types}), - "hostcall","" + "hostcall", + "", ); if crate::jit::config::current().native_f64 { let (symbol, arity) = match method { @@ -505,32 +745,57 @@ pub fn lower_math_call( }; for i in 0..arity { if let Some(v) = args.get(i) { - if let Some(fv) = known_f64.get(v).copied() { b.emit_const_f64(fv); continue; } - if let Some(iv) = known_i64.get(v).copied() { b.emit_const_f64(iv as f64); continue; } + if let Some(fv) = known_f64.get(v).copied() { + b.emit_const_f64(fv); + continue; + } + if let Some(iv) = known_i64.get(v).copied() { + b.emit_const_f64(iv as f64); + continue; + } let mut emitted = false; 'scan: for (_bb_id, bb) in func.blocks.iter() { for ins in bb.instructions.iter() { - if let crate::mir::MirInstruction::NewBox { dst, box_type, args: nb_args } = ins { + if let crate::mir::MirInstruction::NewBox { + dst, + box_type, + args: nb_args, + } = ins + { if *dst == *v && box_type == "FloatBox" { if let Some(srcv) = nb_args.get(0) { - if let Some(fv) = known_f64.get(srcv).copied() { b.emit_const_f64(fv); emitted = true; break 'scan; } - if let Some(iv) = known_i64.get(srcv).copied() { b.emit_const_f64(iv as f64); emitted = true; break 'scan; } + if let Some(fv) = known_f64.get(srcv).copied() { + b.emit_const_f64(fv); + emitted = true; + break 'scan; + } + if let Some(iv) = known_i64.get(srcv).copied() { + b.emit_const_f64(iv as f64); + emitted = true; + break 'scan; + } } } } } } - if !emitted { b.emit_const_f64(0.0); } - } else { b.emit_const_f64(0.0); } + if !emitted { + b.emit_const_f64(0.0); + } + } else { + b.emit_const_f64(0.0); + } } - let kinds: Vec = (0..arity).map(|_| super::builder::ParamKind::F64).collect(); + let kinds: Vec = + (0..arity).map(|_| super::builder::ParamKind::F64).collect(); b.emit_host_call_typed(symbol, &kinds, dst.is_some(), true); } } Err(reason) => { crate::jit::events::emit_lower( serde_json::json!({"id": sym, "decision":"fallback", "reason": reason, "argc": observed_kinds.len(), "arg_types": arg_types}), - "hostcall","" + "hostcall", + "", ); } } diff --git a/src/jit/lower/core_ops.rs b/src/jit/lower/core_ops.rs index 7a3f85e1..9243c61b 100644 --- a/src/jit/lower/core_ops.rs +++ b/src/jit/lower/core_ops.rs @@ -1,6 +1,6 @@ //! Core ops lowering (non-hostcall): BinOp, Compare, Branch, Jump -use super::builder::{IRBuilder, BinOpKind, CmpKind}; -use crate::mir::{BinaryOp, CompareOp, ValueId, MirFunction, MirType}; +use super::builder::{BinOpKind, CmpKind, IRBuilder}; +use crate::mir::{BinaryOp, CompareOp, MirFunction, MirType, ValueId}; use super::core::LowerCore; @@ -8,40 +8,88 @@ impl LowerCore { fn is_string_like(&self, func: &MirFunction, v: &ValueId) -> bool { // Check per-value type metadata if let Some(mt) = func.metadata.value_types.get(v) { - if matches!(mt, MirType::String) { return true; } - if let MirType::Box(ref name) = mt { if name == "StringBox" { return true; } } + if matches!(mt, MirType::String) { + return true; + } + if let MirType::Box(ref name) = mt { + if name == "StringBox" { + return true; + } + } } // Check if this value is a parameter with String or StringBox type if let Some(pidx) = self.param_index.get(v).copied() { if let Some(pt) = func.signature.params.get(pidx) { - if matches!(pt, MirType::String) { return true; } - if let MirType::Box(ref name) = pt { if name == "StringBox" { return true; } } + if matches!(pt, MirType::String) { + return true; + } + if let MirType::Box(ref name) = pt { + if name == "StringBox" { + return true; + } + } } } // Check if it originates from a StringBox NewBox - if let Some(name) = self.box_type_map.get(v) { if name == "StringBox" { return true; } } + if let Some(name) = self.box_type_map.get(v) { + if name == "StringBox" { + return true; + } + } false } - pub fn lower_binop(&mut self, b: &mut dyn IRBuilder, op: &BinaryOp, lhs: &ValueId, rhs: &ValueId, dst: &ValueId, func: &MirFunction) { + pub fn lower_binop( + &mut self, + b: &mut dyn IRBuilder, + op: &BinaryOp, + lhs: &ValueId, + rhs: &ValueId, + dst: &ValueId, + func: &MirFunction, + ) { // Optional: consult unified grammar for operator strategy (non-invasive logging) if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { match op { BinaryOp::Add => { let strat = crate::grammar::engine::get().add_coercion_strategy(); - crate::jit::events::emit("grammar","add", None, None, serde_json::json!({"coercion": strat})); + crate::jit::events::emit( + "grammar", + "add", + None, + None, + serde_json::json!({"coercion": strat}), + ); } BinaryOp::Sub => { let strat = crate::grammar::engine::get().sub_coercion_strategy(); - crate::jit::events::emit("grammar","sub", None, None, serde_json::json!({"coercion": strat})); + crate::jit::events::emit( + "grammar", + "sub", + None, + None, + serde_json::json!({"coercion": strat}), + ); } BinaryOp::Mul => { let strat = crate::grammar::engine::get().mul_coercion_strategy(); - crate::jit::events::emit("grammar","mul", None, None, serde_json::json!({"coercion": strat})); + crate::jit::events::emit( + "grammar", + "mul", + None, + None, + serde_json::json!({"coercion": strat}), + ); } BinaryOp::Div => { let strat = crate::grammar::engine::get().div_coercion_strategy(); - crate::jit::events::emit("grammar","div", None, None, serde_json::json!({"coercion": strat})); + crate::jit::events::emit( + "grammar", + "div", + None, + None, + serde_json::json!({"coercion": strat}), + ); } _ => {} } @@ -52,24 +100,44 @@ impl LowerCore { if self.is_string_like(func, lhs) || self.is_string_like(func, rhs) { self.push_value_if_known_or_param(b, lhs); self.push_value_if_known_or_param(b, rhs); - b.emit_host_call(crate::jit::r#extern::collections::SYM_STRING_CONCAT_HH, 2, true); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_STRING_CONCAT_HH, + 2, + true, + ); // Track handle result for downstream usages self.handle_values.insert(*dst); - let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); return; } // If dynamic Box/Unknown types, route to unified semantics add (handle,handle) - let is_dynamic = match (func.metadata.value_types.get(lhs), func.metadata.value_types.get(rhs)) { - (Some(MirType::Box(_)) | Some(MirType::Unknown) | None, _) | (_, Some(MirType::Box(_)) | Some(MirType::Unknown) | None) => true, + let is_dynamic = match ( + func.metadata.value_types.get(lhs), + func.metadata.value_types.get(rhs), + ) { + (Some(MirType::Box(_)) | Some(MirType::Unknown) | None, _) + | (_, Some(MirType::Box(_)) | Some(MirType::Unknown) | None) => true, _ => false, }; if is_dynamic { self.push_value_if_known_or_param(b, lhs); self.push_value_if_known_or_param(b, rhs); - b.emit_host_call(crate::jit::r#extern::collections::SYM_SEMANTICS_ADD_HH, 2, true); + b.emit_host_call( + crate::jit::r#extern::collections::SYM_SEMANTICS_ADD_HH, + 2, + true, + ); self.handle_values.insert(*dst); - let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); return; } @@ -83,7 +151,9 @@ impl LowerCore { BinaryOp::Mul => BinOpKind::Mul, BinaryOp::Div => BinOpKind::Div, BinaryOp::Mod => BinOpKind::Mod, - _ => { return; } + _ => { + return; + } }; b.emit_binop(kind); if let (Some(a), Some(bv)) = (self.known_i64.get(lhs), self.known_i64.get(rhs)) { @@ -91,22 +161,46 @@ impl LowerCore { BinaryOp::Add => a.wrapping_add(*bv), BinaryOp::Sub => a.wrapping_sub(*bv), BinaryOp::Mul => a.wrapping_mul(*bv), - BinaryOp::Div => if *bv != 0 { a.wrapping_div(*bv) } else { 0 }, - BinaryOp::Mod => if *bv != 0 { a.wrapping_rem(*bv) } else { 0 }, + BinaryOp::Div => { + if *bv != 0 { + a.wrapping_div(*bv) + } else { + 0 + } + } + BinaryOp::Mod => { + if *bv != 0 { + a.wrapping_rem(*bv) + } else { + 0 + } + } _ => 0, }; self.known_i64.insert(*dst, res); } } - pub fn lower_compare(&mut self, b: &mut dyn IRBuilder, op: &CompareOp, lhs: &ValueId, rhs: &ValueId, dst: &ValueId, func: &MirFunction) { + pub fn lower_compare( + &mut self, + b: &mut dyn IRBuilder, + op: &CompareOp, + lhs: &ValueId, + rhs: &ValueId, + dst: &ValueId, + func: &MirFunction, + ) { // Route string-like comparisons (Eq/Lt) to hostcalls (i64 0/1) if crate::jit::config::current().hostcall { if matches!(op, CompareOp::Eq | CompareOp::Lt) { if self.is_string_like(func, lhs) || self.is_string_like(func, rhs) { self.push_value_if_known_or_param(b, lhs); self.push_value_if_known_or_param(b, rhs); - let sym = match op { CompareOp::Eq => crate::jit::r#extern::collections::SYM_STRING_EQ_HH, CompareOp::Lt => crate::jit::r#extern::collections::SYM_STRING_LT_HH, _ => unreachable!() }; + let sym = match op { + CompareOp::Eq => crate::jit::r#extern::collections::SYM_STRING_EQ_HH, + CompareOp::Lt => crate::jit::r#extern::collections::SYM_STRING_LT_HH, + _ => unreachable!(), + }; b.emit_host_call(sym, 2, true); self.bool_values.insert(*dst); return; @@ -126,12 +220,20 @@ impl LowerCore { b.emit_compare(kind); // Persist compare result in a local slot so terminators (Branch) can reload it reliably self.bool_values.insert(*dst); - let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id }); + let slot = *self.local_index.entry(*dst).or_insert_with(|| { + let id = self.next_local; + self.next_local += 1; + id + }); b.store_local_i64(slot); } - pub fn lower_jump(&mut self, b: &mut dyn IRBuilder) { b.emit_jump(); } - pub fn lower_branch(&mut self, b: &mut dyn IRBuilder) { b.emit_branch(); } + pub fn lower_jump(&mut self, b: &mut dyn IRBuilder) { + b.emit_jump(); + } + pub fn lower_branch(&mut self, b: &mut dyn IRBuilder) { + b.emit_branch(); + } } // Methods moved from core.rs to reduce file size and centralize op helpers @@ -139,10 +241,20 @@ impl LowerCore { // Push a value if known or param/local/phi pub(super) fn push_value_if_known_or_param(&self, b: &mut dyn IRBuilder, id: &ValueId) { // Prefer compile-time known constants to avoid stale local slots overshadowing folded values - if let Some(v) = self.known_i64.get(id).copied() { b.emit_const_i64(v); return; } - if let Some(slot) = self.local_index.get(id).copied() { b.load_local_i64(slot); return; } + if let Some(v) = self.known_i64.get(id).copied() { + b.emit_const_i64(v); + return; + } + if let Some(slot) = self.local_index.get(id).copied() { + b.load_local_i64(slot); + return; + } if self.phi_values.contains(id) { - let pos = self.phi_param_index.iter().find_map(|((_, vid), idx)| if vid == id { Some(*idx) } else { None }).unwrap_or(0); + let pos = self + .phi_param_index + .iter() + .find_map(|((_, vid), idx)| if vid == id { Some(*idx) } else { None }) + .unwrap_or(0); if crate::jit::config::current().native_bool && self.bool_phi_values.contains(id) { b.push_block_param_b1_at(pos); } else { @@ -150,7 +262,10 @@ impl LowerCore { } return; } - if let Some(pidx) = self.param_index.get(id).copied() { b.emit_param_i64(pidx); return; } + if let Some(pidx) = self.param_index.get(id).copied() { + b.emit_param_i64(pidx); + return; + } } // Coverage helper: increments covered/unsupported counts @@ -182,7 +297,10 @@ impl LowerCore { | I::Nop | I::PluginInvoke { .. } ); - if supported { self.covered += 1; } else { self.unsupported += 1; } + if supported { + self.covered += 1; + } else { + self.unsupported += 1; + } } - } diff --git a/src/jit/lower/extern_thunks.rs b/src/jit/lower/extern_thunks.rs index 53e9b2f3..27fb75b0 100644 --- a/src/jit/lower/extern_thunks.rs +++ b/src/jit/lower/extern_thunks.rs @@ -15,21 +15,35 @@ use crate::runtime::plugin_loader_v2::PluginBoxV2; #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_box_birth_h(type_id: i64) -> i64 { // Map type_id -> type name and create via plugin host; return runtime handle - if type_id <= 0 { return 0; } + if type_id <= 0 { + return 0; + } let tid = type_id as u32; if let Some(meta) = crate::runtime::plugin_loader_v2::metadata_for_type_id(tid) { if let Ok(host) = crate::runtime::get_global_plugin_host().read() { if let Ok(b) = host.create_box(&meta.box_type, &[]) { let arc: std::sync::Arc = std::sync::Arc::from(b); let h = crate::jit::rt::handles::to_handle(arc); - events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_h", "box_type": meta.box_type, "type_id": meta.type_id, "handle": h}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": "nyash.box.birth_h", "box_type": meta.box_type, "type_id": meta.type_id, "handle": h}), + "hostcall", + "", + ); return h as i64; } else { - events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_h", "error": "create_failed", "box_type": meta.box_type, "type_id": meta.type_id}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": "nyash.box.birth_h", "error": "create_failed", "box_type": meta.box_type, "type_id": meta.type_id}), + "hostcall", + "", + ); } } } else { - events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_h", "error": "type_map_failed", "type_id": tid}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": "nyash.box.birth_h", "error": "type_map_failed", "type_id": tid}), + "hostcall", + "", + ); } 0 } @@ -37,15 +51,22 @@ pub(super) extern "C" fn nyash_box_birth_h(type_id: i64) -> i64 { #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_box_birth_i64(type_id: i64, argc: i64, a1: i64, a2: i64) -> i64 { use crate::runtime::plugin_loader_v2::PluginBoxV2; - if type_id <= 0 { return 0; } + if type_id <= 0 { + return 0; + } // Resolve invoke for the type via loader metadata let Some(meta) = crate::runtime::plugin_loader_v2::metadata_for_type_id(type_id as u32) else { - events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_i64", "error": "type_map_failed", "type_id": type_id}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": "nyash.box.birth_i64", "error": "type_map_failed", "type_id": type_id}), + "hostcall", + "", + ); return 0; }; let invoke_fn = meta.invoke_fn; let box_type = meta.box_type.clone(); - let method_id: u32 = 0; let instance_id: u32 = 0; + let method_id: u32 = 0; + let instance_id: u32 = 0; // Build TLV from a1/a2 let nargs = argc.max(0) as usize; let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16); @@ -56,40 +77,106 @@ pub(super) extern "C" fn nyash_box_birth_i64(type_id: i64, argc: i64, a1: i64, a let host = crate::runtime::get_global_plugin_host(); if let Ok(hg) = host.read() { if p.box_type == "StringBox" { - if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) { - if let Some(s) = sb.as_any().downcast_ref::() { crate::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value); return; } + if let Ok(Some(sb)) = hg.invoke_instance_method( + "StringBox", + "toUtf8", + p.instance_id(), + &[], + ) { + if let Some(s) = + sb.as_any().downcast_ref::() + { + crate::runtime::plugin_ffi_common::encode::string( + &mut buf, &s.value, + ); + return; + } } } else if p.box_type == "IntegerBox" { - if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) { - if let Some(i) = ibx.as_any().downcast_ref::() { crate::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value); return; } + if let Ok(Some(ibx)) = + hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) + { + if let Some(i) = + ibx.as_any().downcast_ref::() + { + crate::runtime::plugin_ffi_common::encode::i64( + &mut buf, i.value, + ); + return; + } } } } - crate::runtime::plugin_ffi_common::encode::plugin_handle(&mut buf, p.inner.type_id, p.instance_id()); + crate::runtime::plugin_ffi_common::encode::plugin_handle( + &mut buf, + p.inner.type_id, + p.instance_id(), + ); return; } } } crate::runtime::plugin_ffi_common::encode::i64(&mut buf, h); }; - if nargs >= 1 { encode_val(a1); } - if nargs >= 2 { encode_val(a2); } + if nargs >= 1 { + encode_val(a1); + } + if nargs >= 2 { + encode_val(a2); + } // Invoke - let mut out = vec![0u8; 1024]; let mut out_len: usize = out.len(); - let rc = unsafe { invoke_fn(type_id as u32, method_id, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) }; - if rc != 0 { events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_i64", "error": "invoke_failed", "type_id": type_id}), "hostcall", ""); return 0; } - if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) { - if tag == 8 && payload.len()==8 { - let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); - let r_type = u32::from_le_bytes(t); let r_inst = u32::from_le_bytes(i); - let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2(box_type.clone(), r_type, r_inst, invoke_fn); + let mut out = vec![0u8; 1024]; + let mut out_len: usize = out.len(); + let rc = unsafe { + invoke_fn( + type_id as u32, + method_id, + instance_id, + buf.as_ptr(), + buf.len(), + out.as_mut_ptr(), + &mut out_len, + ) + }; + if rc != 0 { + events::emit_runtime( + serde_json::json!({"id": "nyash.box.birth_i64", "error": "invoke_failed", "type_id": type_id}), + "hostcall", + "", + ); + return 0; + } + if let Some((tag, _sz, payload)) = + crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) + { + if tag == 8 && payload.len() == 8 { + let mut t = [0u8; 4]; + t.copy_from_slice(&payload[0..4]); + let mut i = [0u8; 4]; + i.copy_from_slice(&payload[4..8]); + let r_type = u32::from_le_bytes(t); + let r_inst = u32::from_le_bytes(i); + let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2( + box_type.clone(), + r_type, + r_inst, + invoke_fn, + ); let arc: std::sync::Arc = std::sync::Arc::new(pb); let h = crate::jit::rt::handles::to_handle(arc); - events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_i64", "box_type": box_type, "type_id": type_id, "argc": nargs, "handle": h}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": "nyash.box.birth_i64", "box_type": box_type, "type_id": type_id, "argc": nargs, "handle": h}), + "hostcall", + "", + ); return h as i64; } } - events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_i64", "error": "decode_failed", "type_id": type_id}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": "nyash.box.birth_i64", "error": "decode_failed", "type_id": type_id}), + "hostcall", + "", + ); 0 } @@ -100,14 +187,17 @@ pub(super) extern "C" fn nyash_handle_of(v: i64) -> i64 { if std::env::var("NYASH_JIT_TRACE_LEN").ok().as_deref() == Some("1") { eprintln!("[JIT-HANDLE_OF] in v={}", v); } - if v > 0 { return v; } + if v > 0 { + return v; + } // Otherwise interpret as legacy param index and convert BoxRef -> handle if v >= 0 { let idx = v as usize; let mut out: i64 = 0; crate::jit::rt::with_legacy_vm_args(|args| { if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) { - let arc: std::sync::Arc = std::sync::Arc::from(b.clone()); + let arc: std::sync::Arc = + std::sync::Arc::from(b.clone()); out = crate::jit::rt::handles::to_handle(arc) as i64; } }); @@ -121,15 +211,25 @@ pub(super) extern "C" fn nyash_handle_of(v: i64) -> i64 { // ---- Math (native f64) ---- #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_math_sin_f64(x: f64) -> f64 { x.sin() } +pub(super) extern "C" fn nyash_math_sin_f64(x: f64) -> f64 { + x.sin() +} #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_math_cos_f64(x: f64) -> f64 { x.cos() } +pub(super) extern "C" fn nyash_math_cos_f64(x: f64) -> f64 { + x.cos() +} #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_math_abs_f64(x: f64) -> f64 { x.abs() } +pub(super) extern "C" fn nyash_math_abs_f64(x: f64) -> f64 { + x.abs() +} #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_math_min_f64(a: f64, b: f64) -> f64 { a.min(b) } +pub(super) extern "C" fn nyash_math_min_f64(a: f64, b: f64) -> f64 { + a.min(b) +} #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_math_max_f64(a: f64, b: f64) -> f64 { a.max(b) } +pub(super) extern "C" fn nyash_math_max_f64(a: f64, b: f64) -> f64 { + a.max(b) +} // ---- Console (handle) ---- #[cfg(feature = "cranelift-jit")] @@ -168,10 +268,20 @@ pub(super) extern "C" fn nyash_gc_barrier_write(handle_or_ptr: u64) -> i64 { // ---- Array (handle) ---- #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_array_len_h(handle: u64) -> i64 { - events::emit_runtime(serde_json::json!({"id": c::SYM_ARRAY_LEN_H, "decision":"allow", "argc":1, "arg_types":["Handle"]}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": c::SYM_ARRAY_LEN_H, "decision":"allow", "argc":1, "arg_types":["Handle"]}), + "hostcall", + "", + ); if let Some(obj) = crate::jit::rt::handles::get(handle) { if let Some(arr) = obj.as_any().downcast_ref::() { - if let Some(ib) = arr.length().as_any().downcast_ref::() { return ib.value; } + if let Some(ib) = arr + .length() + .as_any() + .downcast_ref::() + { + return ib.value; + } } } 0 @@ -179,11 +289,17 @@ pub(super) extern "C" fn nyash_array_len_h(handle: u64) -> i64 { #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_array_get_h(handle: u64, idx: i64) -> i64 { - events::emit_runtime(serde_json::json!({"id": c::SYM_ARRAY_GET_H, "decision":"allow", "argc":2, "arg_types":["Handle","I64"]}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": c::SYM_ARRAY_GET_H, "decision":"allow", "argc":2, "arg_types":["Handle","I64"]}), + "hostcall", + "", + ); if let Some(obj) = crate::jit::rt::handles::get(handle) { if let Some(arr) = obj.as_any().downcast_ref::() { let val = arr.get(Box::new(crate::box_trait::IntegerBox::new(idx))); - if let Some(ib) = val.as_any().downcast_ref::() { return ib.value; } + if let Some(ib) = val.as_any().downcast_ref::() { + return ib.value; + } } } 0 @@ -195,7 +311,9 @@ pub(super) extern "C" fn nyash_array_last_h(handle: u64) -> i64 { if let Some(arr) = obj.as_any().downcast_ref::() { if let Ok(items) = arr.items.read() { if let Some(last) = items.last() { - if let Some(ib) = last.as_any().downcast_ref::() { return ib.value; } + if let Some(ib) = last.as_any().downcast_ref::() { + return ib.value; + } } } } @@ -210,13 +328,24 @@ pub(super) extern "C" fn nyash_array_set_h(handle: u64, idx: i64, val: i64) -> i let pol = crate::jit::policy::current(); let wh = pol.hostcall_whitelist; if classify(sym) == HostcallKind::Mutating && pol.read_only && !wh.iter().any(|s| s == sym) { - events::emit_runtime(serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating"}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating"}), + "hostcall", + "", + ); return 0; } if let Some(obj) = crate::jit::rt::handles::get(handle) { if let Some(arr) = obj.as_any().downcast_ref::() { - let _ = arr.set(Box::new(crate::box_trait::IntegerBox::new(idx)), Box::new(crate::box_trait::IntegerBox::new(val))); - events::emit_runtime(serde_json::json!({"id": sym, "decision":"allow", "argc":3, "arg_types":["Handle","I64","I64"]}), "hostcall", ""); + let _ = arr.set( + Box::new(crate::box_trait::IntegerBox::new(idx)), + Box::new(crate::box_trait::IntegerBox::new(val)), + ); + events::emit_runtime( + serde_json::json!({"id": sym, "decision":"allow", "argc":3, "arg_types":["Handle","I64","I64"]}), + "hostcall", + "", + ); return 0; } } @@ -231,7 +360,11 @@ pub(super) extern "C" fn nyash_array_set_hh(handle: u64, idx: i64, val_h: u64) - let pol = crate::jit::policy::current(); let wh = pol.hostcall_whitelist; if classify(sym) == HostcallKind::Mutating && pol.read_only && !wh.iter().any(|s| s == sym) { - events::emit_runtime(serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating"}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating"}), + "hostcall", + "", + ); return 0; } if let Some(obj) = crate::jit::rt::handles::get(handle) { @@ -241,7 +374,11 @@ pub(super) extern "C" fn nyash_array_set_hh(handle: u64, idx: i64, val_h: u64) - // Prefer share semantics for identity boxes let val_box: Box = v_arc.as_ref().clone_or_share(); let _ = arr.set(Box::new(crate::box_trait::IntegerBox::new(idx)), val_box); - events::emit_runtime(serde_json::json!({"id": sym, "decision":"allow", "argc":3, "arg_types":["Handle","I64","Handle"]}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": sym, "decision":"allow", "argc":3, "arg_types":["Handle","I64","Handle"]}), + "hostcall", + "", + ); return 0; } } @@ -256,14 +393,22 @@ pub(super) extern "C" fn nyash_array_push_h(handle: u64, val: i64) -> i64 { let pol = crate::jit::policy::current(); let wh = pol.hostcall_whitelist; if classify(sym) == HostcallKind::Mutating && pol.read_only && !wh.iter().any(|s| s == sym) { - events::emit_runtime(serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating"}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating"}), + "hostcall", + "", + ); return 0; } if let Some(obj) = crate::jit::rt::handles::get(handle) { if let Some(arr) = obj.as_any().downcast_ref::() { let ib = crate::box_trait::IntegerBox::new(val); let _ = arr.push(Box::new(ib)); - events::emit_runtime(serde_json::json!({"id": sym, "decision":"allow", "argc":2, "arg_types":["Handle","I64"]}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": sym, "decision":"allow", "argc":2, "arg_types":["Handle","I64"]}), + "hostcall", + "", + ); return 0; } } @@ -273,10 +418,20 @@ pub(super) extern "C" fn nyash_array_push_h(handle: u64, val: i64) -> i64 { // ---- Map (handle) ---- #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_map_size_h(handle: u64) -> i64 { - events::emit_runtime(serde_json::json!({"id": c::SYM_MAP_SIZE_H, "decision":"allow", "argc":1, "arg_types":["Handle"]}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": c::SYM_MAP_SIZE_H, "decision":"allow", "argc":1, "arg_types":["Handle"]}), + "hostcall", + "", + ); if let Some(obj) = crate::jit::rt::handles::get(handle) { if let Some(map) = obj.as_any().downcast_ref::() { - if let Some(ib) = map.size().as_any().downcast_ref::() { return ib.value; } + if let Some(ib) = map + .size() + .as_any() + .downcast_ref::() + { + return ib.value; + } } } 0 @@ -284,12 +439,18 @@ pub(super) extern "C" fn nyash_map_size_h(handle: u64) -> i64 { #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_map_get_h(handle: u64, key: i64) -> i64 { - events::emit_runtime(serde_json::json!({"id": c::SYM_MAP_GET_H, "decision":"allow", "argc":2, "arg_types":["Handle","I64"]}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": c::SYM_MAP_GET_H, "decision":"allow", "argc":2, "arg_types":["Handle","I64"]}), + "hostcall", + "", + ); if let Some(obj) = crate::jit::rt::handles::get(handle) { if let Some(map) = obj.as_any().downcast_ref::() { let key_box = Box::new(crate::box_trait::IntegerBox::new(key)); let val = map.get(key_box); - if let Some(ib) = val.as_any().downcast_ref::() { return ib.value; } + if let Some(ib) = val.as_any().downcast_ref::() { + return ib.value; + } } } 0 @@ -297,11 +458,18 @@ pub(super) extern "C" fn nyash_map_get_h(handle: u64, key: i64) -> i64 { #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_map_get_hh(map_h: u64, key_h: u64) -> i64 { - events::emit_runtime(serde_json::json!({"id": c::SYM_MAP_GET_HH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": c::SYM_MAP_GET_HH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}), + "hostcall", + "", + ); let map_arc = crate::jit::rt::handles::get(map_h); let key_arc = crate::jit::rt::handles::get(key_h); if let (Some(mobj), Some(kobj)) = (map_arc, key_arc) { - if let Some(map) = mobj.as_any().downcast_ref::() { + if let Some(map) = mobj + .as_any() + .downcast_ref::() + { let key_box: Box = kobj.share_box(); let val = map.get(key_box); let arc: std::sync::Arc = std::sync::Arc::from(val); @@ -319,7 +487,11 @@ pub(super) extern "C" fn nyash_map_set_h(handle: u64, key: i64, val: i64) -> i64 let pol = crate::jit::policy::current(); let wh = pol.hostcall_whitelist; if classify(sym) == HostcallKind::Mutating && pol.read_only && !wh.iter().any(|s| s == sym) { - events::emit_runtime(serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating"}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating"}), + "hostcall", + "", + ); return 0; } if let Some(obj) = crate::jit::rt::handles::get(handle) { @@ -327,7 +499,11 @@ pub(super) extern "C" fn nyash_map_set_h(handle: u64, key: i64, val: i64) -> i64 let key_box = Box::new(crate::box_trait::IntegerBox::new(key)); let val_box = Box::new(crate::box_trait::IntegerBox::new(val)); let _ = map.set(key_box, val_box); - events::emit_runtime(serde_json::json!({"id": sym, "decision":"allow", "argc":3, "arg_types":["Handle","I64","I64"]}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": sym, "decision":"allow", "argc":3, "arg_types":["Handle","I64","I64"]}), + "hostcall", + "", + ); return 0; } } @@ -336,7 +512,11 @@ pub(super) extern "C" fn nyash_map_set_h(handle: u64, key: i64, val: i64) -> i64 #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_map_has_h(handle: u64, key: i64) -> i64 { - events::emit_runtime(serde_json::json!({"id": c::SYM_MAP_HAS_H, "decision":"allow", "argc":2, "arg_types":["Handle","I64"]}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": c::SYM_MAP_HAS_H, "decision":"allow", "argc":2, "arg_types":["Handle","I64"]}), + "hostcall", + "", + ); if let Some(obj) = crate::jit::rt::handles::get(handle) { if let Some(map) = obj.as_any().downcast_ref::() { let key_box = Box::new(crate::box_trait::IntegerBox::new(key)); @@ -351,10 +531,20 @@ pub(super) extern "C" fn nyash_map_has_h(handle: u64, key: i64) -> i64 { // ---- Any helpers ---- #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_any_length_h(handle: u64) -> i64 { - events::emit_runtime(serde_json::json!({"id": c::SYM_ANY_LEN_H, "decision":"allow", "argc":1, "arg_types":["Handle"]}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": c::SYM_ANY_LEN_H, "decision":"allow", "argc":1, "arg_types":["Handle"]}), + "hostcall", + "", + ); if let Some(obj) = crate::jit::rt::handles::get(handle) { if let Some(arr) = obj.as_any().downcast_ref::() { - if let Some(ib) = arr.length().as_any().downcast_ref::() { return ib.value; } + if let Some(ib) = arr + .length() + .as_any() + .downcast_ref::() + { + return ib.value; + } } if let Some(sb) = obj.as_any().downcast_ref::() { return sb.value.len() as i64; @@ -368,14 +558,24 @@ pub(super) extern "C" fn nyash_any_length_h(handle: u64) -> i64 { if let Some(v) = val { match v { crate::backend::vm::VMValue::BoxRef(b) => { - if let Some(arr) = b.as_any().downcast_ref::() { - if let Some(ib) = arr.length().as_any().downcast_ref::() { return ib.value; } + if let Some(arr) = + b.as_any().downcast_ref::() + { + if let Some(ib) = arr + .length() + .as_any() + .downcast_ref::() + { + return ib.value; + } } if let Some(sb) = b.as_any().downcast_ref::() { return sb.value.len() as i64; } } - crate::backend::vm::VMValue::String(s) => { return s.len() as i64; } + crate::backend::vm::VMValue::String(s) => { + return s.len() as i64; + } _ => {} } } @@ -386,16 +586,28 @@ pub(super) extern "C" fn nyash_any_length_h(handle: u64) -> i64 { #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_any_is_empty_h(handle: u64) -> i64 { - events::emit_runtime(serde_json::json!({"id": c::SYM_ANY_IS_EMPTY_H, "decision":"allow", "argc":1, "arg_types":["Handle"]}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": c::SYM_ANY_IS_EMPTY_H, "decision":"allow", "argc":1, "arg_types":["Handle"]}), + "hostcall", + "", + ); if let Some(obj) = crate::jit::rt::handles::get(handle) { if let Some(arr) = obj.as_any().downcast_ref::() { - if let Ok(items) = arr.items.read() { return if items.is_empty() { 1 } else { 0 }; } + if let Ok(items) = arr.items.read() { + return if items.is_empty() { 1 } else { 0 }; + } } if let Some(sb) = obj.as_any().downcast_ref::() { return if sb.value.is_empty() { 1 } else { 0 }; } if let Some(map) = obj.as_any().downcast_ref::() { - if let Some(ib) = map.size().as_any().downcast_ref::() { return if ib.value == 0 { 1 } else { 0 }; } + if let Some(ib) = map + .size() + .as_any() + .downcast_ref::() + { + return if ib.value == 0 { 1 } else { 0 }; + } } } 0 @@ -403,11 +615,21 @@ pub(super) extern "C" fn nyash_any_is_empty_h(handle: u64) -> i64 { // ---- By-name plugin invoke (generic receiver; resolves method_id at runtime) ---- #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_plugin_invoke_name_getattr_i64(argc: i64, a0: i64, a1: i64, a2: i64) -> i64 { +pub(super) extern "C" fn nyash_plugin_invoke_name_getattr_i64( + argc: i64, + a0: i64, + a1: i64, + a2: i64, +) -> i64 { nyash_plugin_invoke_name_common_i64("getattr", argc, a0, a1, a2) } #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_plugin_invoke_name_call_i64(argc: i64, a0: i64, a1: i64, a2: i64) -> i64 { +pub(super) extern "C" fn nyash_plugin_invoke_name_call_i64( + argc: i64, + a0: i64, + a1: i64, + a2: i64, +) -> i64 { nyash_plugin_invoke_name_common_i64("call", argc, a0, a1, a2) } #[cfg(feature = "cranelift-jit")] @@ -416,21 +638,28 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64 let mut instance_id: u32 = 0; let mut type_id: u32 = 0; let mut box_type: Option = None; - let mut invoke: Optioni32> = None; + let mut invoke: Option< + unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32, + > = None; if a0 > 0 { if let Some(obj) = crate::jit::rt::handles::get(a0 as u64) { if let Some(p) = obj.as_any().downcast_ref::() { - instance_id = p.instance_id(); type_id = p.inner.type_id; box_type = Some(p.box_type.clone()); + instance_id = p.instance_id(); + type_id = p.inner.type_id; + box_type = Some(p.box_type.clone()); invoke = Some(p.inner.invoke_fn); } } } - if invoke.is_none() && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") { + if invoke.is_none() && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") + { crate::jit::rt::with_legacy_vm_args(|args| { let idx = a0.max(0) as usize; if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) { if let Some(p) = b.as_any().downcast_ref::() { - instance_id = p.instance_id(); type_id = p.inner.type_id; box_type = Some(p.box_type.clone()); + instance_id = p.instance_id(); + type_id = p.inner.type_id; + box_type = Some(p.box_type.clone()); invoke = Some(p.inner.invoke_fn); } } @@ -441,20 +670,50 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64 for v in args.iter() { if let crate::backend::vm::VMValue::BoxRef(b) = v { if let Some(p) = b.as_any().downcast_ref::() { - instance_id = p.instance_id(); type_id = p.inner.type_id; box_type = Some(p.box_type.clone()); - invoke = Some(p.inner.invoke_fn); break; + instance_id = p.instance_id(); + type_id = p.inner.type_id; + box_type = Some(p.box_type.clone()); + invoke = Some(p.inner.invoke_fn); + break; } } } }); } - if invoke.is_none() { events::emit_runtime(serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "error": "no_invoke"}), "hostcall", ""); return 0; } + if invoke.is_none() { + events::emit_runtime( + serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "error": "no_invoke"}), + "hostcall", + "", + ); + return 0; + } let box_type = box_type.unwrap_or_default(); // Resolve method_id via PluginHost - let mh = if let Ok(host) = plugin_loader_unified::get_global_plugin_host().read() { host.resolve_method(&box_type, method) } else { events::emit_runtime(serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "error": "host_read_failed"}), "hostcall", ""); return 0 }; - let method_id = match mh { Ok(h) => h.method_id, Err(_) => { events::emit_runtime(serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "error": "resolve_failed"}), "hostcall", ""); return 0 } } as u32; + let mh = if let Ok(host) = plugin_loader_unified::get_global_plugin_host().read() { + host.resolve_method(&box_type, method) + } else { + events::emit_runtime( + serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "error": "host_read_failed"}), + "hostcall", + "", + ); + return 0; + }; + let method_id = match mh { + Ok(h) => h.method_id, + Err(_) => { + events::emit_runtime( + serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "error": "resolve_failed"}), + "hostcall", + "", + ); + return 0; + } + } as u32; // Build TLV args from a1/a2 preferring handles; fallback to legacy (skip receiver=pos0) - let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header(argc.saturating_sub(1).max(0) as u16); + let mut buf = + crate::runtime::plugin_ffi_common::encode_tlv_header(argc.saturating_sub(1).max(0) as u16); let mut encode_arg = |val: i64, pos: usize| { let mut appended = false; if val > 0 { @@ -463,20 +722,49 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64 let host = crate::runtime::get_global_plugin_host(); if let Ok(hg) = host.read() { if p.box_type == "StringBox" { - if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) { - if let Some(s) = sb.as_any().downcast_ref::() { crate::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value); appended = true; } + if let Ok(Some(sb)) = hg.invoke_instance_method( + "StringBox", + "toUtf8", + p.instance_id(), + &[], + ) { + if let Some(s) = + sb.as_any().downcast_ref::() + { + crate::runtime::plugin_ffi_common::encode::string( + &mut buf, &s.value, + ); + appended = true; + } } } else if p.box_type == "IntegerBox" { - if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) { - if let Some(i) = ibx.as_any().downcast_ref::() { crate::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value); appended = true; } + if let Ok(Some(ibx)) = + hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) + { + if let Some(i) = + ibx.as_any().downcast_ref::() + { + crate::runtime::plugin_ffi_common::encode::i64( + &mut buf, i.value, + ); + appended = true; + } } } } - if !appended { crate::runtime::plugin_ffi_common::encode::plugin_handle(&mut buf, p.inner.type_id, p.instance_id()); appended = true; } + if !appended { + crate::runtime::plugin_ffi_common::encode::plugin_handle( + &mut buf, + p.inner.type_id, + p.instance_id(), + ); + appended = true; + } } else { // HostHandle for user/builtin boxes let h = crate::runtime::host_handles::to_handle_arc(obj); - crate::runtime::plugin_ffi_common::encode::host_handle(&mut buf, h); appended = true; + crate::runtime::plugin_ffi_common::encode::host_handle(&mut buf, h); + appended = true; } } } @@ -486,8 +774,12 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64 if let Some(v) = args.get(pos) { use crate::backend::vm::VMValue as V; match v { - V::String(s) => crate::runtime::plugin_ffi_common::encode::string(&mut buf, s), - V::Integer(i) => crate::runtime::plugin_ffi_common::encode::i64(&mut buf, *i), + V::String(s) => { + crate::runtime::plugin_ffi_common::encode::string(&mut buf, s) + } + V::Integer(i) => { + crate::runtime::plugin_ffi_common::encode::i64(&mut buf, *i) + } V::Float(f) => crate::runtime::plugin_ffi_common::encode::f64(&mut buf, *f), V::Bool(b) => crate::runtime::plugin_ffi_common::encode::bool(&mut buf, *b), V::BoxRef(b) => { @@ -495,16 +787,46 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64 let host = crate::runtime::get_global_plugin_host(); if let Ok(hg) = host.read() { if p.box_type == "StringBox" { - if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) { - if let Some(s) = sb.as_any().downcast_ref::() { crate::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value); return; } + if let Ok(Some(sb)) = hg.invoke_instance_method( + "StringBox", + "toUtf8", + p.instance_id(), + &[], + ) { + if let Some(s) = sb + .as_any() + .downcast_ref::() + { + crate::runtime::plugin_ffi_common::encode::string( + &mut buf, &s.value, + ); + return; + } } } else if p.box_type == "IntegerBox" { - if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) { - if let Some(i) = ibx.as_any().downcast_ref::() { crate::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value); return; } + if let Ok(Some(ibx)) = hg.invoke_instance_method( + "IntegerBox", + "get", + p.instance_id(), + &[], + ) { + if let Some(i) = + ibx.as_any() + .downcast_ref::() + { + crate::runtime::plugin_ffi_common::encode::i64( + &mut buf, i.value, + ); + return; + } } } } - crate::runtime::plugin_ffi_common::encode::plugin_handle(&mut buf, p.inner.type_id, p.instance_id()); + crate::runtime::plugin_ffi_common::encode::plugin_handle( + &mut buf, + p.inner.type_id, + p.instance_id(), + ); } else { // HostHandle fallback let h = crate::runtime::host_handles::to_handle_arc(b.clone()); @@ -520,34 +842,100 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64 }); } }; - if argc >= 2 { encode_arg(a1, 1); } - if argc >= 3 { encode_arg(a2, 2); } - let mut out = vec![0u8; 4096]; let mut out_len: usize = out.len(); - let rc = unsafe { invoke.unwrap()(type_id as u32, method_id, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) }; - if rc != 0 { events::emit_runtime(serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "error": "invoke_failed"}), "hostcall", ""); return 0; } + if argc >= 2 { + encode_arg(a1, 1); + } + if argc >= 3 { + encode_arg(a2, 2); + } + let mut out = vec![0u8; 4096]; + let mut out_len: usize = out.len(); + let rc = unsafe { + invoke.unwrap()( + type_id as u32, + method_id, + instance_id, + buf.as_ptr(), + buf.len(), + out.as_mut_ptr(), + &mut out_len, + ) + }; + if rc != 0 { + events::emit_runtime( + serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "error": "invoke_failed"}), + "hostcall", + "", + ); + return 0; + } let out_slice = &out[..out_len]; - if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) { + if let Some((tag, _sz, payload)) = + crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) + { match tag { - 3 => { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } } - 1 => { return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; } - 5 => { if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref()==Some("1") { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); let f=f64::from_le_bytes(b); return f as i64; } } } - _ => { events::emit_runtime(serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "warn": "first_tlv_not_primitive_or_handle", "tag": tag}), "hostcall", ""); } + 3 => { + if payload.len() == 8 { + let mut b = [0u8; 8]; + b.copy_from_slice(payload); + return i64::from_le_bytes(b); + } + } + 1 => { + return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) + { + 1 + } else { + 0 + }; + } + 5 => { + if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") { + if payload.len() == 8 { + let mut b = [0u8; 8]; + b.copy_from_slice(payload); + let f = f64::from_le_bytes(b); + return f as i64; + } + } + } + _ => { + events::emit_runtime( + serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "warn": "first_tlv_not_primitive_or_handle", "tag": tag}), + "hostcall", + "", + ); + } } } - events::emit_runtime(serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "error": "decode_failed"}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "error": "decode_failed"}), + "hostcall", + "", + ); 0 } // ---- String ---- #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_string_charcode_at_h(handle: u64, idx: i64) -> i64 { - events::emit_runtime(serde_json::json!({"id": c::SYM_STRING_CHARCODE_AT_H, "decision":"allow", "argc":2, "arg_types":["Handle","I64"]}), "hostcall", ""); - if idx < 0 { return -1; } + events::emit_runtime( + serde_json::json!({"id": c::SYM_STRING_CHARCODE_AT_H, "decision":"allow", "argc":2, "arg_types":["Handle","I64"]}), + "hostcall", + "", + ); + if idx < 0 { + return -1; + } if let Some(obj) = crate::jit::rt::handles::get(handle) { if let Some(sb) = obj.as_any().downcast_ref::() { let s = &sb.value; let i = idx as usize; - if i < s.len() { return s.as_bytes()[i] as i64; } else { return -1; } + if i < s.len() { + return s.as_bytes()[i] as i64; + } else { + return -1; + } } } -1 @@ -556,15 +944,25 @@ pub(super) extern "C" fn nyash_string_charcode_at_h(handle: u64, idx: i64) -> i6 // String.len_h(handle) -> i64 with param-index fallback (JIT bridge) #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_string_len_h(handle: u64) -> i64 { - events::emit_runtime(serde_json::json!({"id": c::SYM_STRING_LEN_H, "decision":"allow", "argc":1, "arg_types":["Handle"]}), "hostcall", ""); - if std::env::var("NYASH_JIT_TRACE_LEN").ok().as_deref() == Some("1") { eprintln!("[JIT-LEN_H] handle={}", handle); } + events::emit_runtime( + serde_json::json!({"id": c::SYM_STRING_LEN_H, "decision":"allow", "argc":1, "arg_types":["Handle"]}), + "hostcall", + "", + ); + if std::env::var("NYASH_JIT_TRACE_LEN").ok().as_deref() == Some("1") { + eprintln!("[JIT-LEN_H] handle={}", handle); + } if handle > 0 { if let Some(obj) = crate::jit::rt::handles::get(handle) { - if let Some(sb) = obj.as_any().downcast_ref::() { return sb.value.len() as i64; } + if let Some(sb) = obj.as_any().downcast_ref::() { + return sb.value.len() as i64; + } } // Fallback to any.length_h for non-string handles let v = nyash_any_length_h(handle); - if std::env::var("NYASH_JIT_TRACE_LEN").ok().as_deref() == Some("1") { eprintln!("[JIT-LEN_H] any.length_h(handle={}) -> {}", handle, v); } + if std::env::var("NYASH_JIT_TRACE_LEN").ok().as_deref() == Some("1") { + eprintln!("[JIT-LEN_H] any.length_h(handle={}) -> {}", handle, v); + } return v; } // Legacy param index fallback (0..16): disabled in jit-direct-only @@ -576,9 +974,13 @@ pub(super) extern "C" fn nyash_string_len_h(handle: u64) -> i64 { if let Some(v) = val { match v { crate::backend::vm::VMValue::BoxRef(b) => { - if let Some(sb) = b.as_any().downcast_ref::() { return sb.value.len() as i64; } + if let Some(sb) = b.as_any().downcast_ref::() { + return sb.value.len() as i64; + } + } + crate::backend::vm::VMValue::String(s) => { + return s.len() as i64; } - crate::backend::vm::VMValue::String(s) => { return s.len() as i64; } _ => {} } } @@ -624,8 +1026,16 @@ fn handle_to_string_like(handle: u64) -> Option { if let Some(pb) = obj.as_any().downcast_ref::() { if pb.box_type == "StringBox" { if let Ok(host) = crate::runtime::get_global_plugin_host().read() { - if let Ok(val_opt) = host.invoke_instance_method("StringBox", "toUtf8", pb.instance_id(), &[]) { - if let Some(vb) = val_opt { if let Some(sbb) = vb.as_any().downcast_ref::() { return Some(sbb.value.clone()); } } + if let Ok(val_opt) = + host.invoke_instance_method("StringBox", "toUtf8", pb.instance_id(), &[]) + { + if let Some(vb) = val_opt { + if let Some(sbb) = + vb.as_any().downcast_ref::() + { + return Some(sbb.value.clone()); + } + } } } } @@ -654,56 +1064,95 @@ fn handle_to_string_like(handle: u64) -> Option { #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_string_concat_hh(a_h: u64, b_h: u64) -> i64 { - events::emit_runtime(serde_json::json!({"id": c::SYM_STRING_CONCAT_HH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": c::SYM_STRING_CONCAT_HH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}), + "hostcall", + "", + ); let a = handle_to_string_like(a_h).unwrap_or_default(); let b = handle_to_string_like(b_h).unwrap_or_default(); let s = format!("{}{}", a, b); - let arc: std::sync::Arc = std::sync::Arc::new(crate::box_trait::StringBox::new(s)); + let arc: std::sync::Arc = + std::sync::Arc::new(crate::box_trait::StringBox::new(s)); let h = crate::jit::rt::handles::to_handle(arc); h as i64 } #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_string_eq_hh(a_h: u64, b_h: u64) -> i64 { - events::emit_runtime(serde_json::json!({"id": c::SYM_STRING_EQ_HH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": c::SYM_STRING_EQ_HH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}), + "hostcall", + "", + ); let a = handle_to_string_like(a_h).unwrap_or_default(); let b = handle_to_string_like(b_h).unwrap_or_default(); - if a == b { 1 } else { 0 } + if a == b { + 1 + } else { + 0 + } } #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_string_lt_hh(a_h: u64, b_h: u64) -> i64 { - events::emit_runtime(serde_json::json!({"id": c::SYM_STRING_LT_HH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}), "hostcall", ""); + events::emit_runtime( + serde_json::json!({"id": c::SYM_STRING_LT_HH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}), + "hostcall", + "", + ); let a = handle_to_string_like(a_h).unwrap_or_default(); let b = handle_to_string_like(b_h).unwrap_or_default(); - if a < b { 1 } else { 0 } + if a < b { + 1 + } else { + 0 + } } // Unified semantics: addition for dynamic boxes via shared coercions #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_semantics_add_hh(lhs_h: u64, rhs_h: u64) -> i64 { - events::emit_runtime(serde_json::json!({"id": crate::jit::r#extern::collections::SYM_SEMANTICS_ADD_HH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}), "hostcall", ""); - use crate::runtime::semantics; - use crate::box_trait::{StringBox, IntegerBox}; + events::emit_runtime( + serde_json::json!({"id": crate::jit::r#extern::collections::SYM_SEMANTICS_ADD_HH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}), + "hostcall", + "", + ); + use crate::box_trait::{IntegerBox, StringBox}; use crate::jit::rt::handles; - let lhs = if let Some(o) = handles::get(lhs_h) { o } else { return 0 }; - let rhs = if let Some(o) = handles::get(rhs_h) { o } else { return 0 }; + use crate::runtime::semantics; + let lhs = if let Some(o) = handles::get(lhs_h) { + o + } else { + return 0; + }; + let rhs = if let Some(o) = handles::get(rhs_h) { + o + } else { + return 0; + }; let ls_opt = semantics::coerce_to_string(lhs.as_ref()); let rs_opt = semantics::coerce_to_string(rhs.as_ref()); if ls_opt.is_some() || rs_opt.is_some() { let ls = ls_opt.unwrap_or_else(|| lhs.to_string_box().value); let rs = rs_opt.unwrap_or_else(|| rhs.to_string_box().value); let s = format!("{}{}", ls, rs); - let arc: std::sync::Arc = std::sync::Arc::new(StringBox::new(s)); + let arc: std::sync::Arc = + std::sync::Arc::new(StringBox::new(s)); return handles::to_handle(arc) as i64; } - if let (Some(li), Some(ri)) = (semantics::coerce_to_i64(lhs.as_ref()), semantics::coerce_to_i64(rhs.as_ref())) { - let arc: std::sync::Arc = std::sync::Arc::new(IntegerBox::new(li + ri)); + if let (Some(li), Some(ri)) = ( + semantics::coerce_to_i64(lhs.as_ref()), + semantics::coerce_to_i64(rhs.as_ref()), + ) { + let arc: std::sync::Arc = + std::sync::Arc::new(IntegerBox::new(li + ri)); return handles::to_handle(arc) as i64; } // Fallback stringify concat let s = format!("{}{}", lhs.to_string_box().value, rhs.to_string_box().value); - let arc: std::sync::Arc = std::sync::Arc::new(StringBox::new(s)); + let arc: std::sync::Arc = + std::sync::Arc::new(StringBox::new(s)); handles::to_handle(arc) as i64 } @@ -714,13 +1163,16 @@ pub(super) extern "C" fn nyash_semantics_add_hh(lhs_h: u64, rhs_h: u64) -> i64 { #[cfg(feature = "cranelift-jit")] fn vmvalue_from_jit_arg_i64(v: i64) -> crate::backend::vm::VMValue { use crate::backend::vm::VMValue as V; - if v <= 0 { return V::Integer(v); } + if v <= 0 { + return V::Integer(v); + } if let Some(obj) = crate::jit::rt::handles::get(v as u64) { return V::BoxRef(obj); } // Legacy fallback: allow small indices to refer into legacy VM args for string/name lookups if (v as u64) <= 16 { - return crate::jit::rt::with_legacy_vm_args(|args| args.get(v as usize).cloned()).unwrap_or(V::Integer(v)); + return crate::jit::rt::with_legacy_vm_args(|args| args.get(v as usize).cloned()) + .unwrap_or(V::Integer(v)); } V::Integer(v) } @@ -730,10 +1182,17 @@ fn i64_from_vmvalue(v: crate::backend::vm::VMValue) -> i64 { use crate::backend::vm::VMValue as V; match v { V::Integer(i) => i, - V::Bool(b) => if b { 1 } else { 0 }, + V::Bool(b) => { + if b { + 1 + } else { + 0 + } + } V::Float(f) => f as i64, V::String(s) => { - let arc: std::sync::Arc = std::sync::Arc::new(crate::box_trait::StringBox::new(&s)); + let arc: std::sync::Arc = + std::sync::Arc::new(crate::box_trait::StringBox::new(&s)); crate::jit::rt::handles::to_handle(arc) as i64 } V::BoxRef(b) => crate::jit::rt::handles::to_handle(b) as i64, @@ -749,10 +1208,33 @@ fn i64_from_vmvalue(v: crate::backend::vm::VMValue) -> i64 { #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_host_instance_getfield(recv_h: u64, name_i: i64) -> i64 { use crate::backend::vm::VMValue as V; - let recv = match crate::jit::rt::handles::get(recv_h) { Some(a) => a, None => return 0 }; + let recv = match crate::jit::rt::handles::get(recv_h) { + Some(a) => a, + None => return 0, + }; let name_v = vmvalue_from_jit_arg_i64(name_i); if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") { - eprintln!("[HB|getField] name_i={} kind={}", name_i, match &name_v { V::String(_) => "String", V::BoxRef(b) => if b.as_any().downcast_ref::().is_some() {"StringBox"} else {"BoxRef"}, V::Integer(_) => "Integer", V::Bool(_) => "Bool", V::Float(_) => "Float", V::Void => "Void", V::Future(_) => "Future" }); + eprintln!( + "[HB|getField] name_i={} kind={}", + name_i, + match &name_v { + V::String(_) => "String", + V::BoxRef(b) => + if b.as_any() + .downcast_ref::() + .is_some() + { + "StringBox" + } else { + "BoxRef" + }, + V::Integer(_) => "Integer", + V::Bool(_) => "Bool", + V::Float(_) => "Float", + V::Void => "Void", + V::Future(_) => "Future", + } + ); } let out = hb::instance_getfield(&[V::BoxRef(recv), name_v]); i64_from_vmvalue(out) @@ -762,7 +1244,10 @@ pub(super) extern "C" fn nyash_host_instance_getfield(recv_h: u64, name_i: i64) #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_host_instance_setfield(recv_h: u64, name_i: i64, val_i: i64) -> i64 { use crate::backend::vm::VMValue as V; - let recv = match crate::jit::rt::handles::get(recv_h) { Some(a) => a, None => return 0 }; + let recv = match crate::jit::rt::handles::get(recv_h) { + Some(a) => a, + None => return 0, + }; let name_v = vmvalue_from_jit_arg_i64(name_i); let val_v = vmvalue_from_jit_arg_i64(val_i); let out = hb::instance_setfield(&[V::BoxRef(recv), name_v, val_v]); @@ -773,9 +1258,13 @@ pub(super) extern "C" fn nyash_host_instance_setfield(recv_h: u64, name_i: i64, #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_host_instance_field3(recv_h: u64, name_i: i64, val_i: i64) -> i64 { use crate::backend::vm::VMValue as V; - let recv = match crate::jit::rt::handles::get(recv_h) { Some(a) => a, None => return 0 }; + let recv = match crate::jit::rt::handles::get(recv_h) { + Some(a) => a, + None => return 0, + }; let name_v = vmvalue_from_jit_arg_i64(name_i); - if val_i == -1 { // getField + if val_i == -1 { + // getField let out = hb::instance_getfield(&[V::BoxRef(recv), name_v]); return i64_from_vmvalue(out); } @@ -789,11 +1278,23 @@ pub(super) extern "C" fn nyash_host_instance_field3(recv_h: u64, name_i: i64, va #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_host_string_len(recv_h: u64) -> i64 { use crate::backend::vm::VMValue as V; - if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") { eprintln!("[HB|string.len] recv_h={}", recv_h); } - let recv = match crate::jit::rt::handles::get(recv_h) { Some(a) => a, None => { if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref()==Some("1") { eprintln!("[HB|string.len] recv handle not found"); } return 0 } }; + if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") { + eprintln!("[HB|string.len] recv_h={}", recv_h); + } + let recv = match crate::jit::rt::handles::get(recv_h) { + Some(a) => a, + None => { + if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") { + eprintln!("[HB|string.len] recv handle not found"); + } + return 0; + } + }; let out = hb::string_len(&[V::BoxRef(recv)]); let ret = i64_from_vmvalue(out); - if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") { eprintln!("[HB|string.len] ret_i64={}", ret); } + if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") { + eprintln!("[HB|string.len] ret_i64={}", ret); + } ret } @@ -827,11 +1328,17 @@ pub(super) extern "C" fn nyash_host_console_error_i64(val_i: i64) -> i64 { // Build a StringBox handle from raw bytes pointer and length #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_string_from_ptr(ptr: u64, len: u64) -> i64 { - if ptr == 0 || len == 0 { return 0; } + if ptr == 0 || len == 0 { + return 0; + } unsafe { let slice = std::slice::from_raw_parts(ptr as *const u8, len as usize); - let s = match std::str::from_utf8(slice) { Ok(t) => t.to_string(), Err(_) => String::from_utf8_lossy(slice).to_string() }; - let arc: std::sync::Arc = std::sync::Arc::new(crate::box_trait::StringBox::new(s)); + let s = match std::str::from_utf8(slice) { + Ok(t) => t.to_string(), + Err(_) => String::from_utf8_lossy(slice).to_string(), + }; + let arc: std::sync::Arc = + std::sync::Arc::new(crate::box_trait::StringBox::new(s)); return crate::jit::rt::handles::to_handle(arc) as i64; } } @@ -841,8 +1348,14 @@ pub(super) extern "C" fn nyash_string_from_ptr(ptr: u64, len: u64) -> i64 { #[cfg(feature = "cranelift-jit")] fn fn_call_impl(func_h: u64, args: &[i64]) -> i64 { use crate::box_trait::NyashBox; - let f_arc = match crate::jit::rt::handles::get(func_h) { Some(a) => a, None => return 0 }; - if let Some(fun) = f_arc.as_any().downcast_ref::() { + let f_arc = match crate::jit::rt::handles::get(func_h) { + Some(a) => a, + None => return 0, + }; + if let Some(fun) = f_arc + .as_any() + .downcast_ref::() + { let mut ny_args: Vec> = Vec::new(); for &ai in args { let v = vmvalue_from_jit_arg_i64(ai); @@ -855,43 +1368,119 @@ fn fn_call_impl(func_h: u64, args: &[i64]) -> i64 { } Err(_) => 0, } - } else { 0 } + } else { + 0 + } } #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_fn_call0(func_h: u64) -> i64 { fn_call_impl(func_h, &[]) } +pub(super) extern "C" fn nyash_fn_call0(func_h: u64) -> i64 { + fn_call_impl(func_h, &[]) +} #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_fn_call1(func_h: u64, a0: i64) -> i64 { fn_call_impl(func_h, &[a0]) } +pub(super) extern "C" fn nyash_fn_call1(func_h: u64, a0: i64) -> i64 { + fn_call_impl(func_h, &[a0]) +} #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_fn_call2(func_h: u64, a0: i64, a1: i64) -> i64 { fn_call_impl(func_h, &[a0,a1]) } +pub(super) extern "C" fn nyash_fn_call2(func_h: u64, a0: i64, a1: i64) -> i64 { + fn_call_impl(func_h, &[a0, a1]) +} #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_fn_call3(func_h: u64, a0: i64, a1: i64, a2: i64) -> i64 { fn_call_impl(func_h, &[a0,a1,a2]) } +pub(super) extern "C" fn nyash_fn_call3(func_h: u64, a0: i64, a1: i64, a2: i64) -> i64 { + fn_call_impl(func_h, &[a0, a1, a2]) +} #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_fn_call4(func_h: u64, a0: i64, a1: i64, a2: i64, a3: i64) -> i64 { fn_call_impl(func_h, &[a0,a1,a2,a3]) } +pub(super) extern "C" fn nyash_fn_call4(func_h: u64, a0: i64, a1: i64, a2: i64, a3: i64) -> i64 { + fn_call_impl(func_h, &[a0, a1, a2, a3]) +} // extended arities (5..8) #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_fn_call5(func_h: u64, a0: i64, a1: i64, a2: i64, a3: i64, a4: i64) -> i64 { fn_call_impl(func_h, &[a0,a1,a2,a3,a4]) } +pub(super) extern "C" fn nyash_fn_call5( + func_h: u64, + a0: i64, + a1: i64, + a2: i64, + a3: i64, + a4: i64, +) -> i64 { + fn_call_impl(func_h, &[a0, a1, a2, a3, a4]) +} #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_fn_call6(func_h: u64, a0: i64, a1: i64, a2: i64, a3: i64, a4: i64, a5: i64) -> i64 { fn_call_impl(func_h, &[a0,a1,a2,a3,a4,a5]) } +pub(super) extern "C" fn nyash_fn_call6( + func_h: u64, + a0: i64, + a1: i64, + a2: i64, + a3: i64, + a4: i64, + a5: i64, +) -> i64 { + fn_call_impl(func_h, &[a0, a1, a2, a3, a4, a5]) +} #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_fn_call7(func_h: u64, a0: i64, a1: i64, a2: i64, a3: i64, a4: i64, a5: i64, a6: i64) -> i64 { fn_call_impl(func_h, &[a0,a1,a2,a3,a4,a5,a6]) } +pub(super) extern "C" fn nyash_fn_call7( + func_h: u64, + a0: i64, + a1: i64, + a2: i64, + a3: i64, + a4: i64, + a5: i64, + a6: i64, +) -> i64 { + fn_call_impl(func_h, &[a0, a1, a2, a3, a4, a5, a6]) +} #[cfg(feature = "cranelift-jit")] -pub(super) extern "C" fn nyash_fn_call8(func_h: u64, a0: i64, a1: i64, a2: i64, a3: i64, a4: i64, a5: i64, a6: i64, a7: i64) -> i64 { fn_call_impl(func_h, &[a0,a1,a2,a3,a4,a5,a6,a7]) } +pub(super) extern "C" fn nyash_fn_call8( + func_h: u64, + a0: i64, + a1: i64, + a2: i64, + a3: i64, + a4: i64, + a5: i64, + a6: i64, + a7: i64, +) -> i64 { + fn_call_impl(func_h, &[a0, a1, a2, a3, a4, a5, a6, a7]) +} // Build a StringBox handle from two u64 chunks (little-endian) and length (<=16) #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_string_from_u64x2(lo: u64, hi: u64, len: i64) -> i64 { - let n = if len <= 0 { 0usize } else { core::cmp::min(len as usize, 16usize) }; + let n = if len <= 0 { + 0usize + } else { + core::cmp::min(len as usize, 16usize) + }; let mut buf = [0u8; 16]; - for i in 0..core::cmp::min(8, n) { buf[i] = ((lo >> (8 * i)) & 0xFF) as u8; } - if n > 8 { for i in 0..(n - 8) { buf[8 + i] = ((hi >> (8 * i)) & 0xFF) as u8; } } - let s = match std::str::from_utf8(&buf[..n]) { Ok(t) => t.to_string(), Err(_) => String::from_utf8_lossy(&buf[..n]).to_string() }; - let arc: std::sync::Arc = std::sync::Arc::new(crate::box_trait::StringBox::new(s)); + for i in 0..core::cmp::min(8, n) { + buf[i] = ((lo >> (8 * i)) & 0xFF) as u8; + } + if n > 8 { + for i in 0..(n - 8) { + buf[8 + i] = ((hi >> (8 * i)) & 0xFF) as u8; + } + } + let s = match std::str::from_utf8(&buf[..n]) { + Ok(t) => t.to_string(), + Err(_) => String::from_utf8_lossy(&buf[..n]).to_string(), + }; + let arc: std::sync::Arc = + std::sync::Arc::new(crate::box_trait::StringBox::new(s)); let h = crate::jit::rt::handles::to_handle(arc.clone()) as i64; if std::env::var("NYASH_JIT_TRACE_LEN").ok().as_deref() == Some("1") { - if let Some(sb) = arc.as_any().downcast_ref::() { eprintln!("[JIT-STR_H] new handle={} val='{}' len={}", h, sb.value, sb.value.len()); } - else { eprintln!("[JIT-STR_H] new handle={} (non-StringBox)", h); } + if let Some(sb) = arc.as_any().downcast_ref::() { + eprintln!( + "[JIT-STR_H] new handle={} val='{}' len={}", + h, + sb.value, + sb.value.len() + ); + } else { + eprintln!("[JIT-STR_H] new handle={} (non-StringBox)", h); + } } h } @@ -899,11 +1488,24 @@ pub(super) extern "C" fn nyash_string_from_u64x2(lo: u64, hi: u64, len: i64) -> // Create an instance by type name via global unified registry: birth(name) -> handle #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_instance_birth_name_u64x2(lo: u64, hi: u64, len: i64) -> i64 { - let n = if len <= 0 { 0usize } else { core::cmp::min(len as usize, 32usize) }; + let n = if len <= 0 { + 0usize + } else { + core::cmp::min(len as usize, 32usize) + }; let mut buf = [0u8; 32]; - for i in 0..core::cmp::min(8, n) { buf[i] = ((lo >> (8 * i)) & 0xFF) as u8; } - if n > 8 { for i in 0..core::cmp::min(8, n - 8) { buf[8 + i] = ((hi >> (8 * i)) & 0xFF) as u8; } } - let name = match std::str::from_utf8(&buf[..n]) { Ok(t) => t.to_string(), Err(_) => String::from_utf8_lossy(&buf[..n]).to_string() }; + for i in 0..core::cmp::min(8, n) { + buf[i] = ((lo >> (8 * i)) & 0xFF) as u8; + } + if n > 8 { + for i in 0..core::cmp::min(8, n - 8) { + buf[8 + i] = ((hi >> (8 * i)) & 0xFF) as u8; + } + } + let name = match std::str::from_utf8(&buf[..n]) { + Ok(t) => t.to_string(), + Err(_) => String::from_utf8_lossy(&buf[..n]).to_string(), + }; let registry = crate::runtime::get_global_unified_registry(); if let Ok(reg) = registry.lock() { match reg.create_box(&name, &[]) { diff --git a/src/jit/lower/mod.rs b/src/jit/lower/mod.rs index 2f71ab8f..5f1425ca 100644 --- a/src/jit/lower/mod.rs +++ b/src/jit/lower/mod.rs @@ -1,7 +1,7 @@ //! Lowering entry for JIT -pub mod core; pub mod builder; -pub mod extern_thunks; pub mod cfg_dot; +pub mod core; pub mod core_hostcall; pub mod core_ops; +pub mod extern_thunks; diff --git a/src/jit/manager.rs b/src/jit/manager.rs index af8d1cf8..3312f653 100644 --- a/src/jit/manager.rs +++ b/src/jit/manager.rs @@ -3,25 +3,65 @@ pub struct JitManager; #[cfg(feature = "jit-direct-only")] impl JitManager { - pub fn new(_threshold: u32) -> Self { Self } + pub fn new(_threshold: u32) -> Self { + Self + } pub fn set_threshold(&mut self, _t: u32) {} pub fn record_entry(&mut self, _func: &str) {} - pub fn should_jit(&self, _func: &str) -> bool { false } + pub fn should_jit(&self, _func: &str) -> bool { + false + } pub fn mark_compiled(&mut self, _func: &str, _handle: u64) {} - pub fn maybe_compile(&mut self, _func: &str, _mir: &crate::mir::MirFunction) -> bool { false } - pub fn is_compiled(&self, _func: &str) -> bool { false } - pub fn handle_of(&self, _func: &str) -> Option { None } - pub fn sites(&self) -> usize { 0 } - pub fn compiled_count(&self) -> usize { 0 } - pub fn total_hits(&self) -> u64 { 0 } - pub fn exec_ok_count(&self) -> u64 { 0 } - pub fn exec_trap_count(&self) -> u64 { 0 } - pub fn record_lower_stats(&mut self, _func: &str, _phi_total: u64, _phi_b1: u64, _ret_bool_hint: bool) {} - pub fn per_function_stats(&self) -> Vec<(String, u64, u64, u64, u32, bool, u64)> { Vec::new() } - pub fn top_hits(&self, _n: usize) -> Vec<(String, u32, bool, u64)> { Vec::new() } + pub fn maybe_compile(&mut self, _func: &str, _mir: &crate::mir::MirFunction) -> bool { + false + } + pub fn is_compiled(&self, _func: &str) -> bool { + false + } + pub fn handle_of(&self, _func: &str) -> Option { + None + } + pub fn sites(&self) -> usize { + 0 + } + pub fn compiled_count(&self) -> usize { + 0 + } + pub fn total_hits(&self) -> u64 { + 0 + } + pub fn exec_ok_count(&self) -> u64 { + 0 + } + pub fn exec_trap_count(&self) -> u64 { + 0 + } + pub fn record_lower_stats( + &mut self, + _func: &str, + _phi_total: u64, + _phi_b1: u64, + _ret_bool_hint: bool, + ) { + } + pub fn per_function_stats(&self) -> Vec<(String, u64, u64, u64, u32, bool, u64)> { + Vec::new() + } + pub fn top_hits(&self, _n: usize) -> Vec<(String, u32, bool, u64)> { + Vec::new() + } pub fn print_summary(&self) {} - pub fn maybe_dispatch(&mut self, _func: &str, _argc: usize) -> bool { false } - pub fn execute_compiled(&mut self, _func: &str, _ret_ty: &crate::mir::MirType, _args: &[crate::backend::vm::VMValue]) -> Option { None } + pub fn maybe_dispatch(&mut self, _func: &str, _argc: usize) -> bool { + false + } + pub fn execute_compiled( + &mut self, + _func: &str, + _ret_ty: &crate::mir::MirType, + _args: &[crate::backend::vm::VMValue], + ) -> Option { + None + } } #[cfg(not(feature = "jit-direct-only"))] @@ -48,10 +88,22 @@ pub struct JitManager { #[cfg(not(feature = "jit-direct-only"))] impl JitManager { pub fn new(threshold: u32) -> Self { - Self { threshold, hits: HashMap::new(), compiled: HashMap::new(), engine: crate::jit::engine::JitEngine::new(), exec_ok: 0, exec_trap: 0, func_phi_total: HashMap::new(), func_phi_b1: HashMap::new(), func_ret_bool_hint: HashMap::new() } + Self { + threshold, + hits: HashMap::new(), + compiled: HashMap::new(), + engine: crate::jit::engine::JitEngine::new(), + exec_ok: 0, + exec_trap: 0, + func_phi_total: HashMap::new(), + func_phi_b1: HashMap::new(), + func_ret_bool_hint: HashMap::new(), + } } - pub fn set_threshold(&mut self, t: u32) { self.threshold = t.max(1); } + pub fn set_threshold(&mut self, t: u32) { + self.threshold = t.max(1); + } pub fn record_entry(&mut self, func: &str) { let c = self.hits.entry(func.to_string()).or_insert(0); @@ -84,21 +136,47 @@ impl JitManager { self.compiled.contains_key(func) } - pub fn is_compiled(&self, func: &str) -> bool { self.compiled.contains_key(func) } - pub fn handle_of(&self, func: &str) -> Option { self.compiled.get(func).copied() } + pub fn is_compiled(&self, func: &str) -> bool { + self.compiled.contains_key(func) + } + pub fn handle_of(&self, func: &str) -> Option { + self.compiled.get(func).copied() + } // --- Stats accessors for unified reporting --- - pub fn sites(&self) -> usize { self.hits.len() } - pub fn compiled_count(&self) -> usize { self.compiled.len() } - pub fn total_hits(&self) -> u64 { self.hits.values().map(|v| *v as u64).sum() } - pub fn exec_ok_count(&self) -> u64 { self.exec_ok } - pub fn exec_trap_count(&self) -> u64 { self.exec_trap } + pub fn sites(&self) -> usize { + self.hits.len() + } + pub fn compiled_count(&self) -> usize { + self.compiled.len() + } + pub fn total_hits(&self) -> u64 { + self.hits.values().map(|v| *v as u64).sum() + } + pub fn exec_ok_count(&self) -> u64 { + self.exec_ok + } + pub fn exec_trap_count(&self) -> u64 { + self.exec_trap + } // --- Per-function stats --- - pub fn record_lower_stats(&mut self, func: &str, phi_total: u64, phi_b1: u64, ret_bool_hint: bool) { - if phi_total > 0 { *self.func_phi_total.entry(func.to_string()).or_insert(0) += phi_total; } - if phi_b1 > 0 { *self.func_phi_b1.entry(func.to_string()).or_insert(0) += phi_b1; } - if ret_bool_hint { *self.func_ret_bool_hint.entry(func.to_string()).or_insert(0) += 1; } + pub fn record_lower_stats( + &mut self, + func: &str, + phi_total: u64, + phi_b1: u64, + ret_bool_hint: bool, + ) { + if phi_total > 0 { + *self.func_phi_total.entry(func.to_string()).or_insert(0) += phi_total; + } + if phi_b1 > 0 { + *self.func_phi_b1.entry(func.to_string()).or_insert(0) += phi_b1; + } + if ret_bool_hint { + *self.func_ret_bool_hint.entry(func.to_string()).or_insert(0) += 1; + } } pub fn per_function_stats(&self) -> Vec<(String, u64, u64, u64, u32, bool, u64)> { // name, phi_total, phi_b1, ret_bool_hint, hits, compiled, handle @@ -108,7 +186,7 @@ impl JitManager { names.extend(self.func_phi_b1.keys().cloned()); names.extend(self.func_ret_bool_hint.keys().cloned()); let mut out = Vec::new(); - for name in names { + for name in names { let phi_t = self.func_phi_total.get(&name).copied().unwrap_or(0); let phi_b1 = self.func_phi_b1.get(&name).copied().unwrap_or(0); let rb = self.func_ret_bool_hint.get(&name).copied().unwrap_or(0); @@ -135,18 +213,27 @@ impl JitManager { } pub fn print_summary(&self) { - if std::env::var("NYASH_JIT_STATS").ok().as_deref() != Some("1") { return; } + if std::env::var("NYASH_JIT_STATS").ok().as_deref() != Some("1") { + return; + } let sites = self.hits.len(); let total_hits: u64 = self.hits.values().map(|v| *v as u64).sum(); let compiled = self.compiled.len(); - eprintln!("[JIT] sites={} compiled={} hits_total={} exec_ok={} exec_trap={}", sites, compiled, total_hits, self.exec_ok, self.exec_trap); + eprintln!( + "[JIT] sites={} compiled={} hits_total={} exec_ok={} exec_trap={}", + sites, compiled, total_hits, self.exec_ok, self.exec_trap + ); // Top 5 hot functions let mut v: Vec<(&String, &u32)> = self.hits.iter().collect(); v.sort_by(|a, b| b.1.cmp(a.1)); for (i, (k, h)) in v.into_iter().take(5).enumerate() { - let comp = if self.compiled.contains_key(k) { "*" } else { " " }; + let comp = if self.compiled.contains_key(k) { + "*" + } else { + " " + }; let hdl = self.compiled.get(k).copied().unwrap_or(0); - eprintln!(" #{}{} {} hits={} handle={}", i+1, comp, k, h, hdl); + eprintln!(" #{}{} {} hits={} handle={}", i + 1, comp, k, h, hdl); } } @@ -154,7 +241,10 @@ impl JitManager { pub fn maybe_dispatch(&mut self, func: &str, argc: usize) -> bool { if std::env::var("NYASH_JIT_EXEC").ok().as_deref() == Some("1") { if let Some(h) = self.handle_of(func) { - eprintln!("[JIT] executing handle={} argc={} (stub) for {}", h, argc, func); + eprintln!( + "[JIT] executing handle={} argc={} (stub) for {}", + h, argc, func + ); // In 10_c proper, invoke engine with prepared args and return actual result // For now, execute with empty args to exercise the path, ignore result let _ = self.engine.execute_handle(h, &[]); @@ -165,7 +255,12 @@ impl JitManager { } /// 10_c: execute compiled function if present (stub: empty args). Returns Some(VMValue) if JIT path was taken. - pub fn execute_compiled(&mut self, func: &str, ret_ty: &crate::mir::MirType, args: &[crate::backend::vm::VMValue]) -> Option { + pub fn execute_compiled( + &mut self, + func: &str, + ret_ty: &crate::mir::MirType, + args: &[crate::backend::vm::VMValue], + ) -> Option { // Strict/Fail‑FastモードではJITは"コンパイル専用"(実行しない) if std::env::var("NYASH_JIT_STRICT").ok().as_deref() == Some("1") { // 観測のためイベントだけ出す @@ -174,7 +269,8 @@ impl JitManager { "id": "jit_skip_execute_strict", "func": func }), - "jit", func + "jit", + func, ); return None; } @@ -208,7 +304,8 @@ impl JitManager { "reason": "jit_execute_failed", "ms": dt.as_millis() }), - "trap", func + "trap", + func, ); None } diff --git a/src/jit/mod.rs b/src/jit/mod.rs index 28eae333..05a1de08 100644 --- a/src/jit/mod.rs +++ b/src/jit/mod.rs @@ -1,16 +1,16 @@ //! JIT subsystem: Cranelift-based JIT manager and lowering stubs -pub mod manager; -pub mod engine; -pub mod lower; -pub mod r#extern; -pub mod rt; pub mod abi; -pub mod config; -pub mod events; -pub mod hostcall_registry; pub mod boundary; -pub mod shim_trace; +pub mod config; +pub mod engine; +pub mod events; +pub mod r#extern; +pub mod hostcall_registry; +pub mod lower; +pub mod manager; pub mod observe; pub mod policy; +pub mod rt; pub mod semantics; +pub mod shim_trace; diff --git a/src/jit/observe.rs b/src/jit/observe.rs index 49cc710c..1c754bc0 100644 --- a/src/jit/observe.rs +++ b/src/jit/observe.rs @@ -1,14 +1,21 @@ //! Observe facade: centralize compile/runtime/trace output rules. //! Thin wrappers around jit::events and shim_trace to keep callsites tidy. -pub fn lower_plugin_invoke(box_type: &str, method: &str, type_id: u32, method_id: u32, argc: usize) { +pub fn lower_plugin_invoke( + box_type: &str, + method: &str, + type_id: u32, + method_id: u32, + argc: usize, +) { crate::jit::events::emit_lower( serde_json::json!({ "id": format!("plugin:{}:{}", box_type, method), "decision":"allow","reason":"plugin_invoke","argc": argc, "type_id": type_id, "method_id": method_id }), - "plugin","" + "plugin", + "", ); } @@ -21,7 +28,8 @@ pub fn lower_hostcall(symbol: &str, argc: usize, arg_types: &[&str], decision: & "argc": argc, "arg_types": arg_types }), - "hostcall","" + "hostcall", + "", ); } @@ -34,10 +42,14 @@ pub fn runtime_plugin_shim_i64(type_id: i64, method_id: i64, argc: i64, inst: u3 "argc": argc, "inst": inst }), - "plugin", "" + "plugin", + "", ); } -pub fn trace_push(msg: String) { crate::jit::shim_trace::push(msg); } -pub fn trace_enabled() -> bool { crate::jit::shim_trace::is_enabled() } - +pub fn trace_push(msg: String) { + crate::jit::shim_trace::push(msg); +} +pub fn trace_enabled() -> bool { + crate::jit::shim_trace::is_enabled() +} diff --git a/src/jit/policy.rs b/src/jit/policy.rs index 9e9e01bf..4b1bda6a 100644 --- a/src/jit/policy.rs +++ b/src/jit/policy.rs @@ -17,10 +17,19 @@ impl JitPolicy { pub fn from_env() -> Self { let ro = std::env::var("NYASH_JIT_READ_ONLY").ok().as_deref() == Some("1"); // Comma-separated hostcall names - let hc = std::env::var("NYASH_JIT_HOSTCALL_WHITELIST").ok().map(|s| { - s.split(',').map(|t| t.trim().to_string()).filter(|s| !s.is_empty()).collect::>() - }).unwrap_or_default(); - Self { read_only: ro, hostcall_whitelist: hc } + let hc = std::env::var("NYASH_JIT_HOSTCALL_WHITELIST") + .ok() + .map(|s| { + s.split(',') + .map(|t| t.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect::>() + }) + .unwrap_or_default(); + Self { + read_only: ro, + hostcall_whitelist: hc, + } } } @@ -28,14 +37,19 @@ static GLOBAL: OnceCell> = OnceCell::new(); pub fn current() -> JitPolicy { if let Some(l) = GLOBAL.get() { - if let Ok(g) = l.read() { return g.clone(); } + if let Ok(g) = l.read() { + return g.clone(); + } } JitPolicy::from_env() } pub fn set_current(p: JitPolicy) { if let Some(l) = GLOBAL.get() { - if let Ok(mut w) = l.write() { *w = p; return; } + if let Ok(mut w) = l.write() { + *w = p; + return; + } } let _ = GLOBAL.set(RwLock::new(p)); } diff --git a/src/jit/policy/invoke.rs b/src/jit/policy/invoke.rs index f2277c46..d096d6a8 100644 --- a/src/jit/policy/invoke.rs +++ b/src/jit/policy/invoke.rs @@ -5,20 +5,43 @@ #[derive(Debug, Clone)] pub enum InvokeDecision { - PluginInvoke { type_id: u32, method_id: u32, box_type: String, method: String, argc: usize, has_ret: bool }, - HostCall { symbol: String, argc: usize, has_ret: bool, reason: &'static str }, - Fallback { reason: &'static str }, + PluginInvoke { + type_id: u32, + method_id: u32, + box_type: String, + method: String, + argc: usize, + has_ret: bool, + }, + HostCall { + symbol: String, + argc: usize, + has_ret: bool, + reason: &'static str, + }, + Fallback { + reason: &'static str, + }, } fn use_plugin_builtins() -> bool { #[cfg(feature = "jit-direct-only")] - { return false; } + { + return false; + } #[cfg(not(feature = "jit-direct-only"))] - { return std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1"); } + { + return std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1"); + } } /// Decide invocation policy for a known Box method. -pub fn decide_box_method(box_type: &str, method: &str, argc: usize, has_ret: bool) -> InvokeDecision { +pub fn decide_box_method( + box_type: &str, + method: &str, + argc: usize, + has_ret: bool, +) -> InvokeDecision { // HostCall mapping for common collections/strings/instance ops let symbol = match (box_type, method) { ("ArrayBox", "length") => crate::jit::r#extern::collections::SYM_ANY_LEN_H, @@ -26,26 +49,42 @@ pub fn decide_box_method(box_type: &str, method: &str, argc: usize, has_ret: boo ("ArrayBox", "get") => crate::jit::r#extern::collections::SYM_ARRAY_GET_H, ("ArrayBox", "set") => crate::jit::r#extern::collections::SYM_ARRAY_SET_H, ("ArrayBox", "push") => crate::jit::r#extern::collections::SYM_ARRAY_PUSH_H, - ("MapBox", "size") => crate::jit::r#extern::collections::SYM_MAP_SIZE_H, - ("MapBox", "get") => crate::jit::r#extern::collections::SYM_MAP_GET_HH, - ("MapBox", "has") => crate::jit::r#extern::collections::SYM_MAP_HAS_H, - ("MapBox", "set") => crate::jit::r#extern::collections::SYM_MAP_SET_H, - ("StringBox","is_empty") => crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, - ("StringBox","charCodeAt") => crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, - _ => "" // unknown + ("MapBox", "size") => crate::jit::r#extern::collections::SYM_MAP_SIZE_H, + ("MapBox", "get") => crate::jit::r#extern::collections::SYM_MAP_GET_HH, + ("MapBox", "has") => crate::jit::r#extern::collections::SYM_MAP_HAS_H, + ("MapBox", "set") => crate::jit::r#extern::collections::SYM_MAP_SET_H, + ("StringBox", "is_empty") => crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, + ("StringBox", "charCodeAt") => crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, + _ => "", // unknown }; // Prefer HostCall when available if !symbol.is_empty() { - InvokeDecision::HostCall { symbol: symbol.to_string(), argc, has_ret, reason: "mapped_symbol" } + InvokeDecision::HostCall { + symbol: symbol.to_string(), + argc, + has_ret, + reason: "mapped_symbol", + } } else if use_plugin_builtins() { // Try plugin_invoke as a secondary path when enabled if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() { if let Ok(h) = ph.resolve_method(box_type, method) { - return InvokeDecision::PluginInvoke { type_id: h.type_id, method_id: h.method_id, box_type: h.box_type, method: method.to_string(), argc, has_ret }; + return InvokeDecision::PluginInvoke { + type_id: h.type_id, + method_id: h.method_id, + box_type: h.box_type, + method: method.to_string(), + argc, + has_ret, + }; } } - InvokeDecision::Fallback { reason: "unknown_method" } + InvokeDecision::Fallback { + reason: "unknown_method", + } } else { - InvokeDecision::Fallback { reason: "unknown_method" } + InvokeDecision::Fallback { + reason: "unknown_method", + } } } diff --git a/src/jit/rt.rs b/src/jit/rt.rs index e39862a0..a7b8f23f 100644 --- a/src/jit/rt.rs +++ b/src/jit/rt.rs @@ -9,16 +9,36 @@ use crate::jit::abi::JitValue; thread_local! { static LEGACY_VM_ARGS: RefCell> = RefCell::new(Vec::new()); } #[cfg(not(feature = "jit-direct-only"))] -pub fn set_legacy_vm_args(args: &[VMValue]) { LEGACY_VM_ARGS.with(|cell| { let mut v = cell.borrow_mut(); v.clear(); v.extend_from_slice(args); }); } +pub fn set_legacy_vm_args(args: &[VMValue]) { + LEGACY_VM_ARGS.with(|cell| { + let mut v = cell.borrow_mut(); + v.clear(); + v.extend_from_slice(args); + }); +} #[cfg(feature = "jit-direct-only")] -pub fn set_legacy_vm_args(_args: &[VMValue]) { /* no-op in jit-direct-only */ } +pub fn set_legacy_vm_args(_args: &[VMValue]) { /* no-op in jit-direct-only */ +} #[cfg(not(feature = "jit-direct-only"))] -pub fn with_legacy_vm_args(f: F) -> R where F: FnOnce(&[VMValue]) -> R { LEGACY_VM_ARGS.with(|cell| { let v = cell.borrow(); f(&v) }) } +pub fn with_legacy_vm_args(f: F) -> R +where + F: FnOnce(&[VMValue]) -> R, +{ + LEGACY_VM_ARGS.with(|cell| { + let v = cell.borrow(); + f(&v) + }) +} #[cfg(feature = "jit-direct-only")] -pub fn with_legacy_vm_args(f: F) -> R where F: FnOnce(&[VMValue]) -> R { f(&[]) } +pub fn with_legacy_vm_args(f: F) -> R +where + F: FnOnce(&[VMValue]) -> R, +{ + f(&[]) +} // New TLS for independent JIT ABI values thread_local! { @@ -50,16 +70,32 @@ static RET_BOOL_HINT_COUNT: AtomicU64 = AtomicU64::new(0); static PHI_TOTAL_SLOTS: AtomicU64 = AtomicU64::new(0); static PHI_B1_SLOTS: AtomicU64 = AtomicU64::new(0); -pub fn b1_norm_inc(delta: u64) { B1_NORM_COUNT.fetch_add(delta, Ordering::Relaxed); } -pub fn b1_norm_get() -> u64 { B1_NORM_COUNT.load(Ordering::Relaxed) } +pub fn b1_norm_inc(delta: u64) { + B1_NORM_COUNT.fetch_add(delta, Ordering::Relaxed); +} +pub fn b1_norm_get() -> u64 { + B1_NORM_COUNT.load(Ordering::Relaxed) +} -pub fn ret_bool_hint_inc(delta: u64) { RET_BOOL_HINT_COUNT.fetch_add(delta, Ordering::Relaxed); } -pub fn ret_bool_hint_get() -> u64 { RET_BOOL_HINT_COUNT.load(Ordering::Relaxed) } +pub fn ret_bool_hint_inc(delta: u64) { + RET_BOOL_HINT_COUNT.fetch_add(delta, Ordering::Relaxed); +} +pub fn ret_bool_hint_get() -> u64 { + RET_BOOL_HINT_COUNT.load(Ordering::Relaxed) +} -pub fn phi_total_inc(delta: u64) { PHI_TOTAL_SLOTS.fetch_add(delta, Ordering::Relaxed); } -pub fn phi_total_get() -> u64 { PHI_TOTAL_SLOTS.load(Ordering::Relaxed) } -pub fn phi_b1_inc(delta: u64) { PHI_B1_SLOTS.fetch_add(delta, Ordering::Relaxed); } -pub fn phi_b1_get() -> u64 { PHI_B1_SLOTS.load(Ordering::Relaxed) } +pub fn phi_total_inc(delta: u64) { + PHI_TOTAL_SLOTS.fetch_add(delta, Ordering::Relaxed); +} +pub fn phi_total_get() -> u64 { + PHI_TOTAL_SLOTS.load(Ordering::Relaxed) +} +pub fn phi_b1_inc(delta: u64) { + PHI_B1_SLOTS.fetch_add(delta, Ordering::Relaxed); +} +pub fn phi_b1_get() -> u64 { + PHI_B1_SLOTS.load(Ordering::Relaxed) +} // === 10.7c PoC: JIT Handle Registry (thread-local) === use std::collections::HashMap; @@ -80,7 +116,12 @@ pub mod handles { } impl HandleRegistry { - fn new() -> Self { Self { next: 1, map: HashMap::new() } } + fn new() -> Self { + Self { + next: 1, + map: HashMap::new(), + } + } fn to_handle(&mut self, obj: Arc) -> u64 { // Reuse existing handle if already present (pointer equality check) // For PoC simplicity, always assign new handle @@ -92,11 +133,18 @@ pub mod handles { } h } - fn get(&self, h: u64) -> Option> { self.map.get(&h).cloned() } + fn get(&self, h: u64) -> Option> { + self.map.get(&h).cloned() + } #[allow(dead_code)] - fn drop_handle(&mut self, h: u64) { self.map.remove(&h); } + fn drop_handle(&mut self, h: u64) { + self.map.remove(&h); + } #[allow(dead_code)] - fn clear(&mut self) { self.map.clear(); self.next = 1; } + fn clear(&mut self) { + self.map.clear(); + self.next = 1; + } } pub fn to_handle(obj: Arc) -> u64 { @@ -108,8 +156,12 @@ pub mod handles { REG.with(|cell| cell.borrow().get(h)) } #[allow(dead_code)] - pub fn clear() { REG.with(|cell| cell.borrow_mut().clear()); } - pub fn len() -> usize { REG.with(|cell| cell.borrow().map.len()) } + pub fn clear() { + REG.with(|cell| cell.borrow_mut().clear()); + } + pub fn len() -> usize { + REG.with(|cell| cell.borrow().map.len()) + } // Scope management: track and clear handles created within a JIT call pub fn begin_scope() { @@ -128,7 +180,9 @@ pub mod handles { }); REG.with(|cell| { let mut reg = cell.borrow_mut(); - for h in to_drop { reg.map.remove(&h); } + for h in to_drop { + reg.map.remove(&h); + } }); } } diff --git a/src/jit/semantics/clif.rs b/src/jit/semantics/clif.rs index 9072d3b8..1109e228 100644 --- a/src/jit/semantics/clif.rs +++ b/src/jit/semantics/clif.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; -use crate::backend::vm::VMValue; -use crate::mir::{ValueId, BasicBlockId}; use super::Semantics; +use crate::backend::vm::VMValue; +use crate::mir::{BasicBlockId, ValueId}; /// Minimal Semantics for Cranelift skeleton (Const/Add/Return) pub struct ClifSemanticsSkeleton { @@ -11,7 +11,12 @@ pub struct ClifSemanticsSkeleton { } impl ClifSemanticsSkeleton { - pub fn new() -> Self { Self { mem: HashMap::new(), ret_val: None } } + pub fn new() -> Self { + Self { + mem: HashMap::new(), + ret_val: None, + } + } } impl Semantics for ClifSemanticsSkeleton { @@ -20,18 +25,43 @@ impl Semantics for ClifSemanticsSkeleton { type BB = BasicBlockId; // Constants - fn const_i64(&mut self, v: i64) -> Self::Val { VMValue::Integer(v) } - fn const_f64(&mut self, v: f64) -> Self::Val { VMValue::Float(v) } - fn const_bool(&mut self, v: bool) -> Self::Val { VMValue::Bool(v) } - fn const_null(&mut self) -> Self::Val { VMValue::Void } - fn const_str(&mut self, s: &str) -> Self::Val { VMValue::String(s.to_string()) } + fn const_i64(&mut self, v: i64) -> Self::Val { + VMValue::Integer(v) + } + fn const_f64(&mut self, v: f64) -> Self::Val { + VMValue::Float(v) + } + fn const_bool(&mut self, v: bool) -> Self::Val { + VMValue::Bool(v) + } + fn const_null(&mut self) -> Self::Val { + VMValue::Void + } + fn const_str(&mut self, s: &str) -> Self::Val { + VMValue::String(s.to_string()) + } // Unary/Binary/Compare (minimal) fn neg(&mut self, x: Self::Val) -> Self::Val { - match x { VMValue::Integer(i) => VMValue::Integer(-i), VMValue::Float(f) => VMValue::Float(-f), v => v } + match x { + VMValue::Integer(i) => VMValue::Integer(-i), + VMValue::Float(f) => VMValue::Float(-f), + v => v, + } + } + fn not(&mut self, x: Self::Val) -> Self::Val { + VMValue::Bool(matches!( + x, + VMValue::Bool(false) | VMValue::Integer(0) | VMValue::Void + )) + } + fn bit_not(&mut self, x: Self::Val) -> Self::Val { + if let VMValue::Integer(i) = x { + VMValue::Integer(!i) + } else { + x + } } - fn not(&mut self, x: Self::Val) -> Self::Val { VMValue::Bool(matches!(x, VMValue::Bool(false) | VMValue::Integer(0) | VMValue::Void)) } - fn bit_not(&mut self, x: Self::Val) -> Self::Val { if let VMValue::Integer(i) = x { VMValue::Integer(!i) } else { x } } fn add(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { use VMValue as V; match (a, b) { @@ -42,31 +72,101 @@ impl Semantics for ClifSemanticsSkeleton { (V::String(s), V::String(t)) => V::String(format!("{}{}", s, t)), (V::String(s), V::Integer(y)) => V::String(format!("{}{}", s, y)), (V::Integer(x), V::String(t)) => V::String(format!("{}{}", x, t)), - (l, r) => { let s = format!("{}{}", l.to_string(), r.to_string()); V::String(s) } + (l, r) => { + let s = format!("{}{}", l.to_string(), r.to_string()); + V::String(s) + } + } + } + fn sub(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Integer(x - y), + _ => VMValue::Void, + } + } + fn mul(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Integer(x * y), + _ => VMValue::Void, + } + } + fn div(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) if y != 0 => VMValue::Integer(x / y), + _ => VMValue::Void, + } + } + fn modulo(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) if y != 0 => VMValue::Integer(x % y), + _ => VMValue::Void, + } + } + fn cmp_eq(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { + VMValue::Bool(a.to_string() == b.to_string()) + } + fn cmp_ne(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { + VMValue::Bool(a.to_string() != b.to_string()) + } + fn cmp_lt(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x < y), + _ => VMValue::Bool(false), + } + } + fn cmp_le(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x <= y), + _ => VMValue::Bool(false), + } + } + fn cmp_gt(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x > y), + _ => VMValue::Bool(false), + } + } + fn cmp_ge(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x >= y), + _ => VMValue::Bool(false), } } - fn sub(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { match (a, b) { (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Integer(x - y), _ => VMValue::Void } } - fn mul(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { match (a, b) { (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Integer(x * y), _ => VMValue::Void } } - fn div(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { match (a, b) { (VMValue::Integer(x), VMValue::Integer(y)) if y!=0 => VMValue::Integer(x / y), _ => VMValue::Void } } - fn modulo(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { match (a, b) { (VMValue::Integer(x), VMValue::Integer(y)) if y!=0 => VMValue::Integer(x % y), _ => VMValue::Void } } - fn cmp_eq(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { VMValue::Bool(a.to_string() == b.to_string()) } - fn cmp_ne(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { VMValue::Bool(a.to_string() != b.to_string()) } - fn cmp_lt(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { match (a, b) { (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x VMValue::Bool(false) } } - fn cmp_le(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { match (a, b) { (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x<=y), _ => VMValue::Bool(false) } } - fn cmp_gt(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { match (a, b) { (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x>y), _ => VMValue::Bool(false) } } - fn cmp_ge(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { match (a, b) { (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x>=y), _ => VMValue::Bool(false) } } // Memory & control (minimal) - fn alloca_ptr(&mut self, vid: ValueId) -> Self::Ptr { vid } - fn load(&mut self, ptr: &Self::Ptr) -> Self::Val { self.mem.get(ptr).cloned().unwrap_or(VMValue::Void) } - fn store(&mut self, ptr: &Self::Ptr, v: Self::Val) { self.mem.insert(*ptr, v); } + fn alloca_ptr(&mut self, vid: ValueId) -> Self::Ptr { + vid + } + fn load(&mut self, ptr: &Self::Ptr) -> Self::Val { + self.mem.get(ptr).cloned().unwrap_or(VMValue::Void) + } + fn store(&mut self, ptr: &Self::Ptr, v: Self::Val) { + self.mem.insert(*ptr, v); + } fn jump(&mut self, _target: BasicBlockId) {} fn branch(&mut self, _cond: Self::Val, _then_bb: BasicBlockId, _else_bb: BasicBlockId) {} - fn phi_select(&mut self, _incoming: &[(BasicBlockId, Self::Val)]) -> Self::Val { VMValue::Void } - fn ret(&mut self, v: Option) { self.ret_val = v; } + fn phi_select(&mut self, _incoming: &[(BasicBlockId, Self::Val)]) -> Self::Val { + VMValue::Void + } + fn ret(&mut self, v: Option) { + self.ret_val = v; + } // Host/Box calls (unimplemented in skeleton) - fn new_box(&mut self, _type_id: i64, _args: &[Self::Val]) -> Self::Val { VMValue::Void } - fn box_call_tagged(&mut self, _type_id: i64, _method_id: i64, _recv: Self::Val, _argv: &[Self::Val], _tags: &[i64]) -> Self::Val { VMValue::Void } - fn extern_call(&mut self, _iface: &str, _method: &str, _args: &[Self::Val]) -> Self::Val { VMValue::Void } + fn new_box(&mut self, _type_id: i64, _args: &[Self::Val]) -> Self::Val { + VMValue::Void + } + fn box_call_tagged( + &mut self, + _type_id: i64, + _method_id: i64, + _recv: Self::Val, + _argv: &[Self::Val], + _tags: &[i64], + ) -> Self::Val { + VMValue::Void + } + fn extern_call(&mut self, _iface: &str, _method: &str, _args: &[Self::Val]) -> Self::Val { + VMValue::Void + } } diff --git a/src/jit/shim_trace.rs b/src/jit/shim_trace.rs index 6db6589c..b5da7d6d 100644 --- a/src/jit/shim_trace.rs +++ b/src/jit/shim_trace.rs @@ -1,23 +1,32 @@ use once_cell::sync::Lazy; use std::collections::VecDeque; -use std::sync::Mutex; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Mutex; static TRACE_ENABLED: AtomicBool = AtomicBool::new(false); -static EVENTS: Lazy>> = Lazy::new(|| Mutex::new(VecDeque::with_capacity(256))); +static EVENTS: Lazy>> = + Lazy::new(|| Mutex::new(VecDeque::with_capacity(256))); const MAX_EVENTS: usize = 256; -pub fn set_enabled(on: bool) { TRACE_ENABLED.store(on, Ordering::Relaxed); } +pub fn set_enabled(on: bool) { + TRACE_ENABLED.store(on, Ordering::Relaxed); +} pub fn is_enabled() -> bool { - if TRACE_ENABLED.load(Ordering::Relaxed) { return true; } + if TRACE_ENABLED.load(Ordering::Relaxed) { + return true; + } std::env::var("NYASH_JIT_SHIM_TRACE").ok().as_deref() == Some("1") } pub fn push(event: String) { - if !is_enabled() { return; } + if !is_enabled() { + return; + } if let Ok(mut q) = EVENTS.lock() { - if q.len() >= MAX_EVENTS { q.pop_front(); } + if q.len() >= MAX_EVENTS { + q.pop_front(); + } q.push_back(event); } } @@ -26,14 +35,19 @@ pub fn snapshot_joined() -> String { if let Ok(q) = EVENTS.lock() { let mut out = String::new(); for (i, e) in q.iter().enumerate() { - if i > 0 { out.push('\n'); } + if i > 0 { + out.push('\n'); + } out.push_str(e); } out - } else { String::new() } + } else { + String::new() + } } pub fn clear() { - if let Ok(mut q) = EVENTS.lock() { q.clear(); } + if let Ok(mut q) = EVENTS.lock() { + q.clear(); + } } - diff --git a/src/lib.rs b/src/lib.rs index 01afd254..93b3037c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ -/*! +/*! * Nyash Programming Language - Rust Implementation Library - * + * * Everything is Box philosophy implemented in memory-safe Rust */ @@ -8,26 +8,26 @@ #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; +pub mod ast; // Using old ast.rs for now +pub mod box_arithmetic; +pub mod box_factory; // 🏭 Unified Box Factory Architecture (Phase 9.78) +pub mod box_operators; // 🚀 Operator implementations for basic Box types pub mod box_trait; pub mod boxes; -pub mod box_factory; // 🏭 Unified Box Factory Architecture (Phase 9.78) +pub mod channel_box; +pub mod core; // Core models shared by backends +pub mod environment; +pub mod exception_box; +pub mod finalization; +pub mod instance_v2; // 🎯 Phase 9.78d: Simplified InstanceBox implementation +pub mod interpreter; +pub mod method_box; +pub mod operator_traits; // 🚀 Rust-style trait-based operator overloading +pub mod parser; // Using old parser.rs for now pub mod scope_tracker; // 🎯 Phase 9.78a: Box lifecycle tracking for VM pub mod stdlib; -pub mod environment; pub mod tokenizer; -pub mod ast; // Using old ast.rs for now -pub mod parser; // Using old parser.rs for now -pub mod interpreter; -pub mod core; // Core models shared by backends -pub mod instance_v2; // 🎯 Phase 9.78d: Simplified InstanceBox implementation -pub mod channel_box; -pub mod finalization; -pub mod exception_box; -pub mod method_box; -pub mod type_box; // 🌟 TypeBox revolutionary system -pub mod operator_traits; // 🚀 Rust-style trait-based operator overloading -pub mod box_operators; // 🚀 Operator implementations for basic Box types -pub mod box_arithmetic; // 🚀 Arithmetic operations moved from box_trait.rs +pub mod type_box; // 🌟 TypeBox revolutionary system // 🚀 Arithmetic operations moved from box_trait.rs // 🔥 NyashValue Revolutionary System (NEW!) pub mod value; @@ -61,9 +61,9 @@ pub mod config; pub mod cli; // Runtime system (plugins, registry, etc.) -pub mod runtime; -pub mod runner_plugin_init; pub mod debug; +pub mod runner_plugin_init; +pub mod runtime; // Unified Grammar (Phase 11.9 scaffolding) pub mod grammar; pub mod syntax; // Phase 12.7: syntax sugar config and helpers @@ -75,26 +75,26 @@ pub mod wasm_test; pub mod tests; // Re-export main types for easy access -pub use box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox}; -pub use box_arithmetic::{AddBox, SubtractBox, MultiplyBox, DivideBox, ModuloBox, CompareBox}; -pub use environment::{Environment, PythonCompatEnvironment}; -pub use tokenizer::{NyashTokenizer, TokenType, Token}; -pub use type_box::{TypeBox, TypeRegistry, MethodSignature}; // 🌟 TypeBox exports pub use ast::{ASTNode, BinaryOperator, LiteralValue}; -pub use parser::{NyashParser, ParseError}; +pub use box_arithmetic::{AddBox, CompareBox, DivideBox, ModuloBox, MultiplyBox, SubtractBox}; +pub use box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox}; +pub use environment::{Environment, PythonCompatEnvironment}; pub use interpreter::{NyashInterpreter, RuntimeError}; -// pub use instance::InstanceBox; // 旧実装 -pub use instance_v2::InstanceBox; // 🎯 新実装テスト(nyash_rustパス使用) -pub use channel_box::{ChannelBox, MessageBox}; -pub use boxes::math_box::{MathBox, FloatBox, RangeBox}; -pub use boxes::time_box::{TimeBox, DateTimeBox, TimerBox}; +pub use parser::{NyashParser, ParseError}; +pub use tokenizer::{NyashTokenizer, Token, TokenType}; +pub use type_box::{MethodSignature, TypeBox, TypeRegistry}; // 🌟 TypeBox exports + // pub use instance::InstanceBox; // 旧実装 +pub use boxes::console_box::ConsoleBox; +pub use boxes::debug_box::DebugBox; pub use boxes::map_box::MapBox; +pub use boxes::math_box::{FloatBox, MathBox, RangeBox}; +pub use boxes::null_box::{null, NullBox}; pub use boxes::random_box::RandomBox; pub use boxes::sound_box::SoundBox; -pub use boxes::debug_box::DebugBox; -pub use boxes::console_box::ConsoleBox; -pub use method_box::{MethodBox, BoxType, FunctionDefinition, EphemeralInstance}; -pub use boxes::null_box::{NullBox, null}; +pub use boxes::time_box::{DateTimeBox, TimeBox, TimerBox}; +pub use channel_box::{ChannelBox, MessageBox}; +pub use instance_v2::InstanceBox; // 🎯 新実装テスト(nyash_rustパス使用) +pub use method_box::{BoxType, EphemeralInstance, FunctionDefinition, MethodBox}; // 🔥 NyashValue Revolutionary System exports pub use value::NyashValue; @@ -118,17 +118,17 @@ impl NyashWasm { pub fn new() -> Self { // Setup panic handling for better browser debugging console_error_panic_hook::set_once(); - + // Create interpreter with browser-specific setup let interpreter = NyashInterpreter::new(); - + // Register browser-specific boxes // ConsoleBox is available as a constructor: console = new ConsoleBox() // TODO: Also register DOMBox, CanvasBox etc. - + Self { interpreter } } - + /// Evaluate Nyash code and return result as string #[wasm_bindgen] pub fn eval(&mut self, code: &str) -> String { @@ -137,27 +137,29 @@ impl NyashWasm { if trimmed_code.is_empty() { return String::new(); } - + // Split multiline code into logical statements for better WASM handling - let lines: Vec<&str> = trimmed_code.lines() + let lines: Vec<&str> = trimmed_code + .lines() .map(|line| line.trim()) .filter(|line| !line.is_empty() && !line.starts_with("//")) .collect(); - + // If single line or looks like a complete static box/box definition, parse as-is - if lines.len() == 1 || trimmed_code.contains("static box") || trimmed_code.contains("box ") { + if lines.len() == 1 || trimmed_code.contains("static box") || trimmed_code.contains("box ") + { return self.eval_single_block(trimmed_code); } - + // For multiple lines, try to execute line by line let mut results = Vec::new(); let mut accumulated_code = String::new(); - + for line in lines { // Accumulate lines for block structures accumulated_code.push_str(line); accumulated_code.push('\n'); - + // Check if we have a complete statement if self.is_complete_statement(&accumulated_code) { let result = self.eval_single_block(accumulated_code.trim()); @@ -170,7 +172,7 @@ impl NyashWasm { accumulated_code.clear(); } } - + // Execute any remaining accumulated code if !accumulated_code.trim().is_empty() { let result = self.eval_single_block(accumulated_code.trim()); @@ -178,14 +180,15 @@ impl NyashWasm { results.push(result); } } - + // Return the most relevant result - results.into_iter() + results + .into_iter() .filter(|r| !r.starts_with("Parse Error:") && !r.starts_with("Runtime Error:")) .last() .unwrap_or_else(|| "void".to_string()) } - + /// Evaluate a single block of code fn eval_single_block(&mut self, code: &str) -> String { // First parse the code into an AST @@ -193,7 +196,7 @@ impl NyashWasm { Ok(ast) => ast, Err(e) => return format!("Parse Error: {}", e), }; - + // Then execute the AST match self.interpreter.execute(ast) { Ok(result_box) => { @@ -208,24 +211,24 @@ impl NyashWasm { Err(e) => format!("Runtime Error: {}", e), } } - + /// Check if code represents a complete statement (heuristic) fn is_complete_statement(&self, code: &str) -> bool { let trimmed = code.trim(); - + // Always complete: assignments, function calls, simple expressions if trimmed.contains('=') && !trimmed.ends_with('=') { return true; } - + // Block structures need closing braces let open_braces = trimmed.chars().filter(|&c| c == '{').count(); let close_braces = trimmed.chars().filter(|&c| c == '}').count(); - + // Complete if braces are balanced or no braces at all open_braces == 0 || open_braces == close_braces } - + /// Get the current version info #[wasm_bindgen] pub fn version() -> String { diff --git a/src/main.rs b/src/main.rs index bd0b514b..55442ddd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,41 +1,41 @@ /*! * Nyash Rust Implementation - Everything is Box in Memory Safe Rust - * + * * This is the main entry point for the Rust implementation of Nyash, * demonstrating the "Everything is Box" philosophy with Rust's ownership system. - * + * * The main function serves as a thin entry point that delegates to the CLI * and runner modules for actual processing. */ // Core modules +pub mod ast; +pub mod box_arithmetic; // 🚀 Moved from box_trait.rs for better organization +pub mod box_factory; // 🏭 Unified Box Factory Architecture (Phase 9.78) pub mod box_trait; pub mod boxes; -pub mod box_factory; // 🏭 Unified Box Factory Architecture (Phase 9.78) -pub mod stdlib; -pub mod environment; -pub mod tokenizer; -pub mod ast; -pub mod parser; -pub mod interpreter; -pub mod instance_v2; // 🎯 Phase 9.78d: Simplified InstanceBox implementation -pub mod core; // core::model (shared models) pub mod channel_box; -pub mod finalization; +pub mod core; // core::model (shared models) +pub mod environment; pub mod exception_box; -pub mod method_box; -pub mod scope_tracker; // VM scope lifecycle -pub mod operator_traits; -pub mod box_arithmetic; // 🚀 Moved from box_trait.rs for better organization -pub mod value; // 🔥 NyashValue Revolutionary System -pub mod type_box; // 🌟 TypeBox revolutionary system +pub mod finalization; +pub mod instance_v2; // 🎯 Phase 9.78d: Simplified InstanceBox implementation +pub mod interpreter; pub mod messaging; // 🌐 P2P Communication Infrastructure -pub mod transport; // 🌐 P2P Communication Infrastructure +pub mod method_box; +pub mod operator_traits; +pub mod parser; +pub mod scope_tracker; // VM scope lifecycle +pub mod stdlib; +pub mod tokenizer; +pub mod transport; +pub mod type_box; // 🌟 TypeBox revolutionary system +pub mod value; // 🔥 NyashValue Revolutionary System // 🌐 P2P Communication Infrastructure // 🚀 MIR Infrastructure pub mod mir; -// 🚀 Backend Infrastructure +// 🚀 Backend Infrastructure pub mod backend; // JIT subsystem (Phase 10) pub mod jit; @@ -55,11 +55,11 @@ pub mod bid; pub mod config; // Runtime system (plugins, registry, etc.) -pub mod runtime; -pub mod runner_plugin_init; pub mod debug; pub mod grammar; // Phase 11.9 unified grammar scaffolding -pub mod syntax; // Phase 12.7: syntax sugar config and helpers (mirror lib layout) +pub mod runner_plugin_init; +pub mod runtime; +pub mod syntax; // Phase 12.7: syntax sugar config and helpers (mirror lib layout) use nyash_rust::cli::CliConfig; use nyash_rust::config::env as env_config; @@ -71,7 +71,7 @@ fn main() { env_config::bootstrap_from_toml_env(); // Parse command-line arguments let config = CliConfig::parse(); - + // Create and run the execution coordinator let runner = NyashRunner::new(config); runner.run(); @@ -80,8 +80,8 @@ fn main() { #[cfg(test)] mod tests { use super::*; - use box_trait::{StringBox, BoxCore, NyashBox}; - + use box_trait::{BoxCore, NyashBox, StringBox}; + #[test] fn test_main_functionality() { // This test ensures the module structure is correct diff --git a/src/messaging/message_bus.rs b/src/messaging/message_bus.rs index de35ff20..d2063e4f 100644 --- a/src/messaging/message_bus.rs +++ b/src/messaging/message_bus.rs @@ -1,16 +1,16 @@ /*! 🚌 MessageBus - Process-wide Message Routing Singleton - * + * * ## 📝 概要 * MessageBusは、プロセス内でのメッセージルーティングを管理する * シングルトンコンポーネントです。すべてのP2PBoxノードが共有し、 * ローカル通信の高速配送を実現します。 - * + * * ## 🏗️ 設計 * - **Singleton Pattern**: プロセス内で唯一のインスタンス * - **Node Registry**: 登録されたノードの管理 * - **Handler Management**: イベントハンドラーの管理 * - **Async Safe**: Arcによる並行アクセス対応 - * + * * ## 🚀 機能 * - ノードの登録・解除 * - メッセージルーティング @@ -19,9 +19,9 @@ */ use crate::boxes::IntentBox; -use std::sync::{Arc, Mutex}; -use std::collections::HashMap; use once_cell::sync::Lazy; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; /// Intent処理ハンドラーの型 pub type IntentHandler = Box; @@ -40,20 +40,21 @@ impl BusEndpoint { handlers: Arc::new(Mutex::new(HashMap::new())), } } - + /// イベントハンドラーを追加 pub fn add_handler(&self, intent_name: &str, handler: IntentHandler) { let mut handlers = self.handlers.lock().unwrap(); - handlers.entry(intent_name.to_string()) + handlers + .entry(intent_name.to_string()) .or_insert_with(Vec::new) .push(handler); } - + /// メッセージを配送 pub fn deliver(&self, intent: IntentBox, from: &str) { let handlers = self.handlers.lock().unwrap(); let intent_name = intent.get_name().to_string_box().value; - + if let Some(intent_handlers) = handlers.get(&intent_name) { for handler in intent_handlers { handler(intent.clone(), from); @@ -95,27 +96,32 @@ impl MessageBusData { nodes: HashMap::new(), } } - + /// ノードを登録 pub fn register_node(&mut self, id: String, endpoint: BusEndpoint) { self.nodes.insert(id, endpoint); } - + /// ノードを解除 pub fn unregister_node(&mut self, id: &str) -> bool { self.nodes.remove(id).is_some() } - + /// ノードが存在するかチェック pub fn node_exists(&self, id: &str) -> bool { self.nodes.contains_key(id) } - + /// メッセージをルーティング pub fn route(&self, to: &str, intent: IntentBox, from: &str) -> Result<(), SendError> { if let Some(endpoint) = self.nodes.get(to) { if std::env::var("NYASH_DEBUG_P2P").unwrap_or_default() == "1" { - eprintln!("[MessageBus] route {} -> {} intent={}", from, to, intent.get_name().to_string_box().value); + eprintln!( + "[MessageBus] route {} -> {} intent={}", + from, + to, + intent.get_name().to_string_box().value + ); } endpoint.deliver(intent, from); Ok(()) @@ -123,7 +129,7 @@ impl MessageBusData { Err(SendError::NodeNotFound(format!("Node '{}' not found", to))) } } - + /// 登録されたノード一覧を取得 pub fn get_nodes(&self) -> Vec { self.nodes.keys().cloned().collect() @@ -143,9 +149,8 @@ impl MessageBusData { } /// グローバルMessageBusシングルトン -static GLOBAL_MESSAGE_BUS: Lazy = Lazy::new(|| { - Arc::new(Mutex::new(MessageBusData::new())) -}); +static GLOBAL_MESSAGE_BUS: Lazy = + Lazy::new(|| Arc::new(Mutex::new(MessageBusData::new()))); impl MessageBusData { /// グローバルMessageBusへのアクセス diff --git a/src/messaging/mod.rs b/src/messaging/mod.rs index 9eeaf803..cb0c5f10 100644 --- a/src/messaging/mod.rs +++ b/src/messaging/mod.rs @@ -1,9 +1,9 @@ /*! 📡 Messaging Module - P2P Communication Infrastructure - * + * * This module provides the core messaging infrastructure for P2P communication * in Nyash, implementing the MessageBus singleton pattern for local message routing. */ pub mod message_bus; -pub use message_bus::{MessageBus, MessageBusData, BusEndpoint, IntentHandler, SendError}; \ No newline at end of file +pub use message_bus::{BusEndpoint, IntentHandler, MessageBus, MessageBusData, SendError}; diff --git a/src/method_box.rs b/src/method_box.rs index 2a936063..ee75ddaf 100644 --- a/src/method_box.rs +++ b/src/method_box.rs @@ -1,27 +1,27 @@ /*! * MethodBox - Function Pointer Implementation for Nyash - * + * * イベントハンドラーやコールバックを実現するためのMethodBox実装 * ChatGPT先生のアドバイスを全面採用 */ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; use crate::ast::ASTNode; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use crate::instance_v2::InstanceBox; -use std::fmt::{Debug, Display}; -use std::any::Any; -use std::sync::{Arc, Mutex}; use once_cell::sync::OnceCell; +use std::any::Any; +use std::fmt::{Debug, Display}; +use std::sync::{Arc, Mutex}; /// BoxType enum - ChatGPT先生の提案に従い、Box型を分類 #[derive(Debug)] pub enum BoxType { /// 通常のインスタンス Instance(Box), - + /// 関数定義(インスタンスなし) Function(FunctionDefinition), - + /// メソッド参照(インスタンス+メソッド) Method(MethodBox), } @@ -40,13 +40,13 @@ pub struct FunctionDefinition { pub struct MethodBox { /// メソッドを持つインスタンス pub instance: Arc>>, - + /// メソッド名 pub method_name: String, - + /// メソッド定義(キャッシュ用) pub method_def: Option, - + /// Box基底 base: BoxBase, } @@ -57,7 +57,14 @@ impl MethodBox { // メソッド定義をキャッシュ(可能であれば) let method_def = if let Some(inst) = instance.as_any().downcast_ref::() { inst.get_method(&method_name).and_then(|ast| { - if let ASTNode::FunctionDeclaration { name, params, body, is_static, .. } = ast { + if let ASTNode::FunctionDeclaration { + name, + params, + body, + is_static, + .. + } = ast + { Some(FunctionDefinition { name: name.clone(), params: params.clone(), @@ -71,7 +78,7 @@ impl MethodBox { } else { None }; - + Self { instance: Arc::new(Mutex::new(instance)), method_name, @@ -79,15 +86,18 @@ impl MethodBox { base: BoxBase::new(), } } - + /// メソッドを呼び出す pub fn invoke(&self, _args: Vec>) -> Result, String> { if let Some(invoker) = METHOD_INVOKER.get() { return invoker.invoke(self, _args); } - Err(format!("MethodBox.invoke not configured (no invoker) for method '{}'", self.method_name)) + Err(format!( + "MethodBox.invoke not configured (no invoker) for method '{}'", + self.method_name + )) } - + /// インスタンスを取得(内部使用) pub fn get_instance(&self) -> Arc>> { Arc::clone(&self.instance) @@ -96,7 +106,11 @@ impl MethodBox { /// Global invoker hook to connect MethodBox to the interpreter pub trait MethodInvoker: Send + Sync { - fn invoke(&self, method: &MethodBox, args: Vec>) -> Result, String>; + fn invoke( + &self, + method: &MethodBox, + args: Vec>, + ) -> Result, String>; } static METHOD_INVOKER: OnceCell> = OnceCell::new(); @@ -109,29 +123,29 @@ impl NyashBox for MethodBox { fn to_string_box(&self) -> StringBox { StringBox::new(format!("", self.method_name)) } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_method) = other.as_any().downcast_ref::() { // 同じインスタンス、同じメソッド名なら等しい let self_inst = self.instance.lock().unwrap(); let other_inst = other_method.instance.lock().unwrap(); BoolBox::new( - self_inst.box_id() == other_inst.box_id() && - self.method_name == other_method.method_name + self_inst.box_id() == other_inst.box_id() + && self.method_name == other_method.method_name, ) } else { BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { "MethodBox" } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -150,11 +164,11 @@ impl BoxCore for MethodBox { fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "", self.method_name) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -171,7 +185,7 @@ impl Display for MethodBox { pub struct EphemeralInstance { /// ローカル変数 pub locals: HashMap>, - + /// 親インスタンス(メソッド呼び出しの場合) pub parent_instance: Option>>>, } @@ -186,7 +200,7 @@ impl EphemeralInstance { parent_instance: None, } } - + /// インスタンスから一時スコープを作成 pub fn from_instance(instance: Arc>>) -> Self { Self { @@ -194,24 +208,24 @@ impl EphemeralInstance { parent_instance: Some(instance), } } - + /// ローカル変数を設定 pub fn set_local(&mut self, name: String, value: Box) { self.locals.insert(name, value); } - + /// ローカル変数を取得 pub fn get_local(&self, name: &str) -> Option> { self.locals.get(name).map(|v| v.clone_box()) } - + /// 変数を解決(local → instance fields → global) pub fn resolve_variable(&self, name: &str) -> Option> { // 1. ローカル変数 if let Some(value) = self.get_local(name) { return Some(value); } - + // 2. インスタンスフィールド(parent_instanceがある場合) if let Some(parent) = &self.parent_instance { let inst = parent.lock().unwrap(); @@ -221,7 +235,7 @@ impl EphemeralInstance { } } } - + // 3. グローバル(TODO: インタープリタとの統合が必要) None } diff --git a/src/mir/aot_plan_import.rs b/src/mir/aot_plan_import.rs index cfea1e9a..6011f0db 100644 --- a/src/mir/aot_plan_import.rs +++ b/src/mir/aot_plan_import.rs @@ -1,7 +1,10 @@ //! AOT-Plan v1 → MIR13 importer (Phase 15.1) //! Feature-gated behind `aot-plan-import`. -use crate::mir::{MirModule, MirFunction, FunctionSignature, BasicBlockId, MirInstruction, EffectMask, MirType, ConstValue}; +use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, +}; #[derive(Debug, serde::Deserialize)] struct PlanV1 { @@ -21,7 +24,10 @@ struct PlanFunction { } #[derive(Debug, serde::Deserialize)] -struct PlanParam { name: String, r#type: Option } +struct PlanParam { + name: String, + r#type: Option, +} #[derive(Debug, serde::Deserialize)] #[serde(tag = "kind")] @@ -44,31 +50,51 @@ fn map_type(s: Option<&str>) -> MirType { } fn const_from_json(v: &serde_json::Value) -> Option { - if let Some(i) = v.as_i64() { return Some(ConstValue::Integer(i)); } - if let Some(b) = v.as_bool() { return Some(ConstValue::Bool(b)); } - if let Some(f) = v.as_f64() { return Some(ConstValue::Float(f)); } - if let Some(s) = v.as_str() { return Some(ConstValue::String(s.to_string())); } + if let Some(i) = v.as_i64() { + return Some(ConstValue::Integer(i)); + } + if let Some(b) = v.as_bool() { + return Some(ConstValue::Bool(b)); + } + if let Some(f) = v.as_f64() { + return Some(ConstValue::Float(f)); + } + if let Some(s) = v.as_str() { + return Some(ConstValue::String(s.to_string())); + } None } /// Import a v1 plan JSON string into a MIR13 module with skeleton bodies. pub fn import_from_str(plan_json: &str) -> Result { - let plan: PlanV1 = serde_json::from_str(plan_json).map_err(|e| format!("invalid plan json: {}", e))?; - if plan.version != "1" { return Err("unsupported plan version".into()); } + let plan: PlanV1 = + serde_json::from_str(plan_json).map_err(|e| format!("invalid plan json: {}", e))?; + if plan.version != "1" { + return Err("unsupported plan version".into()); + } let mut module = MirModule::new(plan.name.unwrap_or_else(|| "aot_plan".into())); for f in plan.functions.iter() { // Signatures: keep types minimal; params exist but VM uses stackless calling for main let ret_ty = map_type(f.return_type.as_deref()); - let sig = FunctionSignature { name: f.name.clone(), params: vec![], return_type: ret_ty.clone(), effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: f.name.clone(), + params: vec![], + return_type: ret_ty.clone(), + effects: EffectMask::PURE, + }; let mut mf = MirFunction::new(sig, BasicBlockId::new(0)); let bb = mf.entry_block; // Body lowering (skeleton) match &f.body { Some(PlanBody::ConstReturn { value }) => { let dst = mf.next_value_id(); - let cst = const_from_json(value).ok_or_else(|| format!("unsupported const value in {}", f.name))?; - if let Some(b) = mf.get_block_mut(bb) { b.add_instruction(MirInstruction::Const { dst, value: cst }); b.set_terminator(MirInstruction::Return { value: Some(dst) }); } + let cst = const_from_json(value) + .ok_or_else(|| format!("unsupported const value in {}", f.name))?; + if let Some(b) = mf.get_block_mut(bb) { + b.add_instruction(MirInstruction::Const { dst, value: cst }); + b.set_terminator(MirInstruction::Return { value: Some(dst) }); + } // If return_type is unspecified, set Unknown to allow VM dynamic display // Otherwise retain declared type if matches!(ret_ty, MirType::Unknown) { /* keep Unknown */ } @@ -76,7 +102,13 @@ pub fn import_from_str(plan_json: &str) -> Result { Some(PlanBody::Empty) | None => { // Return void or default 0 for integer; choose Unknown for display stability let dst = mf.next_value_id(); - if let Some(b) = mf.get_block_mut(bb) { b.add_instruction(MirInstruction::Const { dst, value: ConstValue::Integer(0) }); b.set_terminator(MirInstruction::Return { value: Some(dst) }); } + if let Some(b) = mf.get_block_mut(bb) { + b.add_instruction(MirInstruction::Const { + dst, + value: ConstValue::Integer(0), + }); + b.set_terminator(MirInstruction::Return { value: Some(dst) }); + } mf.signature.return_type = MirType::Unknown; } } @@ -84,4 +116,3 @@ pub fn import_from_str(plan_json: &str) -> Result { } Ok(module) } - diff --git a/src/mir/basic_block.rs b/src/mir/basic_block.rs index 89658c81..37d5040e 100644 --- a/src/mir/basic_block.rs +++ b/src/mir/basic_block.rs @@ -1,10 +1,10 @@ /*! * MIR Basic Block - Control Flow Graph Building Block - * + * * SSA-form basic blocks with phi functions and terminator instructions */ -use super::{MirInstruction, ValueId, EffectMask}; +use super::{EffectMask, MirInstruction, ValueId}; use std::collections::HashSet; use std::fmt; @@ -17,17 +17,17 @@ impl BasicBlockId { pub fn new(id: u32) -> Self { BasicBlockId(id) } - + /// Get the raw ID value pub fn as_u32(self) -> u32 { self.0 } - + /// Create BasicBlockId from usize (for array indexing) pub fn from_usize(id: usize) -> Self { BasicBlockId(id as u32) } - + /// Convert to usize (for array indexing) pub fn to_usize(self) -> usize { self.0 as usize @@ -45,25 +45,25 @@ impl fmt::Display for BasicBlockId { pub struct BasicBlock { /// Unique identifier for this block pub id: BasicBlockId, - + /// Instructions in this block (excluding terminator) pub instructions: Vec, - + /// Terminator instruction (branch, jump, or return) pub terminator: Option, - + /// Predecessors in the control flow graph pub predecessors: HashSet, - + /// Successors in the control flow graph pub successors: HashSet, - + /// Combined effect mask for all instructions in this block pub effects: EffectMask, - + /// Whether this block is reachable from the entry block pub reachable: bool, - + /// Is this block sealed? (all predecessors are known) pub sealed: bool, } @@ -82,150 +82,155 @@ impl BasicBlock { sealed: false, } } - + /// Add an instruction to this block pub fn add_instruction(&mut self, instruction: MirInstruction) { // Update effect mask self.effects = self.effects | instruction.effects(); - + // Check if this is a terminator instruction if self.is_terminator(&instruction) { if self.terminator.is_some() { panic!("Basic block {} already has a terminator", self.id); } self.terminator = Some(instruction); - + // Update successors based on terminator self.update_successors_from_terminator(); } else { self.instructions.push(instruction); } } - + /// Check if an instruction is a terminator fn is_terminator(&self, instruction: &MirInstruction) -> bool { - matches!(instruction, - MirInstruction::Branch { .. } | - MirInstruction::Jump { .. } | - MirInstruction::Return { .. } | - MirInstruction::Throw { .. } + matches!( + instruction, + MirInstruction::Branch { .. } + | MirInstruction::Jump { .. } + | MirInstruction::Return { .. } + | MirInstruction::Throw { .. } ) } - + /// Update successors based on the terminator instruction fn update_successors_from_terminator(&mut self) { self.successors.clear(); - + if let Some(ref terminator) = self.terminator { match terminator { - MirInstruction::Branch { then_bb, else_bb, .. } => { + MirInstruction::Branch { + then_bb, else_bb, .. + } => { self.successors.insert(*then_bb); self.successors.insert(*else_bb); - }, + } MirInstruction::Jump { target } => { self.successors.insert(*target); - }, + } MirInstruction::Return { .. } => { // No successors for return - }, + } MirInstruction::Throw { .. } => { // No normal successors for throw - control goes to exception handlers // Exception edges are handled separately from normal control flow - }, + } _ => unreachable!("Non-terminator instruction in terminator position"), } } } - + /// Add a predecessor pub fn add_predecessor(&mut self, pred: BasicBlockId) { self.predecessors.insert(pred); } - + /// Remove a predecessor pub fn remove_predecessor(&mut self, pred: BasicBlockId) { self.predecessors.remove(&pred); } - + /// Get all instructions including terminator pub fn all_instructions(&self) -> impl Iterator { self.instructions.iter().chain(self.terminator.iter()) } - + /// Get all values defined in this block pub fn defined_values(&self) -> Vec { self.all_instructions() .filter_map(|inst| inst.dst_value()) .collect() } - + /// Get all values used in this block pub fn used_values(&self) -> Vec { self.all_instructions() .flat_map(|inst| inst.used_values()) .collect() } - + /// Check if this block is empty (no instructions) pub fn is_empty(&self) -> bool { self.instructions.is_empty() && self.terminator.is_none() } - + /// Check if this block has a terminator pub fn is_terminated(&self) -> bool { self.terminator.is_some() } - + /// Check if this block ends with a return pub fn ends_with_return(&self) -> bool { matches!(self.terminator, Some(MirInstruction::Return { .. })) } - + /// Get the phi instructions at the beginning of this block pub fn phi_instructions(&self) -> impl Iterator { - self.instructions.iter() + self.instructions + .iter() .take_while(|inst| matches!(inst, MirInstruction::Phi { .. })) } - + /// Get non-phi instructions pub fn non_phi_instructions(&self) -> impl Iterator { - self.instructions.iter() + self.instructions + .iter() .skip_while(|inst| matches!(inst, MirInstruction::Phi { .. })) } - + /// Insert instruction at the beginning (after phi instructions) pub fn insert_instruction_after_phis(&mut self, instruction: MirInstruction) { let phi_count = self.phi_instructions().count(); self.effects = self.effects | instruction.effects(); self.instructions.insert(phi_count, instruction); } - + /// Replace terminator instruction pub fn set_terminator(&mut self, terminator: MirInstruction) { if !self.is_terminator(&terminator) { panic!("Instruction is not a valid terminator: {:?}", terminator); } - + self.effects = self.effects | terminator.effects(); self.terminator = Some(terminator); self.update_successors_from_terminator(); } - + /// Mark this block as reachable pub fn mark_reachable(&mut self) { self.reachable = true; } - + /// Seal this block (all predecessors are known) pub fn seal(&mut self) { self.sealed = true; } - + /// Check if this block is sealed pub fn is_sealed(&self) -> bool { self.sealed } - + /// Check if this block dominates another block (simplified check) pub fn dominates(&self, other: BasicBlockId, dominators: &[HashSet]) -> bool { if let Some(dom_set) = dominators.get(other.to_usize()) { @@ -247,19 +252,19 @@ impl BasicBlockIdGenerator { pub fn new() -> Self { Self { next_id: 0 } } - + /// Generate the next unique BasicBlockId pub fn next(&mut self) -> BasicBlockId { let id = BasicBlockId(self.next_id); self.next_id += 1; id } - + /// Peek at the next ID without consuming it pub fn peek_next(&self) -> BasicBlockId { BasicBlockId(self.next_id) } - + /// Reset the generator (for testing) pub fn reset(&mut self) { self.next_id = 0; @@ -275,30 +280,28 @@ impl Default for BasicBlockIdGenerator { impl fmt::Display for BasicBlock { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "{}:", self.id)?; - + // Show predecessors if !self.predecessors.is_empty() { - let preds: Vec = self.predecessors.iter() - .map(|p| format!("{}", p)) - .collect(); + let preds: Vec = self.predecessors.iter().map(|p| format!("{}", p)).collect(); writeln!(f, " ; preds: {}", preds.join(", "))?; } - + // Show instructions for instruction in &self.instructions { writeln!(f, " {}", instruction)?; } - + // Show terminator if let Some(ref terminator) = self.terminator { writeln!(f, " {}", terminator)?; } - + // Show effects if not pure if !self.effects.is_pure() { writeln!(f, " ; effects: {}", self.effects)?; } - + Ok(()) } } @@ -306,98 +309,98 @@ impl fmt::Display for BasicBlock { #[cfg(test)] mod tests { use super::*; - use crate::mir::{ConstValue, BinaryOp}; - + use crate::mir::{BinaryOp, ConstValue}; + #[test] fn test_basic_block_creation() { let bb_id = BasicBlockId::new(0); let mut bb = BasicBlock::new(bb_id); - + assert_eq!(bb.id, bb_id); assert!(bb.is_empty()); assert!(!bb.is_terminated()); assert!(bb.effects.is_pure()); } - + #[test] fn test_instruction_addition() { let bb_id = BasicBlockId::new(0); let mut bb = BasicBlock::new(bb_id); - + let const_inst = MirInstruction::Const { dst: ValueId::new(0), value: ConstValue::Integer(42), }; - + bb.add_instruction(const_inst); - + assert_eq!(bb.instructions.len(), 1); assert!(!bb.is_empty()); assert!(bb.effects.is_pure()); } - + #[test] fn test_terminator_addition() { let bb_id = BasicBlockId::new(0); let mut bb = BasicBlock::new(bb_id); - + let return_inst = MirInstruction::Return { value: Some(ValueId::new(0)), }; - + bb.add_instruction(return_inst); - + assert!(bb.is_terminated()); assert!(bb.ends_with_return()); assert_eq!(bb.instructions.len(), 0); // Terminator not in instructions assert!(bb.terminator.is_some()); } - + #[test] fn test_branch_successors() { let bb_id = BasicBlockId::new(0); let mut bb = BasicBlock::new(bb_id); - + let then_bb = BasicBlockId::new(1); let else_bb = BasicBlockId::new(2); - + let branch_inst = MirInstruction::Branch { condition: ValueId::new(0), then_bb, else_bb, }; - + bb.add_instruction(branch_inst); - + assert_eq!(bb.successors.len(), 2); assert!(bb.successors.contains(&then_bb)); assert!(bb.successors.contains(&else_bb)); } - + #[test] fn test_basic_block_id_generator() { let mut gen = BasicBlockIdGenerator::new(); - + let bb1 = gen.next(); let bb2 = gen.next(); let bb3 = gen.next(); - + assert_eq!(bb1, BasicBlockId(0)); assert_eq!(bb2, BasicBlockId(1)); assert_eq!(bb3, BasicBlockId(2)); - + assert_eq!(gen.peek_next(), BasicBlockId(3)); } - + #[test] fn test_value_tracking() { let bb_id = BasicBlockId::new(0); let mut bb = BasicBlock::new(bb_id); - + let val1 = ValueId::new(1); let val2 = ValueId::new(2); let val3 = ValueId::new(3); - + // Add instruction that defines val3 and uses val1, val2 bb.add_instruction(MirInstruction::BinOp { dst: val3, @@ -405,38 +408,38 @@ mod tests { lhs: val1, rhs: val2, }); - + let defined = bb.defined_values(); let used = bb.used_values(); - + assert_eq!(defined, vec![val3]); assert_eq!(used, vec![val1, val2]); } - + #[test] fn test_phi_instruction_ordering() { let bb_id = BasicBlockId::new(0); let mut bb = BasicBlock::new(bb_id); - + // Add phi instruction let phi_inst = MirInstruction::Phi { dst: ValueId::new(0), inputs: vec![(BasicBlockId::new(1), ValueId::new(1))], }; bb.add_instruction(phi_inst); - + // Add regular instruction let const_inst = MirInstruction::Const { dst: ValueId::new(2), value: ConstValue::Integer(42), }; bb.add_instruction(const_inst); - + // Phi instructions should come first let phi_count = bb.phi_instructions().count(); assert_eq!(phi_count, 1); - + let non_phi_count = bb.non_phi_instructions().count(); assert_eq!(non_phi_count, 1); } -} \ No newline at end of file +} diff --git a/src/mir/builder.rs b/src/mir/builder.rs index cbe413e3..62dfbbb0 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -1,35 +1,34 @@ /*! * MIR Builder - Converts AST to MIR/SSA form - * + * * Implements AST → MIR conversion with SSA construction */ -use super::{ - MirInstruction, BasicBlock, BasicBlockId, MirFunction, MirModule, - FunctionSignature, ValueId, ConstValue, CompareOp, - MirType, EffectMask, Effect, BasicBlockIdGenerator, ValueIdGenerator -}; use super::slot_registry::resolve_slot_by_type_name; +use super::{ + BasicBlock, BasicBlockId, BasicBlockIdGenerator, CompareOp, ConstValue, Effect, EffectMask, + FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId, ValueIdGenerator, +}; use crate::ast::{ASTNode, LiteralValue}; use std::collections::HashMap; use std::collections::HashSet; use std::fs; mod builder_calls; -mod phi; -mod stmts; -mod ops; -mod utils; -mod exprs; // expression lowering split mod decls; // declarations lowering split -mod fields; // field access/assignment lowering split -mod exprs_call; // call(expr) -mod exprs_qmark; // ?-propagate -mod exprs_peek; // peek expression -mod exprs_lambda; // lambda lowering +mod exprs; // expression lowering split +mod exprs_call; // call(expr) mod exprs_include; // include lowering +mod exprs_lambda; // lambda lowering +mod exprs_peek; // peek expression +mod exprs_qmark; // ?-propagate +mod fields; // field access/assignment lowering split +pub(crate) mod loops; +mod ops; +mod phi; mod plugin_sigs; // plugin signature loader -mod vars; // variables/scope helpers -pub(crate) mod loops; // small loop helpers (header/exit context) +mod stmts; +mod utils; +mod vars; // variables/scope helpers // small loop helpers (header/exit context) // moved helpers to builder/utils.rs @@ -37,22 +36,22 @@ pub(crate) mod loops; // small loop helpers (header/exit context) pub struct MirBuilder { /// Current module being built pub(super) current_module: Option, - + /// Current function being built pub(super) current_function: Option, - + /// Current basic block being built pub(super) current_block: Option, - + /// Value ID generator pub(super) value_gen: ValueIdGenerator, - + /// Basic block ID generator pub(super) block_gen: BasicBlockIdGenerator, - + /// Variable name to ValueId mapping (for SSA conversion) pub(super) variable_map: HashMap, - + /// Pending phi functions to be inserted #[allow(dead_code)] pub(super) pending_phis: Vec<(BasicBlockId, ValueId, String)>, @@ -120,9 +119,8 @@ impl MirBuilder { } } - // moved to builder_calls.rs: lower_method_as_function - + /// Build a complete MIR module from AST pub fn build_module(&mut self, ast: ASTNode) -> Result { self.prepare_module()?; @@ -147,7 +145,11 @@ impl MirBuilder { self.current_function = Some(main_function); self.current_block = Some(entry_block); - if std::env::var("NYASH_BUILDER_SAFEPOINT_ENTRY").ok().as_deref() == Some("1") { + if std::env::var("NYASH_BUILDER_SAFEPOINT_ENTRY") + .ok() + .as_deref() + == Some("1") + { self.emit_instruction(MirInstruction::Safepoint)?; } @@ -177,7 +179,10 @@ impl MirBuilder { let mut module = self.current_module.take().unwrap(); let mut function = self.current_function.take().unwrap(); function.metadata.value_types = self.value_types.clone(); - if matches!(function.signature.return_type, super::MirType::Void | super::MirType::Unknown) { + if matches!( + function.signature.return_type, + super::MirType::Void | super::MirType::Unknown + ) { let mut inferred: Option = None; 'outer: for (_bid, bb) in function.blocks.iter() { for inst in bb.instructions.iter() { @@ -186,7 +191,8 @@ impl MirBuilder { inferred = Some(mt); break 'outer; } - if let Some(mt) = phi::infer_type_from_phi(&function, *v, &self.value_types) { + if let Some(mt) = phi::infer_type_from_phi(&function, *v, &self.value_types) + { inferred = Some(mt); break 'outer; } @@ -211,59 +217,75 @@ impl MirBuilder { Ok(module) } - + /// Build an expression and return its value ID pub(super) fn build_expression(&mut self, ast: ASTNode) -> Result { // Delegated to exprs.rs to keep this file lean self.build_expression_impl(ast) } - + // Moved implementation to exprs.rs; keeping a small shim here improves readability pub(super) fn build_expression_impl_legacy(&mut self, ast: ASTNode) -> Result { match ast { - ASTNode::Literal { value, .. } => { - self.build_literal(value) - }, - - ASTNode::BinaryOp { left, operator, right, .. } => { - self.build_binary_op(*left, operator, *right) - }, - - ASTNode::UnaryOp { operator, operand, .. } => { + ASTNode::Literal { value, .. } => self.build_literal(value), + + ASTNode::BinaryOp { + left, + operator, + right, + .. + } => self.build_binary_op(*left, operator, *right), + + ASTNode::UnaryOp { + operator, operand, .. + } => { let op_string = match operator { crate::ast::UnaryOperator::Minus => "-".to_string(), crate::ast::UnaryOperator::Not => "not".to_string(), }; self.build_unary_op(op_string, *operand) - }, - - ASTNode::Variable { name, .. } => { - self.build_variable_access(name.clone()) - }, - - ASTNode::Me { .. } => { - self.build_me_expression() - }, - - ASTNode::MethodCall { object, method, arguments, .. } => { + } + + ASTNode::Variable { name, .. } => self.build_variable_access(name.clone()), + + ASTNode::Me { .. } => self.build_me_expression(), + + ASTNode::MethodCall { + object, + method, + arguments, + .. + } => { // Early TypeOp lowering for method-style is()/as() if (method == "is" || method == "as") && arguments.len() == 1 { if let Some(type_name) = Self::extract_string_literal(&arguments[0]) { let obj_val = self.build_expression(*object.clone())?; let ty = Self::parse_type_name_to_mir(&type_name); let dst = self.value_gen.next(); - let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast }; - self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?; + let op = if method == "is" { + super::TypeOpKind::Check + } else { + super::TypeOpKind::Cast + }; + self.emit_instruction(MirInstruction::TypeOp { + dst, + op, + value: obj_val, + ty, + })?; return Ok(dst); } } self.build_method_call(*object.clone(), method.clone(), arguments.clone()) - }, - - ASTNode::FromCall { parent, method, arguments, .. } => { - self.build_from_expression(parent.clone(), method.clone(), arguments.clone()) - }, - + } + + ASTNode::FromCall { + parent, + method, + arguments, + .. + } => self.build_from_expression(parent.clone(), method.clone(), arguments.clone()), + ASTNode::Assignment { target, value, .. } => { // Check if target is a field access for RefSet if let ASTNode::FieldAccess { object, field, .. } = target.as_ref() { @@ -274,33 +296,57 @@ impl MirBuilder { } else { Err("Complex assignment targets not yet supported in MIR".to_string()) } - }, - - ASTNode::FunctionCall { name, arguments, .. } => { + } + + ASTNode::FunctionCall { + name, arguments, .. + } => { // Early TypeOp lowering for function-style isType()/asType() if (name == "isType" || name == "asType") && arguments.len() == 2 { if let Some(type_name) = Self::extract_string_literal(&arguments[1]) { let val = self.build_expression(arguments[0].clone())?; let ty = Self::parse_type_name_to_mir(&type_name); let dst = self.value_gen.next(); - let op = if name == "isType" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast }; - self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?; + let op = if name == "isType" { + super::TypeOpKind::Check + } else { + super::TypeOpKind::Cast + }; + self.emit_instruction(MirInstruction::TypeOp { + dst, + op, + value: val, + ty, + })?; return Ok(dst); } } self.build_function_call(name.clone(), arguments.clone()) - }, - ASTNode::Call { callee, arguments, .. } => { + } + ASTNode::Call { + callee, arguments, .. + } => { // P1.5: Lambdaはインライン、それ以外は Call に正規化 if let ASTNode::Lambda { params, body, .. } = callee.as_ref() { if params.len() != arguments.len() { - return Err(format!("Lambda expects {} args, got {}", params.len(), arguments.len())); + return Err(format!( + "Lambda expects {} args, got {}", + params.len(), + arguments.len() + )); } let mut arg_vals: Vec = Vec::new(); - for a in arguments { arg_vals.push(self.build_expression(a)?); } + for a in arguments { + arg_vals.push(self.build_expression(a)?); + } let saved_vars = self.variable_map.clone(); - for (p, v) in params.iter().zip(arg_vals.iter()) { self.variable_map.insert(p.clone(), *v); } - let prog = ASTNode::Program { statements: body.clone(), span: crate::ast::Span::unknown() }; + for (p, v) in params.iter().zip(arg_vals.iter()) { + self.variable_map.insert(p.clone(), *v); + } + let prog = ASTNode::Program { + statements: body.clone(), + span: crate::ast::Span::unknown(), + }; let out = self.build_expression(prog)?; self.variable_map = saved_vars; Ok(out) @@ -308,43 +354,71 @@ impl MirBuilder { // callee/args を評価し、Call を発行(VM 側で FunctionBox/関数名の両対応) let callee_id = self.build_expression(*callee.clone())?; let mut arg_ids = Vec::new(); - for a in arguments { arg_ids.push(self.build_expression(a)?); } + for a in arguments { + arg_ids.push(self.build_expression(a)?); + } let dst = self.value_gen.next(); - self.emit_instruction(MirInstruction::Call { dst: Some(dst), func: callee_id, args: arg_ids, effects: EffectMask::PURE })?; + self.emit_instruction(MirInstruction::Call { + dst: Some(dst), + func: callee_id, + args: arg_ids, + effects: EffectMask::PURE, + })?; Ok(dst) } - }, - + } + ASTNode::QMarkPropagate { expression, .. } => { // Lower: ok = expr.isOk(); br ok then else; else => return expr; then => expr.getValue() let res_val = self.build_expression(*expression.clone())?; let ok_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::BoxCall { dst: Some(ok_id), box_val: res_val, method: "isOk".to_string(), method_id: None, args: vec![], effects: EffectMask::PURE })?; + self.emit_instruction(MirInstruction::BoxCall { + dst: Some(ok_id), + box_val: res_val, + method: "isOk".to_string(), + method_id: None, + args: vec![], + effects: EffectMask::PURE, + })?; let then_block = self.block_gen.next(); let else_block = self.block_gen.next(); - self.emit_instruction(MirInstruction::Branch { condition: ok_id, then_bb: then_block, else_bb: else_block })?; + self.emit_instruction(MirInstruction::Branch { + condition: ok_id, + then_bb: then_block, + else_bb: else_block, + })?; // else: return res_val self.current_block = Some(else_block); self.ensure_block_exists(else_block)?; - self.emit_instruction(MirInstruction::Return { value: Some(res_val) })?; + self.emit_instruction(MirInstruction::Return { + value: Some(res_val), + })?; // then: getValue() self.current_block = Some(then_block); self.ensure_block_exists(then_block)?; let val_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::BoxCall { dst: Some(val_id), box_val: res_val, method: "getValue".to_string(), method_id: None, args: vec![], effects: EffectMask::PURE })?; + self.emit_instruction(MirInstruction::BoxCall { + dst: Some(val_id), + box_val: res_val, + method: "getValue".to_string(), + method_id: None, + args: vec![], + effects: EffectMask::PURE, + })?; self.value_types.insert(val_id, super::MirType::Unknown); Ok(val_id) - }, - - ASTNode::Print { expression, .. } => { - self.build_print_statement(*expression.clone()) - }, - - ASTNode::Program { statements, .. } => { - self.build_block(statements.clone()) - }, - - ASTNode::If { condition, then_body, else_body, .. } => { + } + + ASTNode::Print { expression, .. } => self.build_print_statement(*expression.clone()), + + ASTNode::Program { statements, .. } => self.build_block(statements.clone()), + + ASTNode::If { + condition, + then_body, + else_body, + .. + } => { let else_ast = if let Some(else_statements) = else_body { Some(ASTNode::Program { statements: else_statements.clone(), @@ -353,31 +427,41 @@ impl MirBuilder { } else { None }; - + self.build_if_statement( - *condition.clone(), + *condition.clone(), ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown(), }, - else_ast + else_ast, ) - }, - - ASTNode::Loop { condition, body, .. } => { - self.build_loop_statement(*condition.clone(), body.clone()) - }, - - ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => { - self.build_try_catch_statement(try_body.clone(), catch_clauses.clone(), finally_body.clone()) - }, - - ASTNode::Throw { expression, .. } => { - self.build_throw_statement(*expression.clone()) - }, + } + + ASTNode::Loop { + condition, body, .. + } => self.build_loop_statement(*condition.clone(), body.clone()), + + ASTNode::TryCatch { + try_body, + catch_clauses, + finally_body, + .. + } => self.build_try_catch_statement( + try_body.clone(), + catch_clauses.clone(), + finally_body.clone(), + ), + + ASTNode::Throw { expression, .. } => self.build_throw_statement(*expression.clone()), // P1: Lower peek expression into if-else chain with phi - ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => { + ASTNode::PeekExpr { + scrutinee, + arms, + else_expr, + .. + } => { // Evaluate scrutinee once let scr_val = self.build_expression(*scrutinee.clone())?; @@ -390,19 +474,30 @@ impl MirBuilder { // Build condition: scr_val == lit let lit_id = self.build_literal(lit)?; let cond_id = self.value_gen.next(); - self.emit_instruction(super::MirInstruction::Compare { dst: cond_id, op: super::CompareOp::Eq, lhs: scr_val, rhs: lit_id })?; + self.emit_instruction(super::MirInstruction::Compare { + dst: cond_id, + op: super::CompareOp::Eq, + lhs: scr_val, + rhs: lit_id, + })?; // Create then and next blocks let then_block = self.block_gen.next(); let next_block = self.block_gen.next(); - self.emit_instruction(super::MirInstruction::Branch { condition: cond_id, then_bb: then_block, else_bb: next_block })?; + self.emit_instruction(super::MirInstruction::Branch { + condition: cond_id, + then_bb: then_block, + else_bb: next_block, + })?; // then: evaluate arm expr, jump to merge self.current_block = Some(then_block); self.ensure_block_exists(then_block)?; let then_val = self.build_expression(arm_expr)?; if !self.is_current_block_terminated() { - self.emit_instruction(super::MirInstruction::Jump { target: merge_block })?; + self.emit_instruction(super::MirInstruction::Jump { + target: merge_block, + })?; } phi_inputs.push((then_block, then_val)); @@ -416,7 +511,9 @@ impl MirBuilder { let cur_block = self.current_block.ok_or("No current basic block")?; let else_val = self.build_expression(*else_expr.clone())?; if !self.is_current_block_terminated() { - self.emit_instruction(super::MirInstruction::Jump { target: merge_block })?; + self.emit_instruction(super::MirInstruction::Jump { + target: merge_block, + })?; } phi_inputs.push((cur_block, else_val)); @@ -424,10 +521,13 @@ impl MirBuilder { self.current_block = Some(merge_block); self.ensure_block_exists(merge_block)?; let result_val = self.value_gen.next(); - self.emit_instruction(super::MirInstruction::Phi { dst: result_val, inputs: phi_inputs })?; + self.emit_instruction(super::MirInstruction::Phi { + dst: result_val, + inputs: phi_inputs, + })?; Ok(result_val) - }, - + } + ASTNode::Lambda { params, body, .. } => { // Lambda→FunctionBox 値 Lower(最小 + 簡易キャプチャ解析) let dst = self.value_gen.next(); @@ -435,57 +535,104 @@ impl MirBuilder { use std::collections::HashSet; let mut used: HashSet = HashSet::new(); let mut locals: HashSet = HashSet::new(); - for p in params.iter() { locals.insert(p.clone()); } - for st in body.iter() { vars::collect_free_vars(st, &mut used, &mut locals); } + for p in params.iter() { + locals.insert(p.clone()); + } + for st in body.iter() { + vars::collect_free_vars(st, &mut used, &mut locals); + } // Materialize captures from current variable_map if known let mut captures: Vec<(String, ValueId)> = Vec::new(); for name in used.into_iter() { - if let Some(&vid) = self.variable_map.get(&name) { captures.push((name, vid)); } + if let Some(&vid) = self.variable_map.get(&name) { + captures.push((name, vid)); + } } // me capture(存在すれば) let me = self.variable_map.get("me").copied(); - self.emit_instruction(MirInstruction::FunctionNew { dst, params: params.clone(), body: body.clone(), captures, me })?; - self.value_types.insert(dst, super::MirType::Box("FunctionBox".to_string())); + self.emit_instruction(MirInstruction::FunctionNew { + dst, + params: params.clone(), + body: body.clone(), + captures, + me, + })?; + self.value_types + .insert(dst, super::MirType::Box("FunctionBox".to_string())); Ok(dst) - }, - - ASTNode::Return { value, .. } => { - self.build_return_statement(value.clone()) - }, - - ASTNode::Local { variables, initial_values, .. } => { - self.build_local_statement(variables.clone(), initial_values.clone()) - }, - - ASTNode::BoxDeclaration { name, methods, is_static, fields, constructors, weak_fields, .. } => { + } + + ASTNode::Return { value, .. } => self.build_return_statement(value.clone()), + + ASTNode::Local { + variables, + initial_values, + .. + } => self.build_local_statement(variables.clone(), initial_values.clone()), + + ASTNode::BoxDeclaration { + name, + methods, + is_static, + fields, + constructors, + weak_fields, + .. + } => { if is_static && name == "Main" { self.build_static_main_box(name.clone(), methods.clone()) } else { // Support user-defined boxes - handle as statement, return void // Track as user-defined (eligible for method lowering) self.user_defined_boxes.insert(name.clone()); - self.build_box_declaration(name.clone(), methods.clone(), fields.clone(), weak_fields.clone())?; + self.build_box_declaration( + name.clone(), + methods.clone(), + fields.clone(), + weak_fields.clone(), + )?; // Phase 2: Lower constructors (birth/N) into MIR functions // Function name pattern: "{BoxName}.{constructor_key}" (e.g., "Person.birth/1") for (ctor_key, ctor_ast) in constructors.clone() { if let ASTNode::FunctionDeclaration { params, body, .. } = ctor_ast { let func_name = format!("{}.{}", name, ctor_key); - self.lower_method_as_function(func_name, name.clone(), params.clone(), body.clone())?; + self.lower_method_as_function( + func_name, + name.clone(), + params.clone(), + body.clone(), + )?; } } // Phase 3: Lower instance methods into MIR functions // Function name pattern: "{BoxName}.{method}/{N}" for (method_name, method_ast) in methods.clone() { - if let ASTNode::FunctionDeclaration { params, body, is_static, .. } = method_ast { + if let ASTNode::FunctionDeclaration { + params, + body, + is_static, + .. + } = method_ast + { if !is_static { - let func_name = format!("{}.{}{}", name, method_name, format!("/{}", params.len())); - self.lower_method_as_function(func_name, name.clone(), params.clone(), body.clone())?; + let func_name = format!( + "{}.{}{}", + name, + method_name, + format!("/{}", params.len()) + ); + self.lower_method_as_function( + func_name, + name.clone(), + params.clone(), + body.clone(), + )?; } } } - + // Return a void value since this is a statement let void_val = self.value_gen.next(); self.emit_instruction(MirInstruction::Const { @@ -494,36 +641,49 @@ impl MirBuilder { })?; Ok(void_val) } - }, - + } + ASTNode::FieldAccess { object, field, .. } => { self.build_field_access(*object.clone(), field.clone()) - }, - - ASTNode::New { class, arguments, .. } => { - self.build_new_expression(class.clone(), arguments.clone()) - }, - + } + + ASTNode::New { + class, arguments, .. + } => self.build_new_expression(class.clone(), arguments.clone()), + ASTNode::ArrayLiteral { elements, .. } => { // Lower: new ArrayBox(); for each elem: .push(elem) let arr_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::NewBox { dst: arr_id, box_type: "ArrayBox".to_string(), args: vec![] })?; + self.emit_instruction(MirInstruction::NewBox { + dst: arr_id, + box_type: "ArrayBox".to_string(), + args: vec![], + })?; for e in elements { let v = self.build_expression(e)?; - self.emit_instruction(MirInstruction::BoxCall { dst: None, box_val: arr_id, method: "push".to_string(), method_id: None, args: vec![v], effects: super::EffectMask::MUT })?; + self.emit_instruction(MirInstruction::BoxCall { + dst: None, + box_val: arr_id, + method: "push".to_string(), + method_id: None, + args: vec![v], + effects: super::EffectMask::MUT, + })?; } Ok(arr_id) - }, - + } + // Phase 7: Async operations - ASTNode::Nowait { variable, expression, .. } => { - self.build_nowait_statement(variable.clone(), *expression.clone()) - }, - + ASTNode::Nowait { + variable, + expression, + .. + } => self.build_nowait_statement(variable.clone(), *expression.clone()), + ASTNode::AwaitExpression { expression, .. } => { self.build_await_expression(*expression.clone()) - }, - + } + ASTNode::Include { filename, .. } => { // Resolve and read included file let mut path = utils::resolve_include_path_builder(&filename); @@ -550,12 +710,19 @@ impl MirBuilder { let mut box_name: Option = None; if let crate::ast::ASTNode::Program { statements, .. } = &included_ast { for st in statements { - if let crate::ast::ASTNode::BoxDeclaration { name, is_static, .. } = st { - if *is_static { box_name = Some(name.clone()); break; } + if let crate::ast::ASTNode::BoxDeclaration { + name, is_static, .. + } = st + { + if *is_static { + box_name = Some(name.clone()); + break; + } } } } - let bname = box_name.ok_or_else(|| format!("Include target '{}' has no static box", filename))?; + let bname = box_name + .ok_or_else(|| format!("Include target '{}' has no static box", filename))?; // Lower included AST into current MIR (register types/methods) let _ = self.build_expression(included_ast)?; // Mark caches @@ -563,14 +730,12 @@ impl MirBuilder { self.include_box_map.insert(path.clone(), bname.clone()); // Return a new instance of included box (no args) self.build_new_expression(bname, vec![]) - }, - - _ => { - Err(format!("Unsupported AST node type: {:?}", ast)) } + + _ => Err(format!("Unsupported AST node type: {:?}", ast)), } } - + /// Build a literal value pub(super) fn build_literal(&mut self, literal: LiteralValue) -> Result { // Determine type without moving literal @@ -590,21 +755,23 @@ impl MirBuilder { LiteralValue::Null => ConstValue::Null, LiteralValue::Void => ConstValue::Void, }; - + let dst = self.value_gen.next(); self.emit_instruction(MirInstruction::Const { dst, value: const_value, })?; // Annotate type - if let Some(ty) = ty_for_dst { self.value_types.insert(dst, ty); } - + if let Some(ty) = ty_for_dst { + self.value_types.insert(dst, ty); + } + Ok(dst) } - + // build_binary_op moved to builder/ops.rs // build_unary_op moved to builder/ops.rs - + /// Build variable access pub(super) fn build_variable_access(&mut self, name: String) -> Result { if let Some(&value_id) = self.variable_map.get(&name) { @@ -613,51 +780,84 @@ impl MirBuilder { Err(format!("Undefined variable: {}", name)) } } - + /// Build assignment - pub(super) fn build_assignment(&mut self, var_name: String, value: ASTNode) -> Result { + pub(super) fn build_assignment( + &mut self, + var_name: String, + value: ASTNode, + ) -> Result { let value_id = self.build_expression(value)?; - + // In SSA form, each assignment creates a new value self.variable_map.insert(var_name.clone(), value_id); - + Ok(value_id) } - + // build_function_call_legacy removed (use builder_calls::build_function_call) - + // build_print_statement_legacy moved to builder/stmts.rs - + // build_block_legacy moved to builder/stmts.rs - + // build_if_statement_legacy moved to builder/stmts.rs // extract_assigned_var moved to builder/stmts.rs (as module helper) - + /// Emit an instruction to the current basic block pub(super) fn emit_instruction(&mut self, instruction: MirInstruction) -> Result<(), String> { let block_id = self.current_block.ok_or("No current basic block")?; - + if let Some(ref mut function) = self.current_function { if let Some(block) = function.get_block_mut(block_id) { if utils::builder_debug_enabled() { - eprintln!("[BUILDER] emit @bb{} -> {}", block_id, match &instruction { - MirInstruction::TypeOp { dst, op, value, ty } => format!("typeop {:?} {} {:?} -> {}", op, value, ty, dst), - MirInstruction::Print { value, .. } => format!("print {}", value), - MirInstruction::BoxCall { box_val, method, method_id, args, dst, .. } => { - if let Some(mid) = method_id { - format!("boxcall {}.{}[#{}]({:?}) -> {:?}", box_val, method, mid, args, dst) - } else { - format!("boxcall {}.{}({:?}) -> {:?}", box_val, method, args, dst) + eprintln!( + "[BUILDER] emit @bb{} -> {}", + block_id, + match &instruction { + MirInstruction::TypeOp { dst, op, value, ty } => + format!("typeop {:?} {} {:?} -> {}", op, value, ty, dst), + MirInstruction::Print { value, .. } => format!("print {}", value), + MirInstruction::BoxCall { + box_val, + method, + method_id, + args, + dst, + .. + } => { + if let Some(mid) = method_id { + format!( + "boxcall {}.{}[#{}]({:?}) -> {:?}", + box_val, method, mid, args, dst + ) + } else { + format!( + "boxcall {}.{}({:?}) -> {:?}", + box_val, method, args, dst + ) + } } - }, - MirInstruction::Call { func, args, dst, .. } => format!("call {}({:?}) -> {:?}", func, args, dst), - MirInstruction::NewBox { dst, box_type, args } => format!("new {}({:?}) -> {}", box_type, args, dst), - MirInstruction::Const { dst, value } => format!("const {:?} -> {}", value, dst), - MirInstruction::Branch { condition, then_bb, else_bb } => format!("br {}, {}, {}", condition, then_bb, else_bb), - MirInstruction::Jump { target } => format!("br {}", target), - _ => format!("{:?}", instruction), - }); + MirInstruction::Call { + func, args, dst, .. + } => format!("call {}({:?}) -> {:?}", func, args, dst), + MirInstruction::NewBox { + dst, + box_type, + args, + } => format!("new {}({:?}) -> {}", box_type, args, dst), + MirInstruction::Const { dst, value } => + format!("const {:?} -> {}", value, dst), + MirInstruction::Branch { + condition, + then_bb, + else_bb, + } => format!("br {}, {}, {}", condition, then_bb, else_bb), + MirInstruction::Jump { target } => format!("br {}", target), + _ => format!("{:?}", instruction), + } + ); } block.add_instruction(instruction); Ok(()) @@ -686,7 +886,10 @@ impl MirBuilder { .get_block_mut(block_id) .ok_or_else(|| format!("Basic block {} does not exist", block_id))?; if let Some(term) = &block.terminator { - if matches!(term, MirInstruction::Return { .. } | MirInstruction::Throw { .. }) { + if matches!( + term, + MirInstruction::Return { .. } | MirInstruction::Throw { .. } + ) { return Ok(()); } } @@ -704,34 +907,43 @@ impl MirBuilder { Err("No current function".to_string()) } } - + // moved to builder/utils.rs: ensure_block_exists - + // build_loop_statement_legacy moved to builder/stmts.rs - + // build_try_catch_statement_legacy moved to builder/stmts.rs - + // build_throw_statement_legacy moved to builder/stmts.rs - + // build_local_statement_legacy moved to builder/stmts.rs - + // build_return_statement_legacy moved to builder/stmts.rs - + // moved to builder/decls.rs: build_static_main_box - + // moved to builder/fields.rs: build_field_access - + /// Build new expression: new ClassName(arguments) - pub(super) fn build_new_expression(&mut self, class: String, arguments: Vec) -> Result { + pub(super) fn build_new_expression( + &mut self, + class: String, + arguments: Vec, + ) -> Result { // Phase 9.78a: Unified Box creation using NewBox instruction // Core-13 pure mode: emit ExternCall(env.box.new) with type name const only if crate::config::env::mir_core13_pure() { // Emit Const String for type name let ty_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: ty_id, value: ConstValue::String(class.clone()) })?; + self.emit_instruction(MirInstruction::Const { + dst: ty_id, + value: ConstValue::String(class.clone()), + })?; // Evaluate arguments (pass through to env.box.new shim) let mut arg_vals: Vec = Vec::with_capacity(arguments.len()); - for a in arguments { arg_vals.push(self.build_expression(a)?); } + for a in arguments { + arg_vals.push(self.build_expression(a)?); + } // Build arg list: [type, a1, a2, ...] let mut args: Vec = Vec::with_capacity(1 + arg_vals.len()); args.push(ty_id); @@ -739,33 +951,45 @@ impl MirBuilder { // Call env.box.new let dst = self.value_gen.next(); self.emit_instruction(MirInstruction::ExternCall { - dst: Some(dst), iface_name: "env.box".to_string(), method_name: "new".to_string(), args, effects: EffectMask::PURE, + dst: Some(dst), + iface_name: "env.box".to_string(), + method_name: "new".to_string(), + args, + effects: EffectMask::PURE, })?; // 型注釈(最小) - self.value_types.insert(dst, super::MirType::Box(class.clone())); + self.value_types + .insert(dst, super::MirType::Box(class.clone())); return Ok(dst); } - + // Optimization: Primitive wrappers → emit Const directly when possible if class == "IntegerBox" && arguments.len() == 1 { - if let ASTNode::Literal { value: LiteralValue::Integer(n), .. } = arguments[0].clone() { + if let ASTNode::Literal { + value: LiteralValue::Integer(n), + .. + } = arguments[0].clone() + { let dst = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Integer(n) })?; + self.emit_instruction(MirInstruction::Const { + dst, + value: ConstValue::Integer(n), + })?; self.value_types.insert(dst, super::MirType::Integer); return Ok(dst); } } - + // First, evaluate all arguments to get their ValueIds let mut arg_values = Vec::new(); for arg in arguments { let arg_value = self.build_expression(arg)?; arg_values.push(arg_value); } - + // Generate the destination ValueId let dst = self.value_gen.next(); - + // Emit NewBox instruction for all Box types // VM will handle optimization for basic types internally self.emit_instruction(MirInstruction::NewBox { @@ -775,11 +999,22 @@ impl MirBuilder { })?; // Annotate primitive boxes match class.as_str() { - "IntegerBox" => { self.value_types.insert(dst, super::MirType::Integer); }, - "FloatBox" => { self.value_types.insert(dst, super::MirType::Float); }, - "BoolBox" => { self.value_types.insert(dst, super::MirType::Bool); }, - "StringBox" => { self.value_types.insert(dst, super::MirType::String); }, - other => { self.value_types.insert(dst, super::MirType::Box(other.to_string())); } + "IntegerBox" => { + self.value_types.insert(dst, super::MirType::Integer); + } + "FloatBox" => { + self.value_types.insert(dst, super::MirType::Float); + } + "BoolBox" => { + self.value_types.insert(dst, super::MirType::Bool); + } + "StringBox" => { + self.value_types.insert(dst, super::MirType::String); + } + other => { + self.value_types + .insert(dst, super::MirType::Box(other.to_string())); + } } // Record origin for optimization: dst was created by NewBox of class @@ -798,14 +1033,14 @@ impl MirBuilder { EffectMask::READ.add(Effect::ReadHeap), )?; } - + Ok(dst) } - + // moved to builder/fields.rs: build_field_assignment - + // moved to builder/utils.rs: start_new_block - + /// Check if the current basic block is terminated fn is_current_block_terminated(&self) -> bool { if let (Some(block_id), Some(ref function)) = (self.current_block, &self.current_function) { @@ -815,16 +1050,16 @@ impl MirBuilder { } false } - + // convert_binary_operator moved to builder/ops.rs // convert_unary_operator moved to builder/ops.rs - + // build_nowait_statement_legacy moved to builder/stmts.rs - + // build_await_expression_legacy moved to builder/stmts.rs - + // build_me_expression_legacy moved to builder/stmts.rs - + // build_method_call_legacy removed (use builder_calls::build_method_call) // parse_type_name_to_mir_legacy removed (use builder_calls::parse_type_name_to_mir) @@ -832,7 +1067,7 @@ impl MirBuilder { // build_from_expression_legacy removed (use builder_calls::build_from_expression) // lower_static_method_as_function_legacy removed (use builder_calls::lower_static_method_as_function) - + // moved to builder/decls.rs: build_box_declaration } @@ -848,28 +1083,28 @@ impl Default for MirBuilder { mod tests { use super::*; use crate::ast::{ASTNode, LiteralValue, Span}; - + #[test] fn test_literal_building() { let mut builder = MirBuilder::new(); - + let ast = ASTNode::Literal { value: LiteralValue::Integer(42), span: Span::unknown(), }; - + let result = builder.build_module(ast); assert!(result.is_ok()); - + let module = result.unwrap(); assert_eq!(module.function_names().len(), 1); assert!(module.get_function("main").is_some()); } - + #[test] fn test_binary_op_building() { let mut builder = MirBuilder::new(); - + let ast = ASTNode::BinaryOp { left: Box::new(ASTNode::Literal { value: LiteralValue::Integer(10), @@ -882,22 +1117,22 @@ mod tests { }), span: Span::unknown(), }; - + let result = builder.build_module(ast); assert!(result.is_ok()); - + let module = result.unwrap(); let function = module.get_function("main").unwrap(); - + // Should have constants and binary operation let stats = function.stats(); assert!(stats.instruction_count >= 3); // 2 constants + 1 binop + 1 return } - + #[test] fn test_if_statement_building() { let mut builder = MirBuilder::new(); - + // Adapt test to current AST: If with statement bodies let ast = ASTNode::If { condition: Box::new(ASTNode::Literal { @@ -914,16 +1149,16 @@ mod tests { }]), span: Span::unknown(), }; - + let result = builder.build_module(ast); assert!(result.is_ok()); - + let module = result.unwrap(); let function = module.get_function("main").unwrap(); - + // Should have multiple blocks for if/then/else/merge assert!(function.blocks.len() >= 3); - + // Should have phi function in merge block let stats = function.stats(); assert!(stats.phi_count >= 1); diff --git a/src/mir/builder/builder_calls.rs b/src/mir/builder/builder_calls.rs index 6c63379b..5cc27b30 100644 --- a/src/mir/builder/builder_calls.rs +++ b/src/mir/builder/builder_calls.rs @@ -1,21 +1,32 @@ // Extracted call-related builders from builder.rs to keep files lean -use super::{ - MirInstruction, FunctionSignature, EffectMask, Effect, MirType, ValueId, -}; -use crate::mir::{TypeOpKind, slot_registry}; +use super::{Effect, EffectMask, FunctionSignature, MirInstruction, MirType, ValueId}; use crate::ast::{ASTNode, LiteralValue}; +use crate::mir::{slot_registry, TypeOpKind}; impl super::MirBuilder { // Build function call: name(args) - pub(super) fn build_function_call(&mut self, name: String, args: Vec) -> Result { + pub(super) fn build_function_call( + &mut self, + name: String, + args: Vec, + ) -> Result { // Minimal TypeOp wiring via function-style: isType(value, "Type"), asType(value, "Type") if (name == "isType" || name == "asType") && args.len() == 2 { if let Some(type_name) = Self::extract_string_literal(&args[1]) { let val = self.build_expression(args[0].clone())?; let ty = Self::parse_type_name_to_mir(&type_name); let dst = self.value_gen.next(); - let op = if name == "isType" { TypeOpKind::Check } else { TypeOpKind::Cast }; - self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?; + let op = if name == "isType" { + TypeOpKind::Check + } else { + TypeOpKind::Cast + }; + self.emit_instruction(MirInstruction::TypeOp { + dst, + op, + value: val, + ty, + })?; return Ok(dst); } } @@ -31,26 +42,47 @@ impl super::MirBuilder { let mut math_args: Vec = Vec::new(); for a in raw_args.into_iter() { match a { - ASTNode::New { class, arguments, .. } if class == "FloatBox" && arguments.len() == 1 => { + ASTNode::New { + class, arguments, .. + } if class == "FloatBox" && arguments.len() == 1 => { let v = self.build_expression(arguments[0].clone())?; math_args.push(v); } - ASTNode::New { class, arguments, .. } if class == "IntegerBox" && arguments.len() == 1 => { + ASTNode::New { + class, arguments, .. + } if class == "IntegerBox" && arguments.len() == 1 => { let iv = self.build_expression(arguments[0].clone())?; let fv = self.value_gen.next(); - self.emit_instruction(MirInstruction::TypeOp { dst: fv, op: TypeOpKind::Cast, value: iv, ty: MirType::Float })?; + self.emit_instruction(MirInstruction::TypeOp { + dst: fv, + op: TypeOpKind::Cast, + value: iv, + ty: MirType::Float, + })?; math_args.push(fv); } - ASTNode::Literal { value: LiteralValue::Float(_), .. } => { - let v = self.build_expression(a)?; math_args.push(v); + ASTNode::Literal { + value: LiteralValue::Float(_), + .. + } => { + let v = self.build_expression(a)?; + math_args.push(v); + } + other => { + let v = self.build_expression(other)?; + math_args.push(v); } - other => { let v = self.build_expression(other)?; math_args.push(v); } } } // new MathBox() let math_recv = self.value_gen.next(); - self.emit_instruction(MirInstruction::NewBox { dst: math_recv, box_type: "MathBox".to_string(), args: vec![] })?; - self.value_origin_newbox.insert(math_recv, "MathBox".to_string()); + self.emit_instruction(MirInstruction::NewBox { + dst: math_recv, + box_type: "MathBox".to_string(), + args: vec![], + })?; + self.value_origin_newbox + .insert(math_recv, "MathBox".to_string()); // birth() let birt_mid = slot_registry::resolve_slot_by_type_name("MathBox", "birth"); self.emit_box_or_plugin_call( @@ -75,37 +107,71 @@ impl super::MirBuilder { // Default: call via fully-qualified function name string let mut arg_values = Vec::new(); - for a in args { arg_values.push(self.build_expression(a)?); } + for a in args { + arg_values.push(self.build_expression(a)?); + } let fun_val = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: fun_val, value: super::ConstValue::String(name) })?; - self.emit_instruction(MirInstruction::Call { dst: Some(dst), func: fun_val, args: arg_values, effects: EffectMask::READ.add(Effect::ReadHeap) })?; + self.emit_instruction(MirInstruction::Const { + dst: fun_val, + value: super::ConstValue::String(name), + })?; + self.emit_instruction(MirInstruction::Call { + dst: Some(dst), + func: fun_val, + args: arg_values, + effects: EffectMask::READ.add(Effect::ReadHeap), + })?; Ok(dst) } // Build method call: object.method(arguments) - pub(super) fn build_method_call(&mut self, object: ASTNode, method: String, arguments: Vec) -> Result { + pub(super) fn build_method_call( + &mut self, + object: ASTNode, + method: String, + arguments: Vec, + ) -> Result { // Minimal TypeOp wiring via method-style syntax: value.is("Type") / value.as("Type") if (method == "is" || method == "as") && arguments.len() == 1 { if let Some(type_name) = Self::extract_string_literal(&arguments[0]) { let object_value = self.build_expression(object.clone())?; let mir_ty = Self::parse_type_name_to_mir(&type_name); let dst = self.value_gen.next(); - let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast }; - self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?; + let op = if method == "is" { + TypeOpKind::Check + } else { + TypeOpKind::Cast + }; + self.emit_instruction(MirInstruction::TypeOp { + dst, + op, + value: object_value, + ty: mir_ty, + })?; return Ok(dst); } } // ExternCall: env.X.* pattern via field access (e.g., env.future.delay) - if let ASTNode::FieldAccess { object: env_obj, field: env_field, .. } = object.clone() { + if let ASTNode::FieldAccess { + object: env_obj, + field: env_field, + .. + } = object.clone() + { if let ASTNode::Variable { name: env_name, .. } = *env_obj { if env_name == "env" { let mut arg_values = Vec::new(); - for arg in &arguments { arg_values.push(self.build_expression(arg.clone())?); } + for arg in &arguments { + arg_values.push(self.build_expression(arg.clone())?); + } match (env_field.as_str(), method.as_str()) { ("future", "delay") => { let result_id = self.value_gen.next(); self.emit_instruction(MirInstruction::ExternCall { - dst: Some(result_id), iface_name: "env.future".to_string(), method_name: "delay".to_string(), args: arg_values, + dst: Some(result_id), + iface_name: "env.future".to_string(), + method_name: "delay".to_string(), + args: arg_values, effects: EffectMask::READ.add(Effect::Io), })?; return Ok(result_id); @@ -113,42 +179,68 @@ impl super::MirBuilder { ("task", "currentToken") => { let result_id = self.value_gen.next(); self.emit_instruction(MirInstruction::ExternCall { - dst: Some(result_id), iface_name: "env.task".to_string(), method_name: "currentToken".to_string(), args: arg_values, + dst: Some(result_id), + iface_name: "env.task".to_string(), + method_name: "currentToken".to_string(), + args: arg_values, effects: EffectMask::READ, })?; return Ok(result_id); } ("task", "cancelCurrent") => { self.emit_instruction(MirInstruction::ExternCall { - dst: None, iface_name: "env.task".to_string(), method_name: "cancelCurrent".to_string(), args: arg_values, + dst: None, + iface_name: "env.task".to_string(), + method_name: "cancelCurrent".to_string(), + args: arg_values, effects: EffectMask::IO, })?; let void_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: void_id, value: super::ConstValue::Void })?; + self.emit_instruction(MirInstruction::Const { + dst: void_id, + value: super::ConstValue::Void, + })?; return Ok(void_id); } ("console", "log") => { self.emit_instruction(MirInstruction::ExternCall { - dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: arg_values, effects: EffectMask::IO, + dst: None, + iface_name: "env.console".to_string(), + method_name: "log".to_string(), + args: arg_values, + effects: EffectMask::IO, })?; let void_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: void_id, value: super::ConstValue::Void })?; + self.emit_instruction(MirInstruction::Const { + dst: void_id, + value: super::ConstValue::Void, + })?; return Ok(void_id); } ("console", "readLine") => { let result_id = self.value_gen.next(); self.emit_instruction(MirInstruction::ExternCall { - dst: Some(result_id), iface_name: "env.console".to_string(), method_name: "readLine".to_string(), args: arg_values, + dst: Some(result_id), + iface_name: "env.console".to_string(), + method_name: "readLine".to_string(), + args: arg_values, effects: EffectMask::IO, })?; return Ok(result_id); } ("canvas", m @ ("fillRect" | "fillText")) => { self.emit_instruction(MirInstruction::ExternCall { - dst: None, iface_name: "env.canvas".to_string(), method_name: m.to_string(), args: arg_values, effects: EffectMask::IO, + dst: None, + iface_name: "env.canvas".to_string(), + method_name: m.to_string(), + args: arg_values, + effects: EffectMask::IO, })?; let void_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: void_id, value: super::ConstValue::Void })?; + self.emit_instruction(MirInstruction::Const { + dst: void_id, + value: super::ConstValue::Void, + })?; return Ok(void_id); } _ => {} @@ -160,12 +252,27 @@ impl super::MirBuilder { if let ASTNode::Me { .. } = object { if let Some(cls_name) = self.current_static_box.clone() { let mut arg_values: Vec = Vec::new(); - for a in &arguments { arg_values.push(self.build_expression(a.clone())?); } + for a in &arguments { + arg_values.push(self.build_expression(a.clone())?); + } let result_id = self.value_gen.next(); - let fun_name = format!("{}.{}{}", cls_name, method, format!("/{}", arg_values.len())); + let fun_name = format!( + "{}.{}{}", + cls_name, + method, + format!("/{}", arg_values.len()) + ); let fun_val = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: fun_val, value: super::ConstValue::String(fun_name) })?; - self.emit_instruction(MirInstruction::Call { dst: Some(result_id), func: fun_val, args: arg_values, effects: EffectMask::READ.add(Effect::ReadHeap) })?; + self.emit_instruction(MirInstruction::Const { + dst: fun_val, + value: super::ConstValue::String(fun_name), + })?; + self.emit_instruction(MirInstruction::Call { + dst: Some(result_id), + func: fun_val, + args: arg_values, + effects: EffectMask::READ.add(Effect::ReadHeap), + })?; return Ok(result_id); } } @@ -176,16 +283,34 @@ impl super::MirBuilder { if let Some(type_name) = Self::extract_string_literal(&arguments[0]) { let mir_ty = Self::parse_type_name_to_mir(&type_name); let dst = self.value_gen.next(); - let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast }; - self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?; + let op = if method == "is" { + TypeOpKind::Check + } else { + TypeOpKind::Cast + }; + self.emit_instruction(MirInstruction::TypeOp { + dst, + op, + value: object_value, + ty: mir_ty, + })?; return Ok(dst); } } // Fallback: generic plugin invoke let mut arg_values: Vec = Vec::new(); - for a in &arguments { arg_values.push(self.build_expression(a.clone())?); } + for a in &arguments { + arg_values.push(self.build_expression(a.clone())?); + } let result_id = self.value_gen.next(); - self.emit_box_or_plugin_call(Some(result_id), object_value, method, None, arg_values, EffectMask::READ.add(Effect::ReadHeap))?; + self.emit_box_or_plugin_call( + Some(result_id), + object_value, + method, + None, + arg_values, + EffectMask::READ.add(Effect::ReadHeap), + )?; Ok(result_id) } @@ -206,8 +331,13 @@ impl super::MirBuilder { let mut cur = node; loop { match cur { - ASTNode::Literal { value: LiteralValue::String(s), .. } => return Some(s.clone()), - ASTNode::New { class, arguments, .. } if class == "StringBox" && arguments.len() == 1 => { + ASTNode::Literal { + value: LiteralValue::String(s), + .. + } => return Some(s.clone()), + ASTNode::New { + class, arguments, .. + } if class == "StringBox" && arguments.len() == 1 => { cur = &arguments[0]; continue; } @@ -217,46 +347,188 @@ impl super::MirBuilder { } // Build from expression: from Parent.method(arguments) - pub(super) fn build_from_expression(&mut self, parent: String, method: String, arguments: Vec) -> Result { + pub(super) fn build_from_expression( + &mut self, + parent: String, + method: String, + arguments: Vec, + ) -> Result { let mut arg_values = Vec::new(); - for arg in arguments { arg_values.push(self.build_expression(arg)?); } + for arg in arguments { + arg_values.push(self.build_expression(arg)?); + } let parent_value = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: parent_value, value: super::ConstValue::String(parent) })?; + self.emit_instruction(MirInstruction::Const { + dst: parent_value, + value: super::ConstValue::String(parent), + })?; let result_id = self.value_gen.next(); - self.emit_box_or_plugin_call(Some(result_id), parent_value, method, None, arg_values, EffectMask::READ.add(Effect::ReadHeap))?; + self.emit_box_or_plugin_call( + Some(result_id), + parent_value, + method, + None, + arg_values, + EffectMask::READ.add(Effect::ReadHeap), + )?; Ok(result_id) } // Lower a box method into a standalone MIR function (with `me` parameter) - pub(super) fn lower_method_as_function(&mut self, func_name: String, box_name: String, params: Vec, body: Vec) -> Result<(), String> { - let mut param_types = Vec::new(); param_types.push(MirType::Box(box_name.clone())); for _ in ¶ms { param_types.push(MirType::Unknown); } - let mut returns_value = false; for st in &body { if let ASTNode::Return { value: Some(_), .. } = st { returns_value = true; break; } } - let ret_ty = if returns_value { MirType::Unknown } else { MirType::Void }; - let signature = FunctionSignature { name: func_name, params: param_types, return_type: ret_ty, effects: EffectMask::READ.add(Effect::ReadHeap) }; - let entry = self.block_gen.next(); let function = super::MirFunction::new(signature, entry); - let saved_function = self.current_function.take(); let saved_block = self.current_block.take(); - let saved_var_map = std::mem::take(&mut self.variable_map); let saved_value_gen = self.value_gen.clone(); - self.value_gen.reset(); self.current_function = Some(function); self.current_block = Some(entry); self.ensure_block_exists(entry)?; - if let Some(ref mut f) = self.current_function { let me_id = self.value_gen.next(); f.params.push(me_id); self.variable_map.insert("me".to_string(), me_id); self.value_origin_newbox.insert(me_id, box_name.clone()); for p in ¶ms { let pid = self.value_gen.next(); f.params.push(pid); self.variable_map.insert(p.clone(), pid); } } - let program_ast = ASTNode::Program { statements: body, span: crate::ast::Span::unknown() }; let _last = self.build_expression(program_ast)?; - if let Some(ref mut f) = self.current_function { if let Some(block) = f.get_block(self.current_block.unwrap()) { if !block.is_terminated() { let void_val = self.value_gen.next(); self.emit_instruction(MirInstruction::Const { dst: void_val, value: super::ConstValue::Void })?; self.emit_instruction(MirInstruction::Return { value: Some(void_val) })?; } } } - let finalized_function = self.current_function.take().unwrap(); if let Some(ref mut module) = self.current_module { module.add_function(finalized_function); } - self.current_function = saved_function; self.current_block = saved_block; self.variable_map = saved_var_map; self.value_gen = saved_value_gen; Ok(()) + pub(super) fn lower_method_as_function( + &mut self, + func_name: String, + box_name: String, + params: Vec, + body: Vec, + ) -> Result<(), String> { + let mut param_types = Vec::new(); + param_types.push(MirType::Box(box_name.clone())); + for _ in ¶ms { + param_types.push(MirType::Unknown); + } + let mut returns_value = false; + for st in &body { + if let ASTNode::Return { value: Some(_), .. } = st { + returns_value = true; + break; + } + } + let ret_ty = if returns_value { + MirType::Unknown + } else { + MirType::Void + }; + let signature = FunctionSignature { + name: func_name, + params: param_types, + return_type: ret_ty, + effects: EffectMask::READ.add(Effect::ReadHeap), + }; + let entry = self.block_gen.next(); + let function = super::MirFunction::new(signature, entry); + let saved_function = self.current_function.take(); + let saved_block = self.current_block.take(); + let saved_var_map = std::mem::take(&mut self.variable_map); + let saved_value_gen = self.value_gen.clone(); + self.value_gen.reset(); + self.current_function = Some(function); + self.current_block = Some(entry); + self.ensure_block_exists(entry)?; + if let Some(ref mut f) = self.current_function { + let me_id = self.value_gen.next(); + f.params.push(me_id); + self.variable_map.insert("me".to_string(), me_id); + self.value_origin_newbox.insert(me_id, box_name.clone()); + for p in ¶ms { + let pid = self.value_gen.next(); + f.params.push(pid); + self.variable_map.insert(p.clone(), pid); + } + } + let program_ast = ASTNode::Program { + statements: body, + span: crate::ast::Span::unknown(), + }; + let _last = self.build_expression(program_ast)?; + if let Some(ref mut f) = self.current_function { + if let Some(block) = f.get_block(self.current_block.unwrap()) { + if !block.is_terminated() { + let void_val = self.value_gen.next(); + self.emit_instruction(MirInstruction::Const { + dst: void_val, + value: super::ConstValue::Void, + })?; + self.emit_instruction(MirInstruction::Return { + value: Some(void_val), + })?; + } + } + } + let finalized_function = self.current_function.take().unwrap(); + if let Some(ref mut module) = self.current_module { + module.add_function(finalized_function); + } + self.current_function = saved_function; + self.current_block = saved_block; + self.variable_map = saved_var_map; + self.value_gen = saved_value_gen; + Ok(()) } // Lower a static method body into a standalone MIR function (no `me` parameter) - pub(super) fn lower_static_method_as_function(&mut self, func_name: String, params: Vec, body: Vec) -> Result<(), String> { - let mut param_types = Vec::new(); for _ in ¶ms { param_types.push(MirType::Unknown); } - let mut returns_value = false; for st in &body { if let ASTNode::Return { value: Some(_), .. } = st { returns_value = true; break; } } - let ret_ty = if returns_value { MirType::Unknown } else { MirType::Void }; - let signature = FunctionSignature { name: func_name, params: param_types, return_type: ret_ty, effects: EffectMask::READ.add(Effect::ReadHeap) }; - let entry = self.block_gen.next(); let function = super::MirFunction::new(signature, entry); - let saved_function = self.current_function.take(); let saved_block = self.current_block.take(); let saved_var_map = std::mem::take(&mut self.variable_map); let saved_value_gen = self.value_gen.clone(); self.value_gen.reset(); - self.current_function = Some(function); self.current_block = Some(entry); self.ensure_block_exists(entry)?; - if let Some(ref mut f) = self.current_function { for p in ¶ms { let pid = self.value_gen.next(); f.params.push(pid); self.variable_map.insert(p.clone(), pid); } } - let program_ast = ASTNode::Program { statements: body, span: crate::ast::Span::unknown() }; let _last = self.build_expression(program_ast)?; - if let Some(ref mut f) = self.current_function { if let Some(block) = f.get_block(self.current_block.unwrap()) { if !block.is_terminated() { let void_val = self.value_gen.next(); self.emit_instruction(MirInstruction::Const { dst: void_val, value: super::ConstValue::Void })?; self.emit_instruction(MirInstruction::Return { value: Some(void_val) })?; } } } - let finalized = self.current_function.take().unwrap(); if let Some(ref mut module) = self.current_module { module.add_function(finalized); } - self.current_function = saved_function; self.current_block = saved_block; self.variable_map = saved_var_map; self.value_gen = saved_value_gen; Ok(()) + pub(super) fn lower_static_method_as_function( + &mut self, + func_name: String, + params: Vec, + body: Vec, + ) -> Result<(), String> { + let mut param_types = Vec::new(); + for _ in ¶ms { + param_types.push(MirType::Unknown); + } + let mut returns_value = false; + for st in &body { + if let ASTNode::Return { value: Some(_), .. } = st { + returns_value = true; + break; + } + } + let ret_ty = if returns_value { + MirType::Unknown + } else { + MirType::Void + }; + let signature = FunctionSignature { + name: func_name, + params: param_types, + return_type: ret_ty, + effects: EffectMask::READ.add(Effect::ReadHeap), + }; + let entry = self.block_gen.next(); + let function = super::MirFunction::new(signature, entry); + let saved_function = self.current_function.take(); + let saved_block = self.current_block.take(); + let saved_var_map = std::mem::take(&mut self.variable_map); + let saved_value_gen = self.value_gen.clone(); + self.value_gen.reset(); + self.current_function = Some(function); + self.current_block = Some(entry); + self.ensure_block_exists(entry)?; + if let Some(ref mut f) = self.current_function { + for p in ¶ms { + let pid = self.value_gen.next(); + f.params.push(pid); + self.variable_map.insert(p.clone(), pid); + } + } + let program_ast = ASTNode::Program { + statements: body, + span: crate::ast::Span::unknown(), + }; + let _last = self.build_expression(program_ast)?; + if let Some(ref mut f) = self.current_function { + if let Some(block) = f.get_block(self.current_block.unwrap()) { + if !block.is_terminated() { + let void_val = self.value_gen.next(); + self.emit_instruction(MirInstruction::Const { + dst: void_val, + value: super::ConstValue::Void, + })?; + self.emit_instruction(MirInstruction::Return { + value: Some(void_val), + })?; + } + } + } + let finalized = self.current_function.take().unwrap(); + if let Some(ref mut module) = self.current_module { + module.add_function(finalized); + } + self.current_function = saved_function; + self.current_block = saved_block; + self.variable_map = saved_var_map; + self.value_gen = saved_value_gen; + Ok(()) } } diff --git a/src/mir/builder/decls.rs b/src/mir/builder/decls.rs index 70e7d523..eb623690 100644 --- a/src/mir/builder/decls.rs +++ b/src/mir/builder/decls.rs @@ -1,8 +1,8 @@ // Declarations lowering: static boxes and box declarations -use super::{MirInstruction, ConstValue, ValueId}; +use super::{ConstValue, MirInstruction, ValueId}; use crate::ast::ASTNode; -use std::collections::HashSet; use crate::mir::slot_registry::{get_or_assign_type_id, reserve_method_slot}; +use std::collections::HashSet; impl super::MirBuilder { /// Build static box (e.g., Main) - extracts main() method body and converts to Program @@ -14,7 +14,9 @@ impl super::MirBuilder { ) -> Result { // Lower other static methods (except main) to standalone MIR functions so JIT can see them for (mname, mast) in methods.iter() { - if mname == "main" { continue; } + if mname == "main" { + continue; + } if let ASTNode::FunctionDeclaration { params, body, .. } = mast { let func_name = format!("{}.{}{}", box_name, mname, format!("/{}", params.len())); self.lower_static_method_as_function(func_name, params.clone(), body.clone())?; @@ -27,16 +29,26 @@ impl super::MirBuilder { let out = if let Some(main_method) = methods.get("main") { if let ASTNode::FunctionDeclaration { params, body, .. } = main_method { // Convert the method body to a Program AST node and lower it - let program_ast = ASTNode::Program { statements: body.clone(), span: crate::ast::Span::unknown() }; + let program_ast = ASTNode::Program { + statements: body.clone(), + span: crate::ast::Span::unknown(), + }; // Bind default parameters if present (e.g., args=[]) let saved_var_map = std::mem::take(&mut self.variable_map); for p in params.iter() { let pid = self.value_gen.next(); if p == "args" { // new ArrayBox() with no args - self.emit_instruction(MirInstruction::NewBox { dst: pid, box_type: "ArrayBox".to_string(), args: vec![] })?; + self.emit_instruction(MirInstruction::NewBox { + dst: pid, + box_type: "ArrayBox".to_string(), + args: vec![], + })?; } else { - self.emit_instruction(MirInstruction::Const { dst: pid, value: ConstValue::Void })?; + self.emit_instruction(MirInstruction::Const { + dst: pid, + value: ConstValue::Void, + })?; } self.variable_map.insert(p.clone(), pid); } @@ -64,12 +76,18 @@ impl super::MirBuilder { ) -> Result<(), String> { // Create a type registration constant (marker) let type_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: type_id, value: ConstValue::String(format!("__box_type_{}", name)) })?; + self.emit_instruction(MirInstruction::Const { + dst: type_id, + value: ConstValue::String(format!("__box_type_{}", name)), + })?; // Emit field metadata markers for field in fields { let field_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: field_id, value: ConstValue::String(format!("__field_{}_{}", name, field)) })?; + self.emit_instruction(MirInstruction::Const { + dst: field_id, + value: ConstValue::String(format!("__field_{}_{}", name, field)), + })?; } // Record weak fields for this box @@ -82,7 +100,9 @@ impl super::MirBuilder { let mut instance_methods: Vec = Vec::new(); for (mname, mast) in &methods { if let ASTNode::FunctionDeclaration { is_static, .. } = mast { - if !*is_static { instance_methods.push(mname.clone()); } + if !*is_static { + instance_methods.push(mname.clone()); + } } } instance_methods.sort(); @@ -98,7 +118,10 @@ impl super::MirBuilder { for (method_name, method_ast) in methods { if let ASTNode::FunctionDeclaration { .. } = method_ast { let method_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: method_id, value: ConstValue::String(format!("__method_{}_{}", name, method_name)) })?; + self.emit_instruction(MirInstruction::Const { + dst: method_id, + value: ConstValue::String(format!("__method_{}_{}", name, method_name)), + })?; } } diff --git a/src/mir/builder/exprs.rs b/src/mir/builder/exprs.rs index c03fc134..0088b4b2 100644 --- a/src/mir/builder/exprs.rs +++ b/src/mir/builder/exprs.rs @@ -1,27 +1,34 @@ // Expression lowering split from builder.rs to keep files lean -use super::{MirInstruction, ConstValue, ValueId}; +use super::{ConstValue, MirInstruction, ValueId}; use crate::ast::ASTNode; impl super::MirBuilder { // Main expression dispatcher pub(super) fn build_expression_impl(&mut self, ast: ASTNode) -> Result { - if matches!(ast, + if matches!( + ast, ASTNode::Program { .. } - | ASTNode::Print { .. } - | ASTNode::If { .. } - | ASTNode::Loop { .. } - | ASTNode::TryCatch { .. } - | ASTNode::Throw { .. } + | ASTNode::Print { .. } + | ASTNode::If { .. } + | ASTNode::Loop { .. } + | ASTNode::TryCatch { .. } + | ASTNode::Throw { .. } ) { return self.build_expression_impl_legacy(ast); } match ast { ASTNode::Literal { value, .. } => self.build_literal(value), - ASTNode::BinaryOp { left, operator, right, .. } => - self.build_binary_op(*left, operator, *right), + ASTNode::BinaryOp { + left, + operator, + right, + .. + } => self.build_binary_op(*left, operator, *right), - ASTNode::UnaryOp { operator, operand, .. } => { + ASTNode::UnaryOp { + operator, operand, .. + } => { let op_string = match operator { crate::ast::UnaryOperator::Minus => "-".to_string(), crate::ast::UnaryOperator::Not => "not".to_string(), @@ -33,22 +40,40 @@ impl super::MirBuilder { ASTNode::Me { .. } => self.build_me_expression(), - ASTNode::MethodCall { object, method, arguments, .. } => { + ASTNode::MethodCall { + object, + method, + arguments, + .. + } => { if (method == "is" || method == "as") && arguments.len() == 1 { if let Some(type_name) = Self::extract_string_literal(&arguments[0]) { let obj_val = self.build_expression_impl(*object.clone())?; let ty = Self::parse_type_name_to_mir(&type_name); let dst = self.value_gen.next(); - let op = if method == "is" { crate::mir::TypeOpKind::Check } else { crate::mir::TypeOpKind::Cast }; - self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?; + let op = if method == "is" { + crate::mir::TypeOpKind::Check + } else { + crate::mir::TypeOpKind::Cast + }; + self.emit_instruction(MirInstruction::TypeOp { + dst, + op, + value: obj_val, + ty, + })?; return Ok(dst); } } self.build_method_call(*object.clone(), method.clone(), arguments.clone()) } - ASTNode::FromCall { parent, method, arguments, .. } => - self.build_from_expression(parent.clone(), method.clone(), arguments.clone()), + ASTNode::FromCall { + parent, + method, + arguments, + .. + } => self.build_from_expression(parent.clone(), method.clone(), arguments.clone()), ASTNode::Assignment { target, value, .. } => { if let ASTNode::FieldAccess { object, field, .. } = target.as_ref() { @@ -60,116 +85,212 @@ impl super::MirBuilder { } } - ASTNode::FunctionCall { name, arguments, .. } => - self.build_function_call(name.clone(), arguments.clone()), + ASTNode::FunctionCall { + name, arguments, .. + } => self.build_function_call(name.clone(), arguments.clone()), - ASTNode::Call { callee, arguments, .. } => - self.build_indirect_call_expression(*callee.clone(), arguments.clone()), + ASTNode::Call { + callee, arguments, .. + } => self.build_indirect_call_expression(*callee.clone(), arguments.clone()), - ASTNode::QMarkPropagate { expression, .. } => - self.build_qmark_propagate_expression(*expression.clone()), + ASTNode::QMarkPropagate { expression, .. } => { + self.build_qmark_propagate_expression(*expression.clone()) + } - ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => - self.build_peek_expression(*scrutinee.clone(), arms.clone(), *else_expr.clone()), + ASTNode::PeekExpr { + scrutinee, + arms, + else_expr, + .. + } => self.build_peek_expression(*scrutinee.clone(), arms.clone(), *else_expr.clone()), - ASTNode::Lambda { params, body, .. } => - self.build_lambda_expression(params.clone(), body.clone()), + ASTNode::Lambda { params, body, .. } => { + self.build_lambda_expression(params.clone(), body.clone()) + } ASTNode::Return { value, .. } => self.build_return_statement(value.clone()), // Control flow: break/continue are handled inside LoopBuilder context + ASTNode::Local { + variables, + initial_values, + .. + } => self.build_local_statement(variables.clone(), initial_values.clone()), - ASTNode::Local { variables, initial_values, .. } => - self.build_local_statement(variables.clone(), initial_values.clone()), - - ASTNode::BoxDeclaration { name, methods, is_static, fields, constructors, weak_fields, .. } => { + ASTNode::BoxDeclaration { + name, + methods, + is_static, + fields, + constructors, + weak_fields, + .. + } => { if is_static && name == "Main" { self.build_static_main_box(name.clone(), methods.clone()) } else { self.user_defined_boxes.insert(name.clone()); - self.build_box_declaration(name.clone(), methods.clone(), fields.clone(), weak_fields.clone())?; + self.build_box_declaration( + name.clone(), + methods.clone(), + fields.clone(), + weak_fields.clone(), + )?; for (ctor_key, ctor_ast) in constructors.clone() { if let ASTNode::FunctionDeclaration { params, body, .. } = ctor_ast { let func_name = format!("{}.{}", name, ctor_key); - self.lower_method_as_function(func_name, name.clone(), params.clone(), body.clone())?; + self.lower_method_as_function( + func_name, + name.clone(), + params.clone(), + body.clone(), + )?; } } for (method_name, method_ast) in methods.clone() { - if let ASTNode::FunctionDeclaration { params, body, is_static, .. } = method_ast { + if let ASTNode::FunctionDeclaration { + params, + body, + is_static, + .. + } = method_ast + { if !is_static { - let func_name = format!("{}.{}{}", name, method_name, format!("/{}", params.len())); - self.lower_method_as_function(func_name, name.clone(), params.clone(), body.clone())?; + let func_name = format!( + "{}.{}{}", + name, + method_name, + format!("/{}", params.len()) + ); + self.lower_method_as_function( + func_name, + name.clone(), + params.clone(), + body.clone(), + )?; } } } let void_val = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: void_val, value: ConstValue::Void })?; + self.emit_instruction(MirInstruction::Const { + dst: void_val, + value: ConstValue::Void, + })?; Ok(void_val) } } - ASTNode::FieldAccess { object, field, .. } => - self.build_field_access(*object.clone(), field.clone()), + ASTNode::FieldAccess { object, field, .. } => { + self.build_field_access(*object.clone(), field.clone()) + } - ASTNode::New { class, arguments, .. } => - self.build_new_expression(class.clone(), arguments.clone()), + ASTNode::New { + class, arguments, .. + } => self.build_new_expression(class.clone(), arguments.clone()), ASTNode::ArrayLiteral { elements, .. } => { let arr_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::NewBox { dst: arr_id, box_type: "ArrayBox".to_string(), args: vec![] })?; + self.emit_instruction(MirInstruction::NewBox { + dst: arr_id, + box_type: "ArrayBox".to_string(), + args: vec![], + })?; for e in elements { let v = self.build_expression_impl(e)?; - self.emit_instruction(MirInstruction::BoxCall { dst: None, box_val: arr_id, method: "push".to_string(), method_id: None, args: vec![v], effects: super::EffectMask::MUT })?; + self.emit_instruction(MirInstruction::BoxCall { + dst: None, + box_val: arr_id, + method: "push".to_string(), + method_id: None, + args: vec![v], + effects: super::EffectMask::MUT, + })?; } Ok(arr_id) } ASTNode::MapLiteral { entries, .. } => { let map_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::NewBox { dst: map_id, box_type: "MapBox".to_string(), args: vec![] })?; + self.emit_instruction(MirInstruction::NewBox { + dst: map_id, + box_type: "MapBox".to_string(), + args: vec![], + })?; for (k, expr) in entries { // const string key let k_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: k_id, value: ConstValue::String(k) })?; + self.emit_instruction(MirInstruction::Const { + dst: k_id, + value: ConstValue::String(k), + })?; let v_id = self.build_expression_impl(expr)?; - self.emit_instruction(MirInstruction::BoxCall { dst: None, box_val: map_id, method: "set".to_string(), method_id: None, args: vec![k_id, v_id], effects: super::EffectMask::MUT })?; + self.emit_instruction(MirInstruction::BoxCall { + dst: None, + box_val: map_id, + method: "set".to_string(), + method_id: None, + args: vec![k_id, v_id], + effects: super::EffectMask::MUT, + })?; } Ok(map_id) } - ASTNode::Nowait { variable, expression, .. } => - self.build_nowait_statement(variable.clone(), *expression.clone()), + ASTNode::Nowait { + variable, + expression, + .. + } => self.build_nowait_statement(variable.clone(), *expression.clone()), - ASTNode::AwaitExpression { expression, .. } => - self.build_await_expression(*expression.clone()), + ASTNode::AwaitExpression { expression, .. } => { + self.build_await_expression(*expression.clone()) + } - ASTNode::Include { filename, .. } => - self.build_include_expression(filename.clone()), + ASTNode::Include { filename, .. } => self.build_include_expression(filename.clone()), - ASTNode::Program { statements, .. } => - self.build_block(statements.clone()), + ASTNode::Program { statements, .. } => self.build_block(statements.clone()), - ASTNode::Print { expression, .. } => - self.build_print_statement(*expression.clone()), + ASTNode::Print { expression, .. } => self.build_print_statement(*expression.clone()), - ASTNode::If { condition, then_body, else_body, .. } => { + ASTNode::If { + condition, + then_body, + else_body, + .. + } => { let else_ast = if let Some(else_statements) = else_body { - Some(ASTNode::Program { statements: else_statements.clone(), span: crate::ast::Span::unknown() }) - } else { None }; + Some(ASTNode::Program { + statements: else_statements.clone(), + span: crate::ast::Span::unknown(), + }) + } else { + None + }; self.build_if_statement( *condition.clone(), - ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown() }, + ASTNode::Program { + statements: then_body.clone(), + span: crate::ast::Span::unknown(), + }, else_ast, ) } - ASTNode::Loop { condition, body, .. } => - self.build_loop_statement(*condition.clone(), body.clone()), + ASTNode::Loop { + condition, body, .. + } => self.build_loop_statement(*condition.clone(), body.clone()), - ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => - self.build_try_catch_statement(try_body.clone(), catch_clauses.clone(), finally_body.clone()), + ASTNode::TryCatch { + try_body, + catch_clauses, + finally_body, + .. + } => self.build_try_catch_statement( + try_body.clone(), + catch_clauses.clone(), + finally_body.clone(), + ), - ASTNode::Throw { expression, .. } => - self.build_throw_statement(*expression.clone()), + ASTNode::Throw { expression, .. } => self.build_throw_statement(*expression.clone()), _ => Err(format!("Unsupported AST node type: {:?}", ast)), } diff --git a/src/mir/builder/exprs_call.rs b/src/mir/builder/exprs_call.rs index cc863594..2001911b 100644 --- a/src/mir/builder/exprs_call.rs +++ b/src/mir/builder/exprs_call.rs @@ -10,7 +10,9 @@ impl super::MirBuilder { ) -> Result { let callee_id = self.build_expression_impl(callee)?; let mut arg_ids: Vec = Vec::new(); - for a in arguments { arg_ids.push(self.build_expression_impl(a)?); } + for a in arguments { + arg_ids.push(self.build_expression_impl(a)?); + } let dst = self.value_gen.next(); self.emit_instruction(super::MirInstruction::Call { dst: Some(dst), @@ -21,4 +23,3 @@ impl super::MirBuilder { Ok(dst) } } - diff --git a/src/mir/builder/exprs_include.rs b/src/mir/builder/exprs_include.rs index c916a3cd..ef33064f 100644 --- a/src/mir/builder/exprs_include.rs +++ b/src/mir/builder/exprs_include.rs @@ -4,24 +4,43 @@ impl super::MirBuilder { // Include lowering: include "path" pub(super) fn build_include_expression(&mut self, filename: String) -> Result { let mut path = super::utils::resolve_include_path_builder(&filename); - if std::path::Path::new(&path).is_dir() { path = format!("{}/index.nyash", path.trim_end_matches('/')); } - else if std::path::Path::new(&path).extension().is_none() { path.push_str(".nyash"); } + if std::path::Path::new(&path).is_dir() { + path = format!("{}/index.nyash", path.trim_end_matches('/')); + } else if std::path::Path::new(&path).extension().is_none() { + path.push_str(".nyash"); + } - if self.include_loading.contains(&path) { return Err(format!("Circular include detected: {}", path)); } - if let Some(name) = self.include_box_map.get(&path).cloned() { return self.build_new_expression(name, vec![]); } + if self.include_loading.contains(&path) { + return Err(format!("Circular include detected: {}", path)); + } + if let Some(name) = self.include_box_map.get(&path).cloned() { + return self.build_new_expression(name, vec![]); + } self.include_loading.insert(path.clone()); - let content = std::fs::read_to_string(&path).map_err(|e| format!("Include read error '{}': {}", filename, e))?; - let included_ast = crate::parser::NyashParser::parse_from_string(&content).map_err(|e| format!("Include parse error '{}': {:?}", filename, e))?; + let content = std::fs::read_to_string(&path) + .map_err(|e| format!("Include read error '{}': {}", filename, e))?; + let included_ast = crate::parser::NyashParser::parse_from_string(&content) + .map_err(|e| format!("Include parse error '{}': {:?}", filename, e))?; let mut box_name: Option = None; if let crate::ast::ASTNode::Program { statements, .. } = &included_ast { - for st in statements { if let crate::ast::ASTNode::BoxDeclaration { name, is_static, .. } = st { if *is_static { box_name = Some(name.clone()); break; } } } + for st in statements { + if let crate::ast::ASTNode::BoxDeclaration { + name, is_static, .. + } = st + { + if *is_static { + box_name = Some(name.clone()); + break; + } + } + } } - let bname = box_name.ok_or_else(|| format!("Include target '{}' has no static box", filename))?; + let bname = + box_name.ok_or_else(|| format!("Include target '{}' has no static box", filename))?; let _ = self.build_expression_impl(included_ast)?; self.include_loading.remove(&path); self.include_box_map.insert(path.clone(), bname.clone()); self.build_new_expression(bname, vec![]) } } - diff --git a/src/mir/builder/exprs_lambda.rs b/src/mir/builder/exprs_lambda.rs index 48874f4f..56333cdd 100644 --- a/src/mir/builder/exprs_lambda.rs +++ b/src/mir/builder/exprs_lambda.rs @@ -11,57 +11,167 @@ impl super::MirBuilder { use std::collections::HashSet; let mut used: HashSet = HashSet::new(); let mut locals: HashSet = HashSet::new(); - for p in ¶ms { locals.insert(p.clone()); } - fn collect_vars(ast: &ASTNode, used: &mut std::collections::HashSet, locals: &mut std::collections::HashSet) { + for p in ¶ms { + locals.insert(p.clone()); + } + fn collect_vars( + ast: &ASTNode, + used: &mut std::collections::HashSet, + locals: &mut std::collections::HashSet, + ) { match ast { - ASTNode::Variable { name, .. } => { if !locals.contains(name) { used.insert(name.clone()); } } - ASTNode::Assignment { target, value, .. } => { collect_vars(target, used, locals); collect_vars(value, used, locals); } - ASTNode::BinaryOp { left, right, .. } => { collect_vars(left, used, locals); collect_vars(right, used, locals); } - ASTNode::UnaryOp { operand, .. } => { collect_vars(operand, used, locals); } - ASTNode::MethodCall { object, arguments, .. } => { collect_vars(object, used, locals); for a in arguments { collect_vars(a, used, locals); } } - ASTNode::FunctionCall { arguments, .. } => { for a in arguments { collect_vars(a, used, locals); } } - ASTNode::Call { callee, arguments, .. } => { collect_vars(callee, used, locals); for a in arguments { collect_vars(a, used, locals); } } - ASTNode::FieldAccess { object, .. } => { collect_vars(object, used, locals); } - ASTNode::New { arguments, .. } => { for a in arguments { collect_vars(a, used, locals); } } - ASTNode::If { condition, then_body, else_body, .. } => { + ASTNode::Variable { name, .. } => { + if !locals.contains(name) { + used.insert(name.clone()); + } + } + ASTNode::Assignment { target, value, .. } => { + collect_vars(target, used, locals); + collect_vars(value, used, locals); + } + ASTNode::BinaryOp { left, right, .. } => { + collect_vars(left, used, locals); + collect_vars(right, used, locals); + } + ASTNode::UnaryOp { operand, .. } => { + collect_vars(operand, used, locals); + } + ASTNode::MethodCall { + object, arguments, .. + } => { + collect_vars(object, used, locals); + for a in arguments { + collect_vars(a, used, locals); + } + } + ASTNode::FunctionCall { arguments, .. } => { + for a in arguments { + collect_vars(a, used, locals); + } + } + ASTNode::Call { + callee, arguments, .. + } => { + collect_vars(callee, used, locals); + for a in arguments { + collect_vars(a, used, locals); + } + } + ASTNode::FieldAccess { object, .. } => { + collect_vars(object, used, locals); + } + ASTNode::New { arguments, .. } => { + for a in arguments { + collect_vars(a, used, locals); + } + } + ASTNode::If { + condition, + then_body, + else_body, + .. + } => { collect_vars(condition, used, locals); - for st in then_body { collect_vars(st, used, locals); } - if let Some(eb) = else_body { for st in eb { collect_vars(st, used, locals); } } + for st in then_body { + collect_vars(st, used, locals); + } + if let Some(eb) = else_body { + for st in eb { + collect_vars(st, used, locals); + } + } } - ASTNode::Loop { condition, body, .. } => { collect_vars(condition, used, locals); for st in body { collect_vars(st, used, locals); } } - ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => { - for st in try_body { collect_vars(st, used, locals); } - for c in catch_clauses { for st in &c.body { collect_vars(st, used, locals); } } - if let Some(fb) = finally_body { for st in fb { collect_vars(st, used, locals); } } + ASTNode::Loop { + condition, body, .. + } => { + collect_vars(condition, used, locals); + for st in body { + collect_vars(st, used, locals); + } } - ASTNode::Throw { expression, .. } => { collect_vars(expression, used, locals); } - ASTNode::Print { expression, .. } => { collect_vars(expression, used, locals); } - ASTNode::Return { value, .. } => { if let Some(v) = value { collect_vars(v, used, locals); } } - ASTNode::AwaitExpression { expression, .. } => { collect_vars(expression, used, locals); } - ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => { + ASTNode::TryCatch { + try_body, + catch_clauses, + finally_body, + .. + } => { + for st in try_body { + collect_vars(st, used, locals); + } + for c in catch_clauses { + for st in &c.body { + collect_vars(st, used, locals); + } + } + if let Some(fb) = finally_body { + for st in fb { + collect_vars(st, used, locals); + } + } + } + ASTNode::Throw { expression, .. } => { + collect_vars(expression, used, locals); + } + ASTNode::Print { expression, .. } => { + collect_vars(expression, used, locals); + } + ASTNode::Return { value, .. } => { + if let Some(v) = value { + collect_vars(v, used, locals); + } + } + ASTNode::AwaitExpression { expression, .. } => { + collect_vars(expression, used, locals); + } + ASTNode::PeekExpr { + scrutinee, + arms, + else_expr, + .. + } => { collect_vars(scrutinee, used, locals); - for (_, e) in arms { collect_vars(e, used, locals); } + for (_, e) in arms { + collect_vars(e, used, locals); + } collect_vars(else_expr, used, locals); } - ASTNode::Program { statements, .. } => { for st in statements { collect_vars(st, used, locals); } } + ASTNode::Program { statements, .. } => { + for st in statements { + collect_vars(st, used, locals); + } + } ASTNode::FunctionDeclaration { params, body, .. } => { let mut inner = locals.clone(); - for p in params { inner.insert(p.clone()); } - for st in body { collect_vars(st, used, &mut inner); } + for p in params { + inner.insert(p.clone()); + } + for st in body { + collect_vars(st, used, &mut inner); + } } _ => {} } } - for st in body.iter() { collect_vars(st, &mut used, &mut locals); } + for st in body.iter() { + collect_vars(st, &mut used, &mut locals); + } let mut captures: Vec<(String, ValueId)> = Vec::new(); for name in used.into_iter() { - if let Some(&vid) = self.variable_map.get(&name) { captures.push((name, vid)); } + if let Some(&vid) = self.variable_map.get(&name) { + captures.push((name, vid)); + } } let me = self.variable_map.get("me").copied(); let dst = self.value_gen.next(); - self.emit_instruction(super::MirInstruction::FunctionNew { dst, params: params.clone(), body: body.clone(), captures, me })?; - self.value_types.insert(dst, crate::mir::MirType::Box("FunctionBox".to_string())); + self.emit_instruction(super::MirInstruction::FunctionNew { + dst, + params: params.clone(), + body: body.clone(), + captures, + me, + })?; + self.value_types + .insert(dst, crate::mir::MirType::Box("FunctionBox".to_string())); Ok(dst) } } - diff --git a/src/mir/builder/exprs_peek.rs b/src/mir/builder/exprs_peek.rs index a0bc96f5..a7ab4598 100644 --- a/src/mir/builder/exprs_peek.rs +++ b/src/mir/builder/exprs_peek.rs @@ -23,11 +23,19 @@ impl super::MirBuilder { let need_jump = { let cur = self.current_block; if let (Some(cb), Some(ref func)) = (cur, &self.current_function) { - if let Some(bb) = func.blocks.get(&cb) { !bb.is_terminated() } else { true } - } else { true } + if let Some(bb) = func.blocks.get(&cb) { + !bb.is_terminated() + } else { + true + } + } else { + true + } }; if need_jump { - self.emit_instruction(super::MirInstruction::Jump { target: dispatch_block })?; + self.emit_instruction(super::MirInstruction::Jump { + target: dispatch_block, + })?; } self.start_new_block(dispatch_block)?; @@ -38,14 +46,19 @@ impl super::MirBuilder { self.start_new_block(else_block)?; let else_val = self.build_expression_impl(else_expr)?; phi_inputs.push((else_block, else_val)); - self.emit_instruction(super::MirInstruction::Jump { target: merge_block })?; + self.emit_instruction(super::MirInstruction::Jump { + target: merge_block, + })?; self.start_new_block(merge_block)?; if self.is_no_phi_mode() { for (pred, val) in phi_inputs { self.insert_edge_copy(pred, result_val, val)?; } } else { - self.emit_instruction(super::MirInstruction::Phi { dst: result_val, inputs: phi_inputs })?; + self.emit_instruction(super::MirInstruction::Phi { + dst: result_val, + inputs: phi_inputs, + })?; } return Ok(result_val); } @@ -58,24 +71,42 @@ impl super::MirBuilder { for (i, (label, arm_expr)) in arms.iter().cloned().enumerate() { let then_block = self.block_gen.next(); // Next dispatch (only for non-last arm) - let next_dispatch = if i + 1 < arms.len() { Some(self.block_gen.next()) } else { None }; + let next_dispatch = if i + 1 < arms.len() { + Some(self.block_gen.next()) + } else { + None + }; let else_target = next_dispatch.unwrap_or(else_block); // In current dispatch block, compare and branch self.start_new_block(cur_dispatch)?; if let LiteralValue::String(s) = label { let lit_id = self.value_gen.next(); - self.emit_instruction(super::MirInstruction::Const { dst: lit_id, value: super::ConstValue::String(s) })?; + self.emit_instruction(super::MirInstruction::Const { + dst: lit_id, + value: super::ConstValue::String(s), + })?; let cond_id = self.value_gen.next(); - self.emit_instruction(super::MirInstruction::Compare { dst: cond_id, op: super::CompareOp::Eq, lhs: scr_val, rhs: lit_id })?; - self.emit_instruction(super::MirInstruction::Branch { condition: cond_id, then_bb: then_block, else_bb: else_target })?; + self.emit_instruction(super::MirInstruction::Compare { + dst: cond_id, + op: super::CompareOp::Eq, + lhs: scr_val, + rhs: lit_id, + })?; + self.emit_instruction(super::MirInstruction::Branch { + condition: cond_id, + then_bb: then_block, + else_bb: else_target, + })?; } // then arm self.start_new_block(then_block)?; let then_val = self.build_expression_impl(arm_expr)?; phi_inputs.push((then_block, then_val)); - self.emit_instruction(super::MirInstruction::Jump { target: merge_block })?; + self.emit_instruction(super::MirInstruction::Jump { + target: merge_block, + })?; // Move to next dispatch or else block cur_dispatch = else_target; @@ -85,7 +116,9 @@ impl super::MirBuilder { self.start_new_block(else_block)?; let else_val = self.build_expression_impl(else_expr)?; phi_inputs.push((else_block, else_val)); - self.emit_instruction(super::MirInstruction::Jump { target: merge_block })?; + self.emit_instruction(super::MirInstruction::Jump { + target: merge_block, + })?; // Merge and yield result self.start_new_block(merge_block)?; @@ -94,7 +127,10 @@ impl super::MirBuilder { self.insert_edge_copy(pred, result_val, val)?; } } else { - self.emit_instruction(super::MirInstruction::Phi { dst: result_val, inputs: phi_inputs })?; + self.emit_instruction(super::MirInstruction::Phi { + dst: result_val, + inputs: phi_inputs, + })?; } Ok(result_val) } diff --git a/src/mir/builder/exprs_qmark.rs b/src/mir/builder/exprs_qmark.rs index 7e096d10..9272fd18 100644 --- a/src/mir/builder/exprs_qmark.rs +++ b/src/mir/builder/exprs_qmark.rs @@ -3,7 +3,10 @@ use crate::ast::ASTNode; impl super::MirBuilder { // QMarkPropagate: result?.value (Result-like) - pub(super) fn build_qmark_propagate_expression(&mut self, expression: ASTNode) -> Result { + pub(super) fn build_qmark_propagate_expression( + &mut self, + expression: ASTNode, + ) -> Result { let res_val = self.build_expression_impl(expression)?; let ok_id = self.value_gen.next(); self.emit_instruction(super::MirInstruction::BoxCall { @@ -16,9 +19,15 @@ impl super::MirBuilder { })?; let then_block = self.block_gen.next(); let else_block = self.block_gen.next(); - self.emit_instruction(super::MirInstruction::Branch { condition: ok_id, then_bb: then_block, else_bb: else_block })?; + self.emit_instruction(super::MirInstruction::Branch { + condition: ok_id, + then_bb: then_block, + else_bb: else_block, + })?; self.start_new_block(then_block)?; - self.emit_instruction(super::MirInstruction::Return { value: Some(res_val) })?; + self.emit_instruction(super::MirInstruction::Return { + value: Some(res_val), + })?; self.start_new_block(else_block)?; let val_id = self.value_gen.next(); self.emit_instruction(super::MirInstruction::BoxCall { diff --git a/src/mir/builder/fields.rs b/src/mir/builder/fields.rs index e0154e68..2ce47205 100644 --- a/src/mir/builder/fields.rs +++ b/src/mir/builder/fields.rs @@ -1,17 +1,24 @@ // Field access and assignment lowering -use super::{MirInstruction, ConstValue, ValueId, EffectMask}; -use crate::mir::slot_registry; +use super::{ConstValue, EffectMask, MirInstruction, ValueId}; use crate::ast::ASTNode; +use crate::mir::slot_registry; impl super::MirBuilder { /// Build field access: object.field - pub(super) fn build_field_access(&mut self, object: ASTNode, field: String) -> Result { + pub(super) fn build_field_access( + &mut self, + object: ASTNode, + field: String, + ) -> Result { let object_clone = object.clone(); let object_value = self.build_expression(object)?; // Emit: field name const let field_name_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: field_name_id, value: ConstValue::String(field.clone()) })?; + self.emit_instruction(MirInstruction::Const { + dst: field_name_id, + value: ConstValue::String(field.clone()), + })?; // BoxCall: getField(name) let field_val = self.value_gen.next(); self.emit_instruction(MirInstruction::BoxCall { @@ -24,16 +31,32 @@ impl super::MirBuilder { })?; // Propagate recorded origin class for this field if any - if let Some(class_name) = self.field_origin_class.get(&(object_value, field.clone())).cloned() { + if let Some(class_name) = self + .field_origin_class + .get(&(object_value, field.clone())) + .cloned() + { self.value_origin_newbox.insert(field_val, class_name); } // If base is a known newbox and field is weak, emit WeakLoad (+ optional barrier) - let mut inferred_class: Option = self.value_origin_newbox.get(&object_value).cloned(); + let mut inferred_class: Option = + self.value_origin_newbox.get(&object_value).cloned(); if inferred_class.is_none() { - if let ASTNode::FieldAccess { object: inner_obj, field: inner_field, .. } = object_clone { + if let ASTNode::FieldAccess { + object: inner_obj, + field: inner_field, + .. + } = object_clone + { if let Ok(base_id) = self.build_expression(*inner_obj.clone()) { - if let Some(cls) = self.field_origin_class.get(&(base_id, inner_field)).cloned() { inferred_class = Some(cls); } + if let Some(cls) = self + .field_origin_class + .get(&(base_id, inner_field)) + .cloned() + { + inferred_class = Some(cls); + } } } } @@ -51,20 +74,30 @@ impl super::MirBuilder { } /// Build field assignment: object.field = value - pub(super) fn build_field_assignment(&mut self, object: ASTNode, field: String, value: ASTNode) -> Result { + pub(super) fn build_field_assignment( + &mut self, + object: ASTNode, + field: String, + value: ASTNode, + ) -> Result { let object_value = self.build_expression(object)?; let mut value_result = self.build_expression(value)?; // If base is known and field is weak, create WeakRef before store if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() { if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) { - if weak_set.contains(&field) { value_result = self.emit_weak_new(value_result)?; } + if weak_set.contains(&field) { + value_result = self.emit_weak_new(value_result)?; + } } } // Emit: field name const let field_name_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: field_name_id, value: ConstValue::String(field.clone()) })?; + self.emit_instruction(MirInstruction::Const { + dst: field_name_id, + value: ConstValue::String(field.clone()), + })?; // Set the field via BoxCall: setField(name, value) self.emit_instruction(MirInstruction::BoxCall { dst: None, @@ -78,13 +111,16 @@ impl super::MirBuilder { // Write barrier if weak field if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() { if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) { - if weak_set.contains(&field) { let _ = self.emit_barrier_write(value_result); } + if weak_set.contains(&field) { + let _ = self.emit_barrier_write(value_result); + } } } // Record origin class for this field value if known if let Some(class_name) = self.value_origin_newbox.get(&value_result).cloned() { - self.field_origin_class.insert((object_value, field.clone()), class_name); + self.field_origin_class + .insert((object_value, field.clone()), class_name); } Ok(value_result) diff --git a/src/mir/builder/loops.rs b/src/mir/builder/loops.rs index 677e7257..138f5092 100644 --- a/src/mir/builder/loops.rs +++ b/src/mir/builder/loops.rs @@ -1,8 +1,12 @@ //! Small loop utilities for MirBuilder -use super::{BasicBlockId}; +use super::BasicBlockId; /// Push loop context (header/exit) onto the MirBuilder stacks. -pub(crate) fn push_loop_context(builder: &mut super::MirBuilder, header: BasicBlockId, exit: BasicBlockId) { +pub(crate) fn push_loop_context( + builder: &mut super::MirBuilder, + header: BasicBlockId, + exit: BasicBlockId, +) { builder.loop_header_stack.push(header); builder.loop_exit_stack.push(exit); } diff --git a/src/mir/builder/ops.rs b/src/mir/builder/ops.rs index 22e99a50..566fae05 100644 --- a/src/mir/builder/ops.rs +++ b/src/mir/builder/ops.rs @@ -1,6 +1,6 @@ -use super::{MirInstruction, ValueId, MirType}; +use super::{MirInstruction, MirType, ValueId}; use crate::ast::{ASTNode, BinaryOperator}; -use crate::mir::{BinaryOp, UnaryOp, CompareOp, TypeOpKind}; +use crate::mir::{BinaryOp, CompareOp, TypeOpKind, UnaryOp}; // Internal classification for binary operations #[derive(Debug)] @@ -81,7 +81,12 @@ impl super::MirBuilder { } else { (lhs, rhs) }; - self.emit_instruction(MirInstruction::Compare { dst, op, lhs: lhs2, rhs: rhs2 })?; + self.emit_instruction(MirInstruction::Compare { + dst, + op, + lhs: lhs2, + rhs: rhs2, + })?; self.value_types.insert(dst, MirType::Bool); } } @@ -90,30 +95,58 @@ impl super::MirBuilder { } // Build a unary operation - pub(super) fn build_unary_op(&mut self, operator: String, operand: ASTNode) -> Result { + pub(super) fn build_unary_op( + &mut self, + operator: String, + operand: ASTNode, + ) -> Result { let operand_val = self.build_expression(operand)?; // Core-13 純化: UnaryOp を直接 展開(Neg/Not/BitNot) if crate::config::env::mir_core13_pure() { match operator.as_str() { "-" => { let zero = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: zero, value: crate::mir::ConstValue::Integer(0) })?; + self.emit_instruction(MirInstruction::Const { + dst: zero, + value: crate::mir::ConstValue::Integer(0), + })?; let dst = self.value_gen.next(); - self.emit_instruction(MirInstruction::BinOp { dst, op: crate::mir::BinaryOp::Sub, lhs: zero, rhs: operand_val })?; + self.emit_instruction(MirInstruction::BinOp { + dst, + op: crate::mir::BinaryOp::Sub, + lhs: zero, + rhs: operand_val, + })?; return Ok(dst); } "!" | "not" => { let f = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: f, value: crate::mir::ConstValue::Bool(false) })?; + self.emit_instruction(MirInstruction::Const { + dst: f, + value: crate::mir::ConstValue::Bool(false), + })?; let dst = self.value_gen.next(); - self.emit_instruction(MirInstruction::Compare { dst, op: crate::mir::CompareOp::Eq, lhs: operand_val, rhs: f })?; + self.emit_instruction(MirInstruction::Compare { + dst, + op: crate::mir::CompareOp::Eq, + lhs: operand_val, + rhs: f, + })?; return Ok(dst); } "~" => { let all1 = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: all1, value: crate::mir::ConstValue::Integer(-1) })?; + self.emit_instruction(MirInstruction::Const { + dst: all1, + value: crate::mir::ConstValue::Integer(-1), + })?; let dst = self.value_gen.next(); - self.emit_instruction(MirInstruction::BinOp { dst, op: crate::mir::BinaryOp::BitXor, lhs: operand_val, rhs: all1 })?; + self.emit_instruction(MirInstruction::BinOp { + dst, + op: crate::mir::BinaryOp::BitXor, + lhs: operand_val, + rhs: all1, + })?; return Ok(dst); } _ => {} @@ -121,7 +154,11 @@ impl super::MirBuilder { } let dst = self.value_gen.next(); let mir_op = self.convert_unary_operator(operator)?; - self.emit_instruction(MirInstruction::UnaryOp { dst, op: mir_op, operand: operand_val })?; + self.emit_instruction(MirInstruction::UnaryOp { + dst, + op: mir_op, + operand: operand_val, + })?; Ok(dst) } diff --git a/src/mir/builder/phi.rs b/src/mir/builder/phi.rs index fc379c43..51c969ba 100644 --- a/src/mir/builder/phi.rs +++ b/src/mir/builder/phi.rs @@ -1,8 +1,7 @@ - -use crate::mir::{MirFunction, ValueId, MirType, MirInstruction, BasicBlockId}; -use std::collections::HashMap; -use crate::ast::ASTNode; use super::MirBuilder; +use crate::ast::ASTNode; +use crate::mir::{BasicBlockId, MirFunction, MirInstruction, MirType, ValueId}; +use std::collections::HashMap; // PHI-based return type inference helper pub(super) fn infer_type_from_phi( @@ -31,15 +30,31 @@ pub(super) fn infer_type_from_phi( pub(super) fn extract_assigned_var(ast: &ASTNode) -> Option { match ast { ASTNode::Assignment { target, .. } => { - if let ASTNode::Variable { name, .. } = target.as_ref() { Some(name.clone()) } else { None } + if let ASTNode::Variable { name, .. } = target.as_ref() { + Some(name.clone()) + } else { + None + } } - ASTNode::Program { statements, .. } => statements.last().and_then(|st| extract_assigned_var(st)), - ASTNode::If { then_body, else_body, .. } => { + ASTNode::Program { statements, .. } => { + statements.last().and_then(|st| extract_assigned_var(st)) + } + ASTNode::If { + then_body, + else_body, + .. + } => { // Look into nested if: if both sides assign the same variable, propagate that name upward. - let then_prog = ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown() }; + let then_prog = ASTNode::Program { + statements: then_body.clone(), + span: crate::ast::Span::unknown(), + }; let tvar = extract_assigned_var(&then_prog); let evar = else_body.as_ref().and_then(|eb| { - let ep = ASTNode::Program { statements: eb.clone(), span: crate::ast::Span::unknown() }; + let ep = ASTNode::Program { + statements: eb.clone(), + span: crate::ast::Span::unknown(), + }; extract_assigned_var(&ep) }); match (tvar, evar) { @@ -70,13 +85,21 @@ impl MirBuilder { // If only the then-branch assigns a variable (e.g., `if c { x = ... }`) and the else // does not assign the same variable, bind that variable to a Phi of (then_value, pre_if_value). let assigned_var_then = extract_assigned_var(then_ast_for_analysis); - let assigned_var_else = else_ast_for_analysis.as_ref().and_then(|a| extract_assigned_var(a)); + let assigned_var_else = else_ast_for_analysis + .as_ref() + .and_then(|a| extract_assigned_var(a)); let result_val = self.value_gen.next(); if self.is_no_phi_mode() { if let Some(var_name) = assigned_var_then.clone() { - let else_assigns_same = assigned_var_else.as_ref().map(|s| s == &var_name).unwrap_or(false); - let then_value_for_var = then_var_map_end.get(&var_name).copied().unwrap_or(then_value_raw); + let else_assigns_same = assigned_var_else + .as_ref() + .map(|s| s == &var_name) + .unwrap_or(false); + let then_value_for_var = then_var_map_end + .get(&var_name) + .copied() + .unwrap_or(then_value_raw); let else_value_for_var = if else_assigns_same { else_var_map_end_opt .as_ref() @@ -98,22 +121,40 @@ impl MirBuilder { } if let Some(var_name) = assigned_var_then.clone() { - let else_assigns_same = assigned_var_else.as_ref().map(|s| s == &var_name).unwrap_or(false); + let else_assigns_same = assigned_var_else + .as_ref() + .map(|s| s == &var_name) + .unwrap_or(false); // Resolve branch-end values for the assigned variable - let then_value_for_var = then_var_map_end.get(&var_name).copied().unwrap_or(then_value_raw); + let then_value_for_var = then_var_map_end + .get(&var_name) + .copied() + .unwrap_or(then_value_raw); let else_value_for_var = if else_assigns_same { - else_var_map_end_opt.as_ref().and_then(|m| m.get(&var_name).copied()).unwrap_or(else_value_raw) + else_var_map_end_opt + .as_ref() + .and_then(|m| m.get(&var_name).copied()) + .unwrap_or(else_value_raw) } else { // Else doesn't assign: use pre-if value if available pre_then_var_value.unwrap_or(else_value_raw) }; // Emit Phi for the assigned variable and bind it - self.emit_instruction(MirInstruction::Phi { dst: result_val, inputs: vec![(then_block, then_value_for_var), (else_block, else_value_for_var)] })?; + self.emit_instruction(MirInstruction::Phi { + dst: result_val, + inputs: vec![ + (then_block, then_value_for_var), + (else_block, else_value_for_var), + ], + })?; self.variable_map = pre_if_var_map.clone(); self.variable_map.insert(var_name, result_val); } else { // No variable assignment pattern detected – just emit Phi for expression result - self.emit_instruction(MirInstruction::Phi { dst: result_val, inputs: vec![(then_block, then_value_raw), (else_block, else_value_raw)] })?; + self.emit_instruction(MirInstruction::Phi { + dst: result_val, + inputs: vec![(then_block, then_value_raw), (else_block, else_value_raw)], + })?; // Merge variable map conservatively to pre-if snapshot (no new bindings) self.variable_map = pre_if_var_map.clone(); } diff --git a/src/mir/builder/stmts.rs b/src/mir/builder/stmts.rs index aa2e27c5..94f0179e 100644 --- a/src/mir/builder/stmts.rs +++ b/src/mir/builder/stmts.rs @@ -1,8 +1,8 @@ -use super::{MirInstruction, EffectMask, Effect, ConstValue, ValueId}; -use crate::mir::TypeOpKind; -use crate::mir::loop_builder::LoopBuilder; -use crate::ast::ASTNode; use super::phi::extract_assigned_var; +use super::{ConstValue, Effect, EffectMask, MirInstruction, ValueId}; +use crate::ast::ASTNode; +use crate::mir::loop_builder::LoopBuilder; +use crate::mir::TypeOpKind; impl super::MirBuilder { // Print statement: env.console.log(value) with early TypeOp handling @@ -10,18 +10,33 @@ impl super::MirBuilder { super::utils::builder_debug_log("enter build_print_statement"); match &expression { // print(isType(val, "Type")) / print(asType(...)) - ASTNode::FunctionCall { name, arguments, .. } - if (name == "isType" || name == "asType") && arguments.len() == 2 => - { + ASTNode::FunctionCall { + name, arguments, .. + } if (name == "isType" || name == "asType") && arguments.len() == 2 => { super::utils::builder_debug_log("pattern: print(FunctionCall isType|asType)"); if let Some(type_name) = super::MirBuilder::extract_string_literal(&arguments[1]) { - super::utils::builder_debug_log(&format!("extract_string_literal OK: {}", type_name)); + super::utils::builder_debug_log(&format!( + "extract_string_literal OK: {}", + type_name + )); let val = self.build_expression(arguments[0].clone())?; let ty = super::MirBuilder::parse_type_name_to_mir(&type_name); let dst = self.value_gen.next(); - let op = if name == "isType" { TypeOpKind::Check } else { TypeOpKind::Cast }; - super::utils::builder_debug_log(&format!("emit TypeOp {:?} value={} dst= {}", op, val, dst)); - self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?; + let op = if name == "isType" { + TypeOpKind::Check + } else { + TypeOpKind::Cast + }; + super::utils::builder_debug_log(&format!( + "emit TypeOp {:?} value={} dst= {}", + op, val, dst + )); + self.emit_instruction(MirInstruction::TypeOp { + dst, + op, + value: val, + ty, + })?; self.emit_instruction(MirInstruction::ExternCall { dst: None, iface_name: "env.console".to_string(), @@ -35,18 +50,36 @@ impl super::MirBuilder { } } // print(obj.is("Type")) / print(obj.as("Type")) - ASTNode::MethodCall { object, method, arguments, .. } - if (method == "is" || method == "as") && arguments.len() == 1 => - { + ASTNode::MethodCall { + object, + method, + arguments, + .. + } if (method == "is" || method == "as") && arguments.len() == 1 => { super::utils::builder_debug_log("pattern: print(MethodCall is|as)"); if let Some(type_name) = super::MirBuilder::extract_string_literal(&arguments[0]) { - super::utils::builder_debug_log(&format!("extract_string_literal OK: {}", type_name)); + super::utils::builder_debug_log(&format!( + "extract_string_literal OK: {}", + type_name + )); let obj_val = self.build_expression(*object.clone())?; let ty = super::MirBuilder::parse_type_name_to_mir(&type_name); let dst = self.value_gen.next(); - let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast }; - super::utils::builder_debug_log(&format!("emit TypeOp {:?} obj={} dst= {}", op, obj_val, dst)); - self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?; + let op = if method == "is" { + TypeOpKind::Check + } else { + TypeOpKind::Cast + }; + super::utils::builder_debug_log(&format!( + "emit TypeOp {:?} obj={} dst= {}", + op, obj_val, dst + )); + self.emit_instruction(MirInstruction::TypeOp { + dst, + op, + value: obj_val, + ty, + })?; self.emit_instruction(MirInstruction::ExternCall { dst: None, iface_name: "env.console".to_string(), @@ -87,7 +120,11 @@ impl super::MirBuilder { } Ok(last_value.unwrap_or_else(|| { let void_val = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: void_val, value: ConstValue::Void }).unwrap(); + self.emit_instruction(MirInstruction::Const { + dst: void_val, + value: ConstValue::Void, + }) + .unwrap(); void_val })) } @@ -103,7 +140,11 @@ impl super::MirBuilder { let then_block = self.block_gen.next(); let else_block = self.block_gen.next(); let merge_block = self.block_gen.next(); - self.emit_instruction(MirInstruction::Branch { condition: condition_val, then_bb: then_block, else_bb: else_block })?; + self.emit_instruction(MirInstruction::Branch { + condition: condition_val, + then_bb: then_block, + else_bb: else_block, + })?; // Snapshot variable map before entering branches to avoid cross-branch pollution let pre_if_var_map = self.variable_map.clone(); @@ -121,22 +162,34 @@ impl super::MirBuilder { self.variable_map = pre_if_var_map.clone(); let then_value_raw = self.build_expression(then_branch)?; let then_var_map_end = self.variable_map.clone(); - if !self.is_current_block_terminated() { self.emit_instruction(MirInstruction::Jump { target: merge_block })?; } + if !self.is_current_block_terminated() { + self.emit_instruction(MirInstruction::Jump { + target: merge_block, + })?; + } // else self.current_block = Some(else_block); self.ensure_block_exists(else_block)?; // Build else with a clean snapshot of pre-if variables - let (mut else_value_raw, else_ast_for_analysis, else_var_map_end_opt) = if let Some(else_ast) = else_branch { - self.variable_map = pre_if_var_map.clone(); - let val = self.build_expression(else_ast.clone())?; - (val, Some(else_ast), Some(self.variable_map.clone())) - } else { - let void_val = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: void_val, value: ConstValue::Void })?; - (void_val, None, None) - }; - if !self.is_current_block_terminated() { self.emit_instruction(MirInstruction::Jump { target: merge_block })?; } + let (mut else_value_raw, else_ast_for_analysis, else_var_map_end_opt) = + if let Some(else_ast) = else_branch { + self.variable_map = pre_if_var_map.clone(); + let val = self.build_expression(else_ast.clone())?; + (val, Some(else_ast), Some(self.variable_map.clone())) + } else { + let void_val = self.value_gen.next(); + self.emit_instruction(MirInstruction::Const { + dst: void_val, + value: ConstValue::Void, + })?; + (void_val, None, None) + }; + if !self.is_current_block_terminated() { + self.emit_instruction(MirInstruction::Jump { + target: merge_block, + })?; + } // merge + phi self.current_block = Some(merge_block); @@ -158,7 +211,11 @@ impl super::MirBuilder { } // Loop: delegate to LoopBuilder - pub(super) fn build_loop_statement(&mut self, condition: ASTNode, body: Vec) -> Result { + pub(super) fn build_loop_statement( + &mut self, + condition: ASTNode, + body: Vec, + ) -> Result { let mut loop_builder = LoopBuilder::new(self); loop_builder.build_loop(condition, body) } @@ -170,49 +227,96 @@ impl super::MirBuilder { catch_clauses: Vec, finally_body: Option>, ) -> Result { - if std::env::var("NYASH_BUILDER_DISABLE_TRYCATCH").ok().as_deref() == Some("1") { - let try_ast = ASTNode::Program { statements: try_body, span: crate::ast::Span::unknown() }; + if std::env::var("NYASH_BUILDER_DISABLE_TRYCATCH") + .ok() + .as_deref() + == Some("1") + { + let try_ast = ASTNode::Program { + statements: try_body, + span: crate::ast::Span::unknown(), + }; let result = self.build_expression(try_ast)?; return Ok(result); } let try_block = self.block_gen.next(); let catch_block = self.block_gen.next(); - let finally_block = if finally_body.is_some() { Some(self.block_gen.next()) } else { None }; + let finally_block = if finally_body.is_some() { + Some(self.block_gen.next()) + } else { + None + }; let exit_block = self.block_gen.next(); if let Some(catch_clause) = catch_clauses.first() { if std::env::var("NYASH_DEBUG_TRYCATCH").ok().as_deref() == Some("1") { - eprintln!("[BUILDER] Emitting catch handler for {:?}", catch_clause.exception_type); + eprintln!( + "[BUILDER] Emitting catch handler for {:?}", + catch_clause.exception_type + ); } let exception_value = self.value_gen.next(); - self.emit_instruction(MirInstruction::Catch { exception_type: catch_clause.exception_type.clone(), exception_value, handler_bb: catch_block })?; + self.emit_instruction(MirInstruction::Catch { + exception_type: catch_clause.exception_type.clone(), + exception_value, + handler_bb: catch_block, + })?; } self.emit_instruction(MirInstruction::Jump { target: try_block })?; self.start_new_block(try_block)?; - let try_ast = ASTNode::Program { statements: try_body, span: crate::ast::Span::unknown() }; + let try_ast = ASTNode::Program { + statements: try_body, + span: crate::ast::Span::unknown(), + }; let _try_result = self.build_expression(try_ast)?; - if !self.is_current_block_terminated() { let next_target = finally_block.unwrap_or(exit_block); self.emit_instruction(MirInstruction::Jump { target: next_target })?; } + if !self.is_current_block_terminated() { + let next_target = finally_block.unwrap_or(exit_block); + self.emit_instruction(MirInstruction::Jump { + target: next_target, + })?; + } self.start_new_block(catch_block)?; - if std::env::var("NYASH_DEBUG_TRYCATCH").ok().as_deref() == Some("1") { eprintln!("[BUILDER] Enter catch block {:?}", catch_block); } + if std::env::var("NYASH_DEBUG_TRYCATCH").ok().as_deref() == Some("1") { + eprintln!("[BUILDER] Enter catch block {:?}", catch_block); + } if let Some(catch_clause) = catch_clauses.first() { - if std::env::var("NYASH_DEBUG_TRYCATCH").ok().as_deref() == Some("1") { eprintln!("[BUILDER] Emitting catch handler for {:?}", catch_clause.exception_type); } - let catch_ast = ASTNode::Program { statements: catch_clause.body.clone(), span: crate::ast::Span::unknown() }; + if std::env::var("NYASH_DEBUG_TRYCATCH").ok().as_deref() == Some("1") { + eprintln!( + "[BUILDER] Emitting catch handler for {:?}", + catch_clause.exception_type + ); + } + let catch_ast = ASTNode::Program { + statements: catch_clause.body.clone(), + span: crate::ast::Span::unknown(), + }; self.build_expression(catch_ast)?; } - if !self.is_current_block_terminated() { let next_target = finally_block.unwrap_or(exit_block); self.emit_instruction(MirInstruction::Jump { target: next_target })?; } + if !self.is_current_block_terminated() { + let next_target = finally_block.unwrap_or(exit_block); + self.emit_instruction(MirInstruction::Jump { + target: next_target, + })?; + } if let (Some(finally_block_id), Some(finally_statements)) = (finally_block, finally_body) { self.start_new_block(finally_block_id)?; - let finally_ast = ASTNode::Program { statements: finally_statements, span: crate::ast::Span::unknown() }; + let finally_ast = ASTNode::Program { + statements: finally_statements, + span: crate::ast::Span::unknown(), + }; self.build_expression(finally_ast)?; self.emit_instruction(MirInstruction::Jump { target: exit_block })?; } self.start_new_block(exit_block)?; let result = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: result, value: ConstValue::Void })?; + self.emit_instruction(MirInstruction::Const { + dst: result, + value: ConstValue::Void, + })?; Ok(result) } @@ -230,7 +334,10 @@ impl super::MirBuilder { return Ok(v); } let exception_value = self.build_expression(expression)?; - self.emit_instruction(MirInstruction::Throw { exception: exception_value, effects: EffectMask::PANIC })?; + self.emit_instruction(MirInstruction::Throw { + exception: exception_value, + effects: EffectMask::PANIC, + })?; Ok(exception_value) } @@ -255,28 +362,51 @@ impl super::MirBuilder { } // Return statement - pub(super) fn build_return_statement(&mut self, value: Option>) -> Result { + pub(super) fn build_return_statement( + &mut self, + value: Option>, + ) -> Result { let return_value = if let Some(expr) = value { self.build_expression(*expr)? } else { let void_dst = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: void_dst, value: ConstValue::Void })?; + self.emit_instruction(MirInstruction::Const { + dst: void_dst, + value: ConstValue::Void, + })?; void_dst }; - self.emit_instruction(MirInstruction::Return { value: Some(return_value) })?; + self.emit_instruction(MirInstruction::Return { + value: Some(return_value), + })?; Ok(return_value) } // Nowait: prefer env.future.spawn_instance if method call; else FutureNew - pub(super) fn build_nowait_statement(&mut self, variable: String, expression: ASTNode) -> Result { - if let ASTNode::MethodCall { object, method, arguments, .. } = expression.clone() { + pub(super) fn build_nowait_statement( + &mut self, + variable: String, + expression: ASTNode, + ) -> Result { + if let ASTNode::MethodCall { + object, + method, + arguments, + .. + } = expression.clone() + { let recv_val = self.build_expression(*object)?; let mname_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::Const { dst: mname_id, value: super::ConstValue::String(method.clone()) })?; + self.emit_instruction(MirInstruction::Const { + dst: mname_id, + value: super::ConstValue::String(method.clone()), + })?; let mut arg_vals: Vec = Vec::with_capacity(2 + arguments.len()); arg_vals.push(recv_val); arg_vals.push(mname_id); - for a in arguments.into_iter() { arg_vals.push(self.build_expression(a)?); } + for a in arguments.into_iter() { + arg_vals.push(self.build_expression(a)?); + } let future_id = self.value_gen.next(); self.emit_instruction(MirInstruction::ExternCall { dst: Some(future_id), @@ -290,30 +420,46 @@ impl super::MirBuilder { } let expression_value = self.build_expression(expression)?; let future_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::FutureNew { dst: future_id, value: expression_value })?; + self.emit_instruction(MirInstruction::FutureNew { + dst: future_id, + value: expression_value, + })?; self.variable_map.insert(variable.clone(), future_id); Ok(future_id) } // Await: insert Safepoint before/after and emit Await - pub(super) fn build_await_expression(&mut self, expression: ASTNode) -> Result { + pub(super) fn build_await_expression( + &mut self, + expression: ASTNode, + ) -> Result { let future_value = self.build_expression(expression)?; self.emit_instruction(MirInstruction::Safepoint)?; let result_id = self.value_gen.next(); - self.emit_instruction(MirInstruction::Await { dst: result_id, future: future_value })?; + self.emit_instruction(MirInstruction::Await { + dst: result_id, + future: future_value, + })?; self.emit_instruction(MirInstruction::Safepoint)?; Ok(result_id) } // me: resolve to param if present; else symbolic const (stable mapping) pub(super) fn build_me_expression(&mut self) -> Result { - if let Some(id) = self.variable_map.get("me").cloned() { return Ok(id); } + if let Some(id) = self.variable_map.get("me").cloned() { + return Ok(id); + } let me_value = self.value_gen.next(); - let me_tag = if let Some(ref cls) = self.current_static_box { cls.clone() } else { "__me__".to_string() }; - self.emit_instruction(MirInstruction::Const { dst: me_value, value: ConstValue::String(me_tag) })?; + let me_tag = if let Some(ref cls) = self.current_static_box { + cls.clone() + } else { + "__me__".to_string() + }; + self.emit_instruction(MirInstruction::Const { + dst: me_value, + value: ConstValue::String(me_tag), + })?; self.variable_map.insert("me".to_string(), me_value); Ok(me_value) } } - - diff --git a/src/mir/builder/utils.rs b/src/mir/builder/utils.rs index 1a47dffb..b7c8de15 100644 --- a/src/mir/builder/utils.rs +++ b/src/mir/builder/utils.rs @@ -1,6 +1,6 @@ -use std::fs; use super::{BasicBlock, BasicBlockId}; -use crate::mir::{TypeOpKind, WeakRefOp, BarrierOp}; +use crate::mir::{BarrierOp, TypeOpKind, WeakRefOp}; +use std::fs; // Resolve include path using nyash.toml include.roots if present pub(super) fn resolve_include_path_builder(filename: &str) -> String { @@ -42,8 +42,6 @@ pub(super) fn builder_debug_log(msg: &str) { } } - - // Lightweight helpers moved from builder.rs to reduce file size impl super::MirBuilder { /// Ensure a basic block exists in the current function @@ -83,12 +81,23 @@ impl super::MirBuilder { args: Vec, effects: super::EffectMask, ) -> Result<(), String> { - self.emit_instruction(super::MirInstruction::BoxCall { dst, box_val, method: method.clone(), method_id, args, effects })?; + self.emit_instruction(super::MirInstruction::BoxCall { + dst, + box_val, + method: method.clone(), + method_id, + args, + effects, + })?; if let Some(d) = dst { let mut recv_box: Option = self.value_origin_newbox.get(&box_val).cloned(); if recv_box.is_none() { if let Some(t) = self.value_types.get(&box_val) { - match t { super::MirType::String => recv_box = Some("StringBox".to_string()), super::MirType::Box(name) => recv_box = Some(name.clone()), _ => {} } + match t { + super::MirType::String => recv_box = Some("StringBox".to_string()), + super::MirType::Box(name) => recv_box = Some(name.clone()), + _ => {} + } } } if let Some(bt) = recv_box { @@ -96,17 +105,26 @@ impl super::MirBuilder { self.value_types.insert(d, mt.clone()); } else { let inferred: Option = match (bt.as_str(), method.as_str()) { - ("StringBox", "length") | ("StringBox", "len") => Some(super::MirType::Integer), + ("StringBox", "length") | ("StringBox", "len") => { + Some(super::MirType::Integer) + } ("StringBox", "is_empty") => Some(super::MirType::Bool), ("StringBox", "charCodeAt") => Some(super::MirType::Integer), - ("StringBox", "substring") | ("StringBox", "concat") | ("StringBox", "replace") | ("StringBox", "trim") | ("StringBox", "toUpper") | ("StringBox", "toLower") => Some(super::MirType::String), + ("StringBox", "substring") + | ("StringBox", "concat") + | ("StringBox", "replace") + | ("StringBox", "trim") + | ("StringBox", "toUpper") + | ("StringBox", "toLower") => Some(super::MirType::String), ("ArrayBox", "length") => Some(super::MirType::Integer), ("MapBox", "size") => Some(super::MirType::Integer), ("MapBox", "has") => Some(super::MirType::Bool), ("MapBox", "get") => Some(super::MirType::Box("Any".to_string())), _ => None, }; - if let Some(mt) = inferred { self.value_types.insert(d, mt); } + if let Some(mt) = inferred { + self.value_types.insert(d, mt); + } } } } @@ -114,42 +132,84 @@ impl super::MirBuilder { } #[allow(dead_code)] - pub(super) fn emit_type_check(&mut self, value: super::ValueId, expected_type: String) -> Result { + pub(super) fn emit_type_check( + &mut self, + value: super::ValueId, + expected_type: String, + ) -> Result { let dst = self.value_gen.next(); - self.emit_instruction(super::MirInstruction::TypeOp { dst, op: TypeOpKind::Check, value, ty: super::MirType::Box(expected_type) })?; + self.emit_instruction(super::MirInstruction::TypeOp { + dst, + op: TypeOpKind::Check, + value, + ty: super::MirType::Box(expected_type), + })?; Ok(dst) } #[allow(dead_code)] - pub(super) fn emit_cast(&mut self, value: super::ValueId, target_type: super::MirType) -> Result { + pub(super) fn emit_cast( + &mut self, + value: super::ValueId, + target_type: super::MirType, + ) -> Result { let dst = self.value_gen.next(); - self.emit_instruction(super::MirInstruction::TypeOp { dst, op: TypeOpKind::Cast, value, ty: target_type.clone() })?; + self.emit_instruction(super::MirInstruction::TypeOp { + dst, + op: TypeOpKind::Cast, + value, + ty: target_type.clone(), + })?; Ok(dst) } #[allow(dead_code)] - pub(super) fn emit_weak_new(&mut self, box_val: super::ValueId) -> Result { - if crate::config::env::mir_core13_pure() { return Ok(box_val); } + pub(super) fn emit_weak_new( + &mut self, + box_val: super::ValueId, + ) -> Result { + if crate::config::env::mir_core13_pure() { + return Ok(box_val); + } let dst = self.value_gen.next(); - self.emit_instruction(super::MirInstruction::WeakRef { dst, op: WeakRefOp::New, value: box_val })?; + self.emit_instruction(super::MirInstruction::WeakRef { + dst, + op: WeakRefOp::New, + value: box_val, + })?; Ok(dst) } #[allow(dead_code)] - pub(super) fn emit_weak_load(&mut self, weak_ref: super::ValueId) -> Result { - if crate::config::env::mir_core13_pure() { return Ok(weak_ref); } + pub(super) fn emit_weak_load( + &mut self, + weak_ref: super::ValueId, + ) -> Result { + if crate::config::env::mir_core13_pure() { + return Ok(weak_ref); + } let dst = self.value_gen.next(); - self.emit_instruction(super::MirInstruction::WeakRef { dst, op: WeakRefOp::Load, value: weak_ref })?; + self.emit_instruction(super::MirInstruction::WeakRef { + dst, + op: WeakRefOp::Load, + value: weak_ref, + })?; Ok(dst) } #[allow(dead_code)] pub(super) fn emit_barrier_read(&mut self, ptr: super::ValueId) -> Result<(), String> { - self.emit_instruction(super::MirInstruction::Barrier { op: BarrierOp::Read, ptr }) + self.emit_instruction(super::MirInstruction::Barrier { + op: BarrierOp::Read, + ptr, + }) } #[allow(dead_code)] pub(super) fn emit_barrier_write(&mut self, ptr: super::ValueId) -> Result<(), String> { - self.emit_instruction(super::MirInstruction::Barrier { op: BarrierOp::Write, ptr }) + self.emit_instruction(super::MirInstruction::Barrier { + op: BarrierOp::Write, + ptr, + }) } } diff --git a/src/mir/builder/vars.rs b/src/mir/builder/vars.rs index 10b7e0e9..fa749046 100644 --- a/src/mir/builder/vars.rs +++ b/src/mir/builder/vars.rs @@ -3,7 +3,11 @@ use std::collections::HashSet; /// Collect free variables used in `node` into `used`, excluding names present in `locals`. /// `locals` is updated as new local declarations are encountered. -pub(super) fn collect_free_vars(node: &ASTNode, used: &mut HashSet, locals: &mut HashSet) { +pub(super) fn collect_free_vars( + node: &ASTNode, + used: &mut HashSet, + locals: &mut HashSet, +) { match node { ASTNode::Variable { name, .. } => { if name != "me" && name != "this" && !locals.contains(name) { @@ -11,7 +15,9 @@ pub(super) fn collect_free_vars(node: &ASTNode, used: &mut HashSet, loca } } ASTNode::Local { variables, .. } => { - for v in variables { locals.insert(v.clone()); } + for v in variables { + locals.insert(v.clone()); + } } ASTNode::Assignment { target, value, .. } => { collect_free_vars(target, used, locals); @@ -21,54 +27,122 @@ pub(super) fn collect_free_vars(node: &ASTNode, used: &mut HashSet, loca collect_free_vars(left, used, locals); collect_free_vars(right, used, locals); } - ASTNode::UnaryOp { operand, .. } => { collect_free_vars(operand, used, locals); } - ASTNode::MethodCall { object, arguments, .. } => { + ASTNode::UnaryOp { operand, .. } => { + collect_free_vars(operand, used, locals); + } + ASTNode::MethodCall { + object, arguments, .. + } => { collect_free_vars(object, used, locals); - for a in arguments { collect_free_vars(a, used, locals); } + for a in arguments { + collect_free_vars(a, used, locals); + } } ASTNode::FunctionCall { arguments, .. } => { - for a in arguments { collect_free_vars(a, used, locals); } + for a in arguments { + collect_free_vars(a, used, locals); + } } - ASTNode::Call { callee, arguments, .. } => { + ASTNode::Call { + callee, arguments, .. + } => { collect_free_vars(callee, used, locals); - for a in arguments { collect_free_vars(a, used, locals); } + for a in arguments { + collect_free_vars(a, used, locals); + } + } + ASTNode::FieldAccess { object, .. } => { + collect_free_vars(object, used, locals); } - ASTNode::FieldAccess { object, .. } => { collect_free_vars(object, used, locals); } ASTNode::New { arguments, .. } => { - for a in arguments { collect_free_vars(a, used, locals); } + for a in arguments { + collect_free_vars(a, used, locals); + } } - ASTNode::If { condition, then_body, else_body, .. } => { + ASTNode::If { + condition, + then_body, + else_body, + .. + } => { collect_free_vars(condition, used, locals); - for st in then_body { collect_free_vars(st, used, locals); } - if let Some(eb) = else_body { for st in eb { collect_free_vars(st, used, locals); } } + for st in then_body { + collect_free_vars(st, used, locals); + } + if let Some(eb) = else_body { + for st in eb { + collect_free_vars(st, used, locals); + } + } } - ASTNode::Loop { condition, body, .. } => { + ASTNode::Loop { + condition, body, .. + } => { collect_free_vars(condition, used, locals); - for st in body { collect_free_vars(st, used, locals); } + for st in body { + collect_free_vars(st, used, locals); + } } - ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => { - for st in try_body { collect_free_vars(st, used, locals); } - for c in catch_clauses { for st in &c.body { collect_free_vars(st, used, locals); } } - if let Some(fb) = finally_body { for st in fb { collect_free_vars(st, used, locals); } } + ASTNode::TryCatch { + try_body, + catch_clauses, + finally_body, + .. + } => { + for st in try_body { + collect_free_vars(st, used, locals); + } + for c in catch_clauses { + for st in &c.body { + collect_free_vars(st, used, locals); + } + } + if let Some(fb) = finally_body { + for st in fb { + collect_free_vars(st, used, locals); + } + } } - ASTNode::Throw { expression, .. } => { collect_free_vars(expression, used, locals); } - ASTNode::Print { expression, .. } => { collect_free_vars(expression, used, locals); } - ASTNode::Return { value, .. } => { if let Some(v) = value { collect_free_vars(v, used, locals); } } - ASTNode::AwaitExpression { expression, .. } => { collect_free_vars(expression, used, locals); } - ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => { + ASTNode::Throw { expression, .. } => { + collect_free_vars(expression, used, locals); + } + ASTNode::Print { expression, .. } => { + collect_free_vars(expression, used, locals); + } + ASTNode::Return { value, .. } => { + if let Some(v) = value { + collect_free_vars(v, used, locals); + } + } + ASTNode::AwaitExpression { expression, .. } => { + collect_free_vars(expression, used, locals); + } + ASTNode::PeekExpr { + scrutinee, + arms, + else_expr, + .. + } => { collect_free_vars(scrutinee, used, locals); - for (_, e) in arms { collect_free_vars(e, used, locals); } + for (_, e) in arms { + collect_free_vars(e, used, locals); + } collect_free_vars(else_expr, used, locals); } ASTNode::Program { statements, .. } => { - for st in statements { collect_free_vars(st, used, locals); } + for st in statements { + collect_free_vars(st, used, locals); + } } ASTNode::FunctionDeclaration { params, body, .. } => { let mut inner = locals.clone(); - for p in params { inner.insert(p.clone()); } - for st in body { collect_free_vars(st, used, &mut inner); } + for p in params { + inner.insert(p.clone()); + } + for st in body { + collect_free_vars(st, used, &mut inner); + } } _ => {} } } - diff --git a/src/mir/effect.rs b/src/mir/effect.rs index c9b2418f..162f42a1 100644 --- a/src/mir/effect.rs +++ b/src/mir/effect.rs @@ -1,11 +1,11 @@ /*! * MIR Effect System - Track side effects for optimization - * + * * Based on ChatGPT5's design for parallel execution and optimization safety */ -use std::fmt; use crate::debug::log as dlog; +use std::fmt; /// Effect flags for tracking side effects and enabling optimizations #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -22,7 +22,7 @@ pub enum Effect { Io = 0x0004, /// Control flow operations (Tier-0: affects execution flow) Control = 0x0008, - + // Legacy effects for compatibility (will be mapped to above categories) /// Reads from heap/memory (maps to Pure if read-only) ReadHeap = 0x0010, @@ -51,83 +51,83 @@ pub enum Effect { impl EffectMask { /// No effects - pure computation pub const PURE: Self = Self(Effect::Pure as u16); - + /// Mutable operations (writes, ownership changes) pub const MUT: Self = Self(Effect::Mut as u16); - + /// I/O operations (external effects, cannot reorder) pub const IO: Self = Self(Effect::Io as u16); - + /// Control flow operations pub const CONTROL: Self = Self(Effect::Control as u16); - + // Legacy constants for compatibility /// Memory read effects pub const READ: Self = Self(Effect::ReadHeap as u16); pub const READ_ALIAS: Self = Self::READ; // Uppercase alias for compatibility - + /// Memory write effects (includes read) pub const WRITE: Self = Self((Effect::WriteHeap as u16) | (Effect::ReadHeap as u16)); - + /// P2P communication effects pub const P2P: Self = Self(Effect::P2P as u16); - + /// Panic/exception effects pub const PANIC: Self = Self(Effect::Panic as u16); - + /// All effects - maximum side effects pub const ALL: Self = Self(0xFFFF); - + /// Create an empty effect mask pub fn new() -> Self { Self(0) } - + /// Create effect mask from raw bits pub fn from_bits(bits: u16) -> Self { Self(bits) } - + /// Get raw bits pub fn bits(self) -> u16 { self.0 } - + /// Add an effect to the mask pub fn add(self, effect: Effect) -> Self { Self(self.0 | (effect as u16)) } - + /// Remove an effect from the mask pub fn remove(self, effect: Effect) -> Self { Self(self.0 & !(effect as u16)) } - + /// Check if mask contains an effect pub fn contains(self, effect: Effect) -> bool { (self.0 & (effect as u16)) != 0 } - + /// Check if mask contains any of the given effects pub fn contains_any(self, mask: EffectMask) -> bool { (self.0 & mask.0) != 0 } - + /// Check if mask contains all of the given effects pub fn contains_all(self, mask: EffectMask) -> bool { (self.0 & mask.0) == mask.0 } - + /// Combine two effect masks pub fn union(self, other: EffectMask) -> Self { Self(self.0 | other.0) } - + /// Get intersection of two effect masks pub fn intersection(self, other: EffectMask) -> Self { Self(self.0 & other.0) } - + /// Check if the computation is pure (no side effects) pub fn is_pure(self) -> bool { // READ/WRITE/IO/CONTROLがあれば純粋ではない(READはreadonly扱い) @@ -136,37 +136,44 @@ impl EffectMask { && !self.is_io() && !self.is_control(); if dlog::on("NYASH_DEBUG_EFFECTS") { - eprintln!("[EFFECT] bits={:#06x} primary={:?} is_pure={} read_only={} mut={} io={}", - self.bits(), self.primary_category(), pure, self.is_read_only(), self.is_mut(), self.is_io()); + eprintln!( + "[EFFECT] bits={:#06x} primary={:?} is_pure={} read_only={} mut={} io={}", + self.bits(), + self.primary_category(), + pure, + self.is_read_only(), + self.is_mut(), + self.is_io() + ); } pure } - + /// Check if the computation is mutable (modifies state) pub fn is_mut(self) -> bool { - self.contains(Effect::Mut) || - self.contains(Effect::WriteHeap) || - self.contains(Effect::Alloc) + self.contains(Effect::Mut) + || self.contains(Effect::WriteHeap) + || self.contains(Effect::Alloc) } - + /// Check if the computation has I/O effects (external side effects) pub fn is_io(self) -> bool { - self.contains(Effect::Io) || - self.contains(Effect::P2P) || - self.contains(Effect::FFI) || - self.contains(Effect::Global) || - self.contains(Effect::Async) || - self.contains(Effect::Unsafe) || - self.contains(Effect::Debug) || - self.contains(Effect::Barrier) || - self.contains(Effect::Panic) + self.contains(Effect::Io) + || self.contains(Effect::P2P) + || self.contains(Effect::FFI) + || self.contains(Effect::Global) + || self.contains(Effect::Async) + || self.contains(Effect::Unsafe) + || self.contains(Effect::Debug) + || self.contains(Effect::Barrier) + || self.contains(Effect::Panic) } - + /// Check if the computation affects control flow pub fn is_control(self) -> bool { self.contains(Effect::Control) } - + /// Get the primary effect category for MIR optimization pub fn primary_category(self) -> Effect { if self.is_control() { @@ -179,51 +186,81 @@ impl EffectMask { Effect::Pure } } - + /// Check if the computation only reads (doesn't modify state) pub fn is_read_only(self) -> bool { !self.is_mut() && !self.is_io() } - + /// Check if parallel execution is safe pub fn is_parallel_safe(self) -> bool { - !self.contains(Effect::WriteHeap) && - !self.contains(Effect::Global) && - !self.contains(Effect::Unsafe) + !self.contains(Effect::WriteHeap) + && !self.contains(Effect::Global) + && !self.contains(Effect::Unsafe) } - + /// Check if operation can be moved across other operations pub fn is_moveable(self) -> bool { self.is_pure() || self.is_read_only() } - + /// Get a human-readable list of effects pub fn effect_names(self) -> Vec<&'static str> { let mut names = Vec::new(); - + // Primary categories - if self.contains(Effect::Pure) { names.push("pure"); } - if self.contains(Effect::Mut) { names.push("mut"); } - if self.contains(Effect::Io) { names.push("io"); } - if self.contains(Effect::Control) { names.push("control"); } - + if self.contains(Effect::Pure) { + names.push("pure"); + } + if self.contains(Effect::Mut) { + names.push("mut"); + } + if self.contains(Effect::Io) { + names.push("io"); + } + if self.contains(Effect::Control) { + names.push("control"); + } + // Legacy effects for detailed tracking - if self.contains(Effect::ReadHeap) { names.push("read"); } - if self.contains(Effect::WriteHeap) { names.push("write"); } - if self.contains(Effect::P2P) { names.push("p2p"); } - if self.contains(Effect::FFI) { names.push("ffi"); } - if self.contains(Effect::Panic) { names.push("panic"); } - if self.contains(Effect::Alloc) { names.push("alloc"); } - if self.contains(Effect::Global) { names.push("global"); } - if self.contains(Effect::Async) { names.push("async"); } - if self.contains(Effect::Unsafe) { names.push("unsafe"); } - if self.contains(Effect::Debug) { names.push("debug"); } - if self.contains(Effect::Barrier) { names.push("barrier"); } - + if self.contains(Effect::ReadHeap) { + names.push("read"); + } + if self.contains(Effect::WriteHeap) { + names.push("write"); + } + if self.contains(Effect::P2P) { + names.push("p2p"); + } + if self.contains(Effect::FFI) { + names.push("ffi"); + } + if self.contains(Effect::Panic) { + names.push("panic"); + } + if self.contains(Effect::Alloc) { + names.push("alloc"); + } + if self.contains(Effect::Global) { + names.push("global"); + } + if self.contains(Effect::Async) { + names.push("async"); + } + if self.contains(Effect::Unsafe) { + names.push("unsafe"); + } + if self.contains(Effect::Debug) { + names.push("debug"); + } + if self.contains(Effect::Barrier) { + names.push("barrier"); + } + if names.is_empty() { names.push("none"); } - + names } } @@ -247,7 +284,7 @@ impl fmt::Display for EffectMask { impl std::ops::BitOr for EffectMask { type Output = Self; - + fn bitor(self, rhs: Self) -> Self { self.union(rhs) } @@ -261,7 +298,7 @@ impl std::ops::BitOrAssign for EffectMask { impl std::ops::BitAnd for EffectMask { type Output = Self; - + fn bitand(self, rhs: Self) -> Self { self.intersection(rhs) } @@ -276,82 +313,82 @@ impl std::ops::BitAndAssign for EffectMask { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_effect_mask_creation() { let pure = EffectMask::PURE; let read = EffectMask::READ; let write = EffectMask::WRITE; - + assert!(pure.is_pure()); assert!(!read.is_pure()); assert!(!write.is_pure()); - + assert!(read.is_read_only()); assert!(!write.is_read_only()); } - + #[test] fn test_effect_combination() { let mut effects = EffectMask::new(); assert!(effects.is_pure()); - + effects = effects.add(Effect::ReadHeap); assert!(effects.contains(Effect::ReadHeap)); assert!(effects.is_read_only()); - + effects = effects.add(Effect::WriteHeap); assert!(effects.contains(Effect::WriteHeap)); assert!(!effects.is_read_only()); - + effects = effects.add(Effect::Io); assert!(effects.contains(Effect::Io)); assert!(!effects.is_parallel_safe()); } - + #[test] fn test_effect_union() { let read_effect = EffectMask::READ; let io_effect = EffectMask::IO; - + let combined = read_effect | io_effect; - + assert!(combined.contains(Effect::ReadHeap)); assert!(combined.contains(Effect::Io)); assert!(!combined.is_pure()); // IO + read remains parallel-safe under current semantics assert!(combined.is_parallel_safe()); } - + #[test] fn test_parallel_safety() { let pure = EffectMask::PURE; let read = EffectMask::READ; let write = EffectMask::WRITE; let io = EffectMask::IO; - + assert!(pure.is_parallel_safe()); assert!(read.is_parallel_safe()); assert!(!write.is_parallel_safe()); assert!(io.is_parallel_safe()); // I/O can be parallel if properly synchronized } - + #[test] fn test_effect_names() { let pure = EffectMask::PURE; assert_eq!(pure.effect_names(), vec!["pure"]); - + let read_write = EffectMask::READ.add(Effect::WriteHeap); let names = read_write.effect_names(); assert!(names.contains(&"read")); assert!(names.contains(&"write")); } - + #[test] fn test_effect_display() { let pure = EffectMask::PURE; assert_eq!(format!("{}", pure), "pure"); - + let read_io = EffectMask::READ | EffectMask::IO; let display = format!("{}", read_io); assert!(display.contains("read")); diff --git a/src/mir/function.rs b/src/mir/function.rs index b015bf96..5351f341 100644 --- a/src/mir/function.rs +++ b/src/mir/function.rs @@ -1,10 +1,10 @@ /*! * MIR Function and Module - High-level MIR organization - * + * * Functions contain basic blocks and SSA values, modules contain functions */ -use super::{BasicBlock, BasicBlockId, ValueId, EffectMask, MirType}; +use super::{BasicBlock, BasicBlockId, EffectMask, MirType, ValueId}; use std::collections::HashMap; use std::fmt; @@ -13,13 +13,13 @@ use std::fmt; pub struct FunctionSignature { /// Function name pub name: String, - + /// Parameter types pub params: Vec, - + /// Return type pub return_type: MirType, - + /// Overall effect mask for the function pub effects: EffectMask, } @@ -29,22 +29,22 @@ pub struct FunctionSignature { pub struct MirFunction { /// Function signature pub signature: FunctionSignature, - + /// Basic blocks indexed by ID pub blocks: HashMap, - + /// Entry basic block ID pub entry_block: BasicBlockId, - + /// Local variable declarations (before SSA conversion) pub locals: Vec, - + /// Parameter value IDs pub params: Vec, - + /// Next available value ID pub next_value_id: u32, - + /// Function-level metadata pub metadata: FunctionMetadata, } @@ -54,16 +54,16 @@ pub struct MirFunction { pub struct FunctionMetadata { /// Source file location pub source_file: Option, - + /// Line number in source pub line_number: Option, - + /// Whether this function is an entry point pub is_entry_point: bool, - + /// Whether this function is pure (no side effects) pub is_pure: bool, - + /// Optimization hints pub optimization_hints: Vec, @@ -76,7 +76,7 @@ impl MirFunction { pub fn new(signature: FunctionSignature, entry_block: BasicBlockId) -> Self { let mut blocks = HashMap::new(); blocks.insert(entry_block, BasicBlock::new(entry_block)); - + Self { signature, blocks, @@ -87,63 +87,64 @@ impl MirFunction { metadata: FunctionMetadata::default(), } } - + /// Get the next available ValueId pub fn next_value_id(&mut self) -> ValueId { let id = ValueId::new(self.next_value_id); self.next_value_id += 1; id } - + /// Add a new basic block pub fn add_block(&mut self, block: BasicBlock) -> BasicBlockId { let id = block.id; self.blocks.insert(id, block); id } - + /// Get a basic block by ID pub fn get_block(&self, id: BasicBlockId) -> Option<&BasicBlock> { self.blocks.get(&id) } - + /// Get a mutable basic block by ID pub fn get_block_mut(&mut self, id: BasicBlockId) -> Option<&mut BasicBlock> { self.blocks.get_mut(&id) } - + /// Get the entry block pub fn entry_block(&self) -> &BasicBlock { - self.blocks.get(&self.entry_block) + self.blocks + .get(&self.entry_block) .expect("Entry block must exist") } - + /// Get all basic block IDs in insertion order pub fn block_ids(&self) -> Vec { let mut ids: Vec<_> = self.blocks.keys().copied().collect(); ids.sort(); ids } - + /// Get all values defined in this function pub fn defined_values(&self) -> Vec { let mut values = Vec::new(); values.extend(&self.params); - + for block in self.blocks.values() { values.extend(block.defined_values()); } - + values } - + /// Verify function integrity (basic checks) pub fn verify(&self) -> Result<(), String> { // Check entry block exists if !self.blocks.contains_key(&self.entry_block) { return Err("Entry block does not exist".to_string()); } - + // Check all blocks are reachable from entry let reachable = self.compute_reachable_blocks(); for (id, _block) in &self.blocks { @@ -151,13 +152,13 @@ impl MirFunction { eprintln!("Warning: Block {} is unreachable", id); } } - + // Check terminator consistency for block in self.blocks.values() { if !block.is_terminated() && !block.is_empty() { return Err(format!("Block {} is not properly terminated", block.id)); } - + // Check successor/predecessor consistency for successor_id in &block.successors { if let Some(successor) = self.blocks.get(successor_id) { @@ -168,20 +169,22 @@ impl MirFunction { )); } } else { - return Err(format!("Block {} references non-existent successor {}", - block.id, successor_id)); + return Err(format!( + "Block {} references non-existent successor {}", + block.id, successor_id + )); } } } - + Ok(()) } - + /// Compute reachable blocks from entry fn compute_reachable_blocks(&self) -> std::collections::HashSet { let mut reachable = std::collections::HashSet::new(); let mut worklist = vec![self.entry_block]; - + while let Some(current) = worklist.pop() { if reachable.insert(current) { if let Some(block) = self.blocks.get(¤t) { @@ -189,31 +192,31 @@ impl MirFunction { } } } - + reachable } - + /// Update predecessor/successor relationships pub fn update_cfg(&mut self) { // Clear all predecessors for block in self.blocks.values_mut() { block.predecessors.clear(); } - + // Rebuild predecessors from successors - let edges: Vec<(BasicBlockId, BasicBlockId)> = self.blocks.values() - .flat_map(|block| { - block.successors.iter().map(move |&succ| (block.id, succ)) - }) + let edges: Vec<(BasicBlockId, BasicBlockId)> = self + .blocks + .values() + .flat_map(|block| block.successors.iter().map(move |&succ| (block.id, succ))) .collect(); - + for (pred, succ) in edges { if let Some(successor_block) = self.blocks.get_mut(&succ) { successor_block.add_predecessor(pred); } } } - + /// Mark reachable blocks pub fn mark_reachable_blocks(&mut self) { let reachable = self.compute_reachable_blocks(); @@ -223,17 +226,21 @@ impl MirFunction { } } } - + /// Get function statistics pub fn stats(&self) -> FunctionStats { - let instruction_count = self.blocks.values() + let instruction_count = self + .blocks + .values() .map(|block| block.instructions.len() + if block.terminator.is_some() { 1 } else { 0 }) .sum(); - - let phi_count = self.blocks.values() + + let phi_count = self + .blocks + .values() .map(|block| block.phi_instructions().count()) .sum(); - + FunctionStats { block_count: self.blocks.len(), instruction_count, @@ -259,13 +266,13 @@ pub struct FunctionStats { pub struct MirModule { /// Module name pub name: String, - + /// Functions in this module pub functions: HashMap, - + /// Global constants/statics pub globals: HashMap, - + /// Module metadata pub metadata: ModuleMetadata, } @@ -275,13 +282,13 @@ pub struct MirModule { pub struct ModuleMetadata { /// Source file this module was compiled from pub source_file: Option, - + /// Compilation timestamp pub compiled_at: Option, - + /// Compiler version pub compiler_version: Option, - + /// Optimization level used pub optimization_level: u32, } @@ -296,56 +303,54 @@ impl MirModule { metadata: ModuleMetadata::default(), } } - + /// Add a function to the module pub fn add_function(&mut self, function: MirFunction) { let name = function.signature.name.clone(); self.functions.insert(name, function); } - + /// Get a function by name pub fn get_function(&self, name: &str) -> Option<&MirFunction> { self.functions.get(name) } - + /// Get a mutable function by name pub fn get_function_mut(&mut self, name: &str) -> Option<&mut MirFunction> { self.functions.get_mut(name) } - + /// Get all function names pub fn function_names(&self) -> Vec<&String> { self.functions.keys().collect() } - + /// Add a global constant pub fn add_global(&mut self, name: String, value: super::ConstValue) { self.globals.insert(name, value); } - + /// Verify entire module pub fn verify(&self) -> Result<(), Vec> { let mut errors = Vec::new(); - + for (name, function) in &self.functions { if let Err(e) = function.verify() { errors.push(format!("Function '{}': {}", name, e)); } } - + if errors.is_empty() { Ok(()) } else { Err(errors) } } - + /// Get module statistics pub fn stats(&self) -> ModuleStats { - let function_stats: Vec<_> = self.functions.values() - .map(|f| f.stats()) - .collect(); - + let function_stats: Vec<_> = self.functions.values().map(|f| f.stats()).collect(); + ModuleStats { function_count: self.functions.len(), global_count: self.globals.len(), @@ -370,30 +375,35 @@ pub struct ModuleStats { impl fmt::Display for MirFunction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "function {}({}) -> {} {{", - self.signature.name, - self.signature.params.iter() - .enumerate() - .map(|(i, ty)| format!("%{}: {:?}", i, ty)) - .collect::>() - .join(", "), - format!("{:?}", self.signature.return_type))?; - + writeln!( + f, + "function {}({}) -> {} {{", + self.signature.name, + self.signature + .params + .iter() + .enumerate() + .map(|(i, ty)| format!("%{}: {:?}", i, ty)) + .collect::>() + .join(", "), + format!("{:?}", self.signature.return_type) + )?; + // Show effects if not pure if !self.signature.effects.is_pure() { writeln!(f, " ; effects: {}", self.signature.effects)?; } - + // Show blocks in order let mut block_ids: Vec<_> = self.blocks.keys().copied().collect(); block_ids.sort(); - + for block_id in block_ids { if let Some(block) = self.blocks.get(&block_id) { write!(f, "{}", block)?; } } - + writeln!(f, "}}")?; Ok(()) } @@ -402,7 +412,7 @@ impl fmt::Display for MirFunction { impl fmt::Display for MirModule { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "module {} {{", self.name)?; - + // Show globals if !self.globals.is_empty() { writeln!(f, " ; globals:")?; @@ -411,12 +421,12 @@ impl fmt::Display for MirModule { } writeln!(f)?; } - + // Show functions for function in self.functions.values() { writeln!(f, "{}", function)?; } - + writeln!(f, "}}")?; Ok(()) } @@ -425,8 +435,8 @@ impl fmt::Display for MirModule { #[cfg(test)] mod tests { use super::*; - use crate::mir::{MirType, EffectMask}; - + use crate::mir::{EffectMask, MirType}; + #[test] fn test_function_creation() { let signature = FunctionSignature { @@ -435,34 +445,34 @@ mod tests { return_type: MirType::Integer, effects: EffectMask::PURE, }; - + let entry_block = BasicBlockId::new(0); let function = MirFunction::new(signature.clone(), entry_block); - + assert_eq!(function.signature.name, "test_func"); assert_eq!(function.entry_block, entry_block); assert!(function.blocks.contains_key(&entry_block)); } - + #[test] fn test_module_creation() { let mut module = MirModule::new("test_module".to_string()); - + let signature = FunctionSignature { name: "main".to_string(), params: vec![], return_type: MirType::Void, effects: EffectMask::PURE, }; - + let function = MirFunction::new(signature, BasicBlockId::new(0)); module.add_function(function); - + assert_eq!(module.name, "test_module"); assert!(module.get_function("main").is_some()); assert_eq!(module.function_names().len(), 1); } - + #[test] fn test_value_id_generation() { let signature = FunctionSignature { @@ -471,18 +481,18 @@ mod tests { return_type: MirType::Void, effects: EffectMask::PURE, }; - + let mut function = MirFunction::new(signature, BasicBlockId::new(0)); - + let val1 = function.next_value_id(); let val2 = function.next_value_id(); let val3 = function.next_value_id(); - + assert_eq!(val1, ValueId::new(0)); assert_eq!(val2, ValueId::new(1)); assert_eq!(val3, ValueId::new(2)); } - + #[test] fn test_function_stats() { let signature = FunctionSignature { @@ -491,10 +501,10 @@ mod tests { return_type: MirType::Void, effects: EffectMask::PURE, }; - + let function = MirFunction::new(signature, BasicBlockId::new(0)); let stats = function.stats(); - + assert_eq!(stats.block_count, 1); assert_eq!(stats.instruction_count, 0); assert_eq!(stats.value_count, 0); diff --git a/src/mir/instruction.rs b/src/mir/instruction.rs index 1e23c18b..3bb21796 100644 --- a/src/mir/instruction.rs +++ b/src/mir/instruction.rs @@ -1,10 +1,10 @@ /*! * MIR Instruction Set - 20 Core Instructions per ChatGPT5 Design - * + * * SSA-form instructions with effect tracking for optimization */ -use super::{ValueId, EffectMask, Effect}; +use super::{Effect, EffectMask, ValueId}; // use crate::value::NyashValue; // Commented out to avoid circular dependency use std::fmt; @@ -14,11 +14,8 @@ pub enum MirInstruction { // === Constants and Values === /// Load a constant value /// `%dst = const value` - Const { - dst: ValueId, - value: ConstValue, - }, - + Const { dst: ValueId, value: ConstValue }, + // === Arithmetic Operations === /// Binary arithmetic operation /// `%dst = %lhs op %rhs` @@ -28,7 +25,7 @@ pub enum MirInstruction { lhs: ValueId, rhs: ValueId, }, - + /// Unary operation /// `%dst = op %operand` UnaryOp { @@ -36,7 +33,7 @@ pub enum MirInstruction { op: UnaryOp, operand: ValueId, }, - + // === Comparison Operations === /// Compare two values /// `%dst = %lhs cmp %rhs` @@ -46,22 +43,16 @@ pub enum MirInstruction { lhs: ValueId, rhs: ValueId, }, - + // === Memory Operations === /// Load from memory/variable /// `%dst = load %ptr` - Load { - dst: ValueId, - ptr: ValueId, - }, - + Load { dst: ValueId, ptr: ValueId }, + /// Store to memory/variable /// `store %value -> %ptr` - Store { - value: ValueId, - ptr: ValueId, - }, - + Store { value: ValueId, ptr: ValueId }, + // === Function Calls === /// Call a function /// `%dst = call %func(%args...)` @@ -84,7 +75,7 @@ pub enum MirInstruction { /// Optional 'me' value to capture weakly if it is a BoxRef at runtime me: Option, }, - + /// Box method invocation /// `%dst = invoke %box.method(%args...)` /// method_id: Optional numeric slot id when resolved at build time @@ -107,7 +98,7 @@ pub enum MirInstruction { args: Vec, effects: EffectMask, }, - + // === Control Flow === /// Conditional branch /// `br %condition -> %then_bb, %else_bb` @@ -116,19 +107,15 @@ pub enum MirInstruction { then_bb: super::BasicBlockId, else_bb: super::BasicBlockId, }, - + /// Unconditional jump /// `jmp %target_bb` - Jump { - target: super::BasicBlockId, - }, - + Jump { target: super::BasicBlockId }, + /// Return from function /// `ret %value` or `ret void` - Return { - value: Option, - }, - + Return { value: Option }, + // === SSA Phi Function === /// SSA phi function for merging values from different paths /// `%dst = phi [%val1 from %bb1, %val2 from %bb2, ...]` @@ -136,7 +123,7 @@ pub enum MirInstruction { dst: ValueId, inputs: Vec<(super::BasicBlockId, ValueId)>, }, - + // === Box Operations === /// Create a new Box instance /// `%dst = new_box "BoxType"(%args...)` @@ -145,7 +132,7 @@ pub enum MirInstruction { box_type: String, args: Vec, }, - + /// Check Box type /// `%dst = type_check %box "BoxType"` TypeCheck { @@ -153,7 +140,7 @@ pub enum MirInstruction { value: ValueId, expected_type: String, }, - + // === Type Conversion === /// Convert between types /// `%dst = cast %value as Type` @@ -172,7 +159,7 @@ pub enum MirInstruction { value: ValueId, ty: MirType, }, - + // === Array Operations === /// Get array element /// `%dst = %array[%index]` @@ -181,7 +168,7 @@ pub enum MirInstruction { array: ValueId, index: ValueId, }, - + /// Set array element /// `%array[%index] = %value` ArraySet { @@ -189,41 +176,31 @@ pub enum MirInstruction { index: ValueId, value: ValueId, }, - + // === Special Operations === /// Copy a value (for optimization passes) /// `%dst = copy %src` - Copy { - dst: ValueId, - src: ValueId, - }, - + Copy { dst: ValueId, src: ValueId }, + /// Debug/introspection instruction /// `debug %value "message"` - Debug { - value: ValueId, - message: String, - }, - + Debug { value: ValueId, message: String }, + /// Print instruction for console output /// `print %value` - Print { - value: ValueId, - effects: EffectMask, - }, - + Print { value: ValueId, effects: EffectMask }, + /// No-op instruction (for optimization placeholders) Nop, - + // === Control Flow & Exception Handling (Phase 5) === - /// Throw an exception /// `throw %exception_value` Throw { exception: ValueId, effects: EffectMask, }, - + /// Catch handler setup (landing pad for exceptions) /// `catch %exception_type -> %handler_bb` Catch { @@ -231,20 +208,16 @@ pub enum MirInstruction { exception_value: ValueId, // Where to store caught exception handler_bb: super::BasicBlockId, }, - + /// Safepoint instruction (no-op for now, can be used for GC/debugging) /// `safepoint` Safepoint, - + // === Phase 6: Box Reference Operations === - /// Create a new reference to a Box /// `%dst = ref_new %box` - RefNew { - dst: ValueId, - box_val: ValueId, - }, - + RefNew { dst: ValueId, box_val: ValueId }, + /// Get/dereference a Box field through reference /// `%dst = ref_get %ref.field` RefGet { @@ -252,7 +225,7 @@ pub enum MirInstruction { reference: ValueId, field: String, }, - + /// Set/assign Box field through reference /// `ref_set %ref.field = %value` RefSet { @@ -260,32 +233,22 @@ pub enum MirInstruction { field: String, value: ValueId, }, - + /// Create a weak reference to a Box /// `%dst = weak_new %box` - WeakNew { - dst: ValueId, - box_val: ValueId, - }, - + WeakNew { dst: ValueId, box_val: ValueId }, + /// Load from weak reference (if still alive) /// `%dst = weak_load %weak_ref` - WeakLoad { - dst: ValueId, - weak_ref: ValueId, - }, - + WeakLoad { dst: ValueId, weak_ref: ValueId }, + /// Memory barrier read (no-op for now, proper effect annotation) /// `barrier_read %ptr` - BarrierRead { - ptr: ValueId, - }, - + BarrierRead { ptr: ValueId }, + /// Memory barrier write (no-op for now, proper effect annotation) /// `barrier_write %ptr` - BarrierWrite { - ptr: ValueId, - }, + BarrierWrite { ptr: ValueId }, // === Unified PoC: WeakRef/Barrier (flags-only scaffolding) === /// Unified weak reference op (PoC) @@ -298,42 +261,28 @@ pub enum MirInstruction { /// Unified barrier op (PoC) /// `barrier read %ptr` or `barrier write %ptr` - Barrier { - op: BarrierOp, - ptr: ValueId, - }, - + Barrier { op: BarrierOp, ptr: ValueId }, + // === Phase 7: Async/Future Operations === - /// Create a new Future with initial value /// `%dst = future_new %value` - FutureNew { - dst: ValueId, - value: ValueId, - }, - + FutureNew { dst: ValueId, value: ValueId }, + /// Set Future value and mark as ready /// `future_set %future = %value` - FutureSet { - future: ValueId, - value: ValueId, - }, - + FutureSet { future: ValueId, value: ValueId }, + /// Wait for Future completion and get value /// `%dst = await %future` - Await { - dst: ValueId, - future: ValueId, - }, - + Await { dst: ValueId, future: ValueId }, + // === Phase 9.7: External Function Calls (Box FFI/ABI) === - /// External function call through Box FFI/ABI /// `%dst = extern_call interface.method(%args...)` ExternCall { dst: Option, - iface_name: String, // e.g., "env.console" - method_name: String, // e.g., "log" + iface_name: String, // e.g., "env.console" + method_name: String, // e.g., "log" args: Vec, effects: EffectMask, }, @@ -354,13 +303,22 @@ pub enum ConstValue { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum BinaryOp { // Arithmetic - Add, Sub, Mul, Div, Mod, - + Add, + Sub, + Mul, + Div, + Mod, + // Bitwise - BitAnd, BitOr, BitXor, Shl, Shr, - + BitAnd, + BitOr, + BitXor, + Shl, + Shr, + // Logical - And, Or, + And, + Or, } /// Unary operations @@ -368,10 +326,10 @@ pub enum BinaryOp { pub enum UnaryOp { // Arithmetic Neg, - + // Logical Not, - + // Bitwise BitNot, } @@ -379,7 +337,12 @@ pub enum UnaryOp { /// Comparison operations #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CompareOp { - Eq, Ne, Lt, Le, Gt, Ge, + Eq, + Ne, + Lt, + Le, + Gt, + Ge, } /// MIR type system @@ -401,47 +364,46 @@ impl MirInstruction { pub fn effects(&self) -> EffectMask { match self { // Pure operations - MirInstruction::Const { .. } | - MirInstruction::BinOp { .. } | - MirInstruction::UnaryOp { .. } | - MirInstruction::Compare { .. } | - MirInstruction::Cast { .. } | - MirInstruction::TypeOp { .. } | - MirInstruction::Copy { .. } | - MirInstruction::Phi { .. } | - MirInstruction::TypeCheck { .. } | - MirInstruction::Nop => EffectMask::PURE, - + MirInstruction::Const { .. } + | MirInstruction::BinOp { .. } + | MirInstruction::UnaryOp { .. } + | MirInstruction::Compare { .. } + | MirInstruction::Cast { .. } + | MirInstruction::TypeOp { .. } + | MirInstruction::Copy { .. } + | MirInstruction::Phi { .. } + | MirInstruction::TypeCheck { .. } + | MirInstruction::Nop => EffectMask::PURE, + // Memory operations MirInstruction::Load { .. } => EffectMask::READ, - MirInstruction::Store { .. } | - MirInstruction::ArraySet { .. } => EffectMask::WRITE, + MirInstruction::Store { .. } | MirInstruction::ArraySet { .. } => EffectMask::WRITE, MirInstruction::ArrayGet { .. } => EffectMask::READ, - + // Function calls use provided effect mask - MirInstruction::Call { effects, .. } | - MirInstruction::BoxCall { effects, .. } | - MirInstruction::PluginInvoke { effects, .. } => *effects, - + MirInstruction::Call { effects, .. } + | MirInstruction::BoxCall { effects, .. } + | MirInstruction::PluginInvoke { effects, .. } => *effects, + // Control flow (pure but affects execution) - MirInstruction::Branch { .. } | - MirInstruction::Jump { .. } | - MirInstruction::Return { .. } => EffectMask::PURE, - + MirInstruction::Branch { .. } + | MirInstruction::Jump { .. } + | MirInstruction::Return { .. } => EffectMask::PURE, + // Box creation may allocate MirInstruction::NewBox { .. } => EffectMask::PURE.add(Effect::Alloc), - + // Debug has debug effect MirInstruction::Debug { .. } => EffectMask::PURE.add(Effect::Debug), - + // Print has external write effect MirInstruction::Print { effects, .. } => *effects, - + // Phase 5: Control flow & exception handling MirInstruction::Throw { effects, .. } => *effects, MirInstruction::Catch { .. } => EffectMask::CONTROL, // Handler setup affects control handling - MirInstruction::Safepoint => EffectMask::PURE, // No-op for now - + MirInstruction::Safepoint => EffectMask::PURE, // No-op for now + // Phase 6: Box reference operations MirInstruction::RefNew { .. } => EffectMask::PURE, // Creating reference is pure MirInstruction::RefGet { .. } => EffectMask::READ, // Reading field has read effects @@ -459,143 +421,154 @@ impl MirInstruction { BarrierOp::Read => EffectMask::READ.add(Effect::Barrier), BarrierOp::Write => EffectMask::WRITE.add(Effect::Barrier), }, - + // Phase 7: Async/Future Operations MirInstruction::FutureNew { .. } => EffectMask::PURE.add(Effect::Alloc), // Creating future may allocate MirInstruction::FutureSet { .. } => EffectMask::WRITE, // Setting future has write effects MirInstruction::Await { .. } => EffectMask::READ.add(Effect::Async), // Await blocks and reads - + // Phase 9.7: External Function Calls MirInstruction::ExternCall { effects, .. } => *effects, // Use provided effect mask // Function value construction: treat as pure with allocation MirInstruction::FunctionNew { .. } => EffectMask::PURE.add(Effect::Alloc), } } - + /// Get the destination ValueId if this instruction produces a value pub fn dst_value(&self) -> Option { match self { - MirInstruction::Const { dst, .. } | - MirInstruction::BinOp { dst, .. } | - MirInstruction::UnaryOp { dst, .. } | - MirInstruction::Compare { dst, .. } | - MirInstruction::Load { dst, .. } | - MirInstruction::Phi { dst, .. } | - MirInstruction::NewBox { dst, .. } | - MirInstruction::TypeCheck { dst, .. } | - MirInstruction::Cast { dst, .. } | - MirInstruction::TypeOp { dst, .. } | - MirInstruction::ArrayGet { dst, .. } | - MirInstruction::Copy { dst, .. } | - MirInstruction::RefNew { dst, .. } | - MirInstruction::RefGet { dst, .. } | - MirInstruction::WeakNew { dst, .. } | - MirInstruction::WeakLoad { dst, .. } | - MirInstruction::WeakRef { dst, .. } | - MirInstruction::FutureNew { dst, .. } | - MirInstruction::Await { dst, .. } => Some(*dst), + MirInstruction::Const { dst, .. } + | MirInstruction::BinOp { dst, .. } + | MirInstruction::UnaryOp { dst, .. } + | MirInstruction::Compare { dst, .. } + | MirInstruction::Load { dst, .. } + | MirInstruction::Phi { dst, .. } + | MirInstruction::NewBox { dst, .. } + | MirInstruction::TypeCheck { dst, .. } + | MirInstruction::Cast { dst, .. } + | MirInstruction::TypeOp { dst, .. } + | MirInstruction::ArrayGet { dst, .. } + | MirInstruction::Copy { dst, .. } + | MirInstruction::RefNew { dst, .. } + | MirInstruction::RefGet { dst, .. } + | MirInstruction::WeakNew { dst, .. } + | MirInstruction::WeakLoad { dst, .. } + | MirInstruction::WeakRef { dst, .. } + | MirInstruction::FutureNew { dst, .. } + | MirInstruction::Await { dst, .. } => Some(*dst), MirInstruction::FunctionNew { dst, .. } => Some(*dst), - - MirInstruction::Call { dst, .. } | - MirInstruction::BoxCall { dst, .. } | - MirInstruction::PluginInvoke { dst, .. } | - MirInstruction::ExternCall { dst, .. } => *dst, - - MirInstruction::Store { .. } | - MirInstruction::Branch { .. } | - MirInstruction::Jump { .. } | - MirInstruction::Return { .. } | - MirInstruction::ArraySet { .. } | - MirInstruction::Debug { .. } | - MirInstruction::Print { .. } | - MirInstruction::Throw { .. } | - MirInstruction::RefSet { .. } | - MirInstruction::BarrierRead { .. } | - MirInstruction::BarrierWrite { .. } | - MirInstruction::Barrier { .. } | - MirInstruction::FutureSet { .. } | - MirInstruction::Safepoint | - MirInstruction::Nop => None, - - MirInstruction::Catch { exception_value, .. } => Some(*exception_value), + + MirInstruction::Call { dst, .. } + | MirInstruction::BoxCall { dst, .. } + | MirInstruction::PluginInvoke { dst, .. } + | MirInstruction::ExternCall { dst, .. } => *dst, + + MirInstruction::Store { .. } + | MirInstruction::Branch { .. } + | MirInstruction::Jump { .. } + | MirInstruction::Return { .. } + | MirInstruction::ArraySet { .. } + | MirInstruction::Debug { .. } + | MirInstruction::Print { .. } + | MirInstruction::Throw { .. } + | MirInstruction::RefSet { .. } + | MirInstruction::BarrierRead { .. } + | MirInstruction::BarrierWrite { .. } + | MirInstruction::Barrier { .. } + | MirInstruction::FutureSet { .. } + | MirInstruction::Safepoint + | MirInstruction::Nop => None, + + MirInstruction::Catch { + exception_value, .. + } => Some(*exception_value), } } - + /// Get all ValueIds used by this instruction pub fn used_values(&self) -> Vec { match self { - MirInstruction::Const { .. } | - MirInstruction::Jump { .. } | - MirInstruction::Nop => Vec::new(), - - MirInstruction::UnaryOp { operand, .. } | - MirInstruction::Load { ptr: operand, .. } | - MirInstruction::TypeCheck { value: operand, .. } | - MirInstruction::Cast { value: operand, .. } | - MirInstruction::TypeOp { value: operand, .. } | - MirInstruction::Copy { src: operand, .. } | - MirInstruction::Debug { value: operand, .. } | - MirInstruction::Print { value: operand, .. } => vec![*operand], - - MirInstruction::BinOp { lhs, rhs, .. } | - MirInstruction::Compare { lhs, rhs, .. } | - MirInstruction::Store { value: lhs, ptr: rhs, .. } => vec![*lhs, *rhs], - + MirInstruction::Const { .. } | MirInstruction::Jump { .. } | MirInstruction::Nop => { + Vec::new() + } + + MirInstruction::UnaryOp { operand, .. } + | MirInstruction::Load { ptr: operand, .. } + | MirInstruction::TypeCheck { value: operand, .. } + | MirInstruction::Cast { value: operand, .. } + | MirInstruction::TypeOp { value: operand, .. } + | MirInstruction::Copy { src: operand, .. } + | MirInstruction::Debug { value: operand, .. } + | MirInstruction::Print { value: operand, .. } => vec![*operand], + + MirInstruction::BinOp { lhs, rhs, .. } + | MirInstruction::Compare { lhs, rhs, .. } + | MirInstruction::Store { + value: lhs, + ptr: rhs, + .. + } => vec![*lhs, *rhs], + MirInstruction::ArrayGet { array, index, .. } => vec![*array, *index], - - MirInstruction::ArraySet { array, index, value } => vec![*array, *index, *value], - + + MirInstruction::ArraySet { + array, + index, + value, + } => vec![*array, *index, *value], + MirInstruction::Branch { condition, .. } => vec![*condition], - - MirInstruction::Return { value } => { - value.map(|v| vec![v]).unwrap_or_default() - }, - + + MirInstruction::Return { value } => value.map(|v| vec![v]).unwrap_or_default(), + MirInstruction::Call { func, args, .. } => { let mut used = vec![*func]; used.extend(args); used - }, + } MirInstruction::FunctionNew { captures, me, .. } => { let mut used: Vec = Vec::new(); used.extend(captures.iter().map(|(_, v)| *v)); - if let Some(m) = me { used.push(*m); } + if let Some(m) = me { + used.push(*m); + } used } - - MirInstruction::BoxCall { box_val, args, .. } | MirInstruction::PluginInvoke { box_val, args, .. } => { + + MirInstruction::BoxCall { box_val, args, .. } + | MirInstruction::PluginInvoke { box_val, args, .. } => { let mut used = vec![*box_val]; used.extend(args); used - }, - + } + MirInstruction::NewBox { args, .. } => args.clone(), - - MirInstruction::Phi { inputs, .. } => { - inputs.iter().map(|(_, value)| *value).collect() - }, - + + MirInstruction::Phi { inputs, .. } => inputs.iter().map(|(_, value)| *value).collect(), + // Phase 5: Control flow & exception handling MirInstruction::Throw { exception, .. } => vec![*exception], MirInstruction::Catch { .. } => Vec::new(), // Handler setup doesn't use values MirInstruction::Safepoint => Vec::new(), - + // Phase 6: Box reference operations MirInstruction::RefNew { box_val, .. } => vec![*box_val], MirInstruction::RefGet { reference, .. } => vec![*reference], - MirInstruction::RefSet { reference, value, .. } => vec![*reference, *value], + MirInstruction::RefSet { + reference, value, .. + } => vec![*reference, *value], MirInstruction::WeakNew { box_val, .. } => vec![*box_val], MirInstruction::WeakLoad { weak_ref, .. } => vec![*weak_ref], MirInstruction::BarrierRead { ptr } => vec![*ptr], MirInstruction::BarrierWrite { ptr } => vec![*ptr], MirInstruction::WeakRef { value, .. } => vec![*value], MirInstruction::Barrier { ptr, .. } => vec![*ptr], - + // Phase 7: Async/Future Operations MirInstruction::FutureNew { value, .. } => vec![*value], MirInstruction::FutureSet { future, value } => vec![*future, *value], MirInstruction::Await { future, .. } => vec![*future], - + // Phase 9.7: External Function Calls MirInstruction::ExternCall { args, .. } => args.clone(), } @@ -611,11 +584,17 @@ pub enum TypeOpKind { /// Kind of unified weak reference operation (PoC) #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum WeakRefOp { New, Load } +pub enum WeakRefOp { + New, + Load, +} /// Kind of unified barrier operation (PoC) #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum BarrierOp { Read, Write } +pub enum BarrierOp { + Read, + Write, +} impl ConstValue { /* @@ -630,7 +609,7 @@ impl ConstValue { ConstValue::Void => NyashValue::new_void(), } } - + /// Create from NyashValue pub fn from_nyash_value(value: &NyashValue) -> Option { match value { @@ -651,60 +630,126 @@ impl fmt::Display for MirInstruction { match self { MirInstruction::Const { dst, value } => { write!(f, "{} = const {}", dst, value) - }, + } MirInstruction::BinOp { dst, op, lhs, rhs } => { write!(f, "{} = {} {:?} {}", dst, lhs, op, rhs) - }, + } MirInstruction::UnaryOp { dst, op, operand } => { write!(f, "{} = {:?} {}", dst, op, operand) - }, + } MirInstruction::Compare { dst, op, lhs, rhs } => { write!(f, "{} = {} {:?} {}", dst, lhs, op, rhs) - }, + } MirInstruction::Load { dst, ptr } => { write!(f, "{} = load {}", dst, ptr) - }, + } MirInstruction::Store { value, ptr } => { write!(f, "store {} -> {}", value, ptr) - }, - MirInstruction::Call { dst, func, args, effects } => { + } + MirInstruction::Call { + dst, + func, + args, + effects, + } => { if let Some(dst) = dst { - write!(f, "{} = call {}({}); effects: {}", dst, func, - args.iter().map(|v| format!("{}", v)).collect::>().join(", "), - effects) + write!( + f, + "{} = call {}({}); effects: {}", + dst, + func, + args.iter() + .map(|v| format!("{}", v)) + .collect::>() + .join(", "), + effects + ) } else { - write!(f, "call {}({}); effects: {}", func, - args.iter().map(|v| format!("{}", v)).collect::>().join(", "), - effects) + write!( + f, + "call {}({}); effects: {}", + func, + args.iter() + .map(|v| format!("{}", v)) + .collect::>() + .join(", "), + effects + ) } - }, - MirInstruction::PluginInvoke { dst, box_val, method, args, effects: _ } => { + } + MirInstruction::PluginInvoke { + dst, + box_val, + method, + args, + effects: _, + } => { if let Some(dst) = dst { - write!(f, "{} = plugin_invoke {}.{}({})", dst, box_val, method, - args.iter().map(|v| format!("{}", v)).collect::>().join(", ")) + write!( + f, + "{} = plugin_invoke {}.{}({})", + dst, + box_val, + method, + args.iter() + .map(|v| format!("{}", v)) + .collect::>() + .join(", ") + ) } else { - write!(f, "plugin_invoke {}.{}({})", box_val, method, - args.iter().map(|v| format!("{}", v)).collect::>().join(", ")) + write!( + f, + "plugin_invoke {}.{}({})", + box_val, + method, + args.iter() + .map(|v| format!("{}", v)) + .collect::>() + .join(", ") + ) } - }, + } MirInstruction::Return { value } => { if let Some(value) = value { write!(f, "ret {}", value) } else { write!(f, "ret void") } - }, - MirInstruction::ExternCall { dst, iface_name, method_name, args, effects } => { + } + MirInstruction::ExternCall { + dst, + iface_name, + method_name, + args, + effects, + } => { if let Some(dst) = dst { - write!(f, "{} = extern_call {}.{}({}); effects: {}", dst, iface_name, method_name, - args.iter().map(|v| format!("{}", v)).collect::>().join(", "), - effects) + write!( + f, + "{} = extern_call {}.{}({}); effects: {}", + dst, + iface_name, + method_name, + args.iter() + .map(|v| format!("{}", v)) + .collect::>() + .join(", "), + effects + ) } else { - write!(f, "extern_call {}.{}({}); effects: {}", iface_name, method_name, - args.iter().map(|v| format!("{}", v)).collect::>().join(", "), - effects) + write!( + f, + "extern_call {}.{}({}); effects: {}", + iface_name, + method_name, + args.iter() + .map(|v| format!("{}", v)) + .collect::>() + .join(", "), + effects + ) } - }, + } _ => write!(f, "{:?}", self), // Fallback for other instructions } } @@ -726,7 +771,7 @@ impl fmt::Display for ConstValue { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_const_instruction() { let dst = ValueId::new(0); @@ -734,136 +779,161 @@ mod tests { dst, value: ConstValue::Integer(42), }; - + assert_eq!(inst.dst_value(), Some(dst)); assert!(inst.used_values().is_empty()); assert!(inst.effects().is_pure()); } - + #[test] fn test_binop_instruction() { let dst = ValueId::new(0); let lhs = ValueId::new(1); let rhs = ValueId::new(2); - + let inst = MirInstruction::BinOp { - dst, op: BinaryOp::Add, lhs, rhs + dst, + op: BinaryOp::Add, + lhs, + rhs, }; - + assert_eq!(inst.dst_value(), Some(dst)); assert_eq!(inst.used_values(), vec![lhs, rhs]); assert!(inst.effects().is_pure()); } - + #[test] fn test_call_instruction() { let dst = ValueId::new(0); let func = ValueId::new(1); let arg1 = ValueId::new(2); let arg2 = ValueId::new(3); - + let inst = MirInstruction::Call { dst: Some(dst), func, args: vec![arg1, arg2], effects: EffectMask::IO, }; - + assert_eq!(inst.dst_value(), Some(dst)); assert_eq!(inst.used_values(), vec![func, arg1, arg2]); assert_eq!(inst.effects(), EffectMask::IO); } - + /* #[test] fn test_const_value_conversion() { let const_val = ConstValue::Integer(42); let nyash_val = const_val.to_nyash_value(); - + assert_eq!(nyash_val, NyashValue::new_integer(42)); - + let back = ConstValue::from_nyash_value(&nyash_val).unwrap(); assert_eq!(back, const_val); } */ - + #[test] fn test_ref_new_instruction() { let dst = ValueId::new(0); let box_val = ValueId::new(1); let inst = MirInstruction::RefNew { dst, box_val }; - + assert_eq!(inst.dst_value(), Some(dst)); assert_eq!(inst.used_values(), vec![box_val]); assert!(inst.effects().is_pure()); } - + #[test] fn test_ref_get_instruction() { let dst = ValueId::new(0); let reference = ValueId::new(1); let field = "name".to_string(); - let inst = MirInstruction::RefGet { dst, reference, field }; - + let inst = MirInstruction::RefGet { + dst, + reference, + field, + }; + assert_eq!(inst.dst_value(), Some(dst)); assert_eq!(inst.used_values(), vec![reference]); assert!(!inst.effects().is_pure()); - assert!(inst.effects().contains(super::super::effect::Effect::ReadHeap)); + assert!(inst + .effects() + .contains(super::super::effect::Effect::ReadHeap)); } - + #[test] fn test_ref_set_instruction() { let reference = ValueId::new(0); let field = "value".to_string(); let value = ValueId::new(1); - let inst = MirInstruction::RefSet { reference, field, value }; - + let inst = MirInstruction::RefSet { + reference, + field, + value, + }; + assert_eq!(inst.dst_value(), None); assert_eq!(inst.used_values(), vec![reference, value]); assert!(!inst.effects().is_pure()); - assert!(inst.effects().contains(super::super::effect::Effect::WriteHeap)); + assert!(inst + .effects() + .contains(super::super::effect::Effect::WriteHeap)); } - - #[test] + + #[test] fn test_weak_new_instruction() { let dst = ValueId::new(0); let box_val = ValueId::new(1); let inst = MirInstruction::WeakNew { dst, box_val }; - + assert_eq!(inst.dst_value(), Some(dst)); assert_eq!(inst.used_values(), vec![box_val]); assert!(inst.effects().is_pure()); } - + #[test] fn test_weak_load_instruction() { let dst = ValueId::new(0); let weak_ref = ValueId::new(1); let inst = MirInstruction::WeakLoad { dst, weak_ref }; - + assert_eq!(inst.dst_value(), Some(dst)); assert_eq!(inst.used_values(), vec![weak_ref]); assert!(!inst.effects().is_pure()); - assert!(inst.effects().contains(super::super::effect::Effect::ReadHeap)); + assert!(inst + .effects() + .contains(super::super::effect::Effect::ReadHeap)); } - + #[test] fn test_barrier_instructions() { let ptr = ValueId::new(0); - + let read_barrier = MirInstruction::BarrierRead { ptr }; assert_eq!(read_barrier.dst_value(), None); assert_eq!(read_barrier.used_values(), vec![ptr]); - assert!(read_barrier.effects().contains(super::super::effect::Effect::Barrier)); - assert!(read_barrier.effects().contains(super::super::effect::Effect::ReadHeap)); - + assert!(read_barrier + .effects() + .contains(super::super::effect::Effect::Barrier)); + assert!(read_barrier + .effects() + .contains(super::super::effect::Effect::ReadHeap)); + let write_barrier = MirInstruction::BarrierWrite { ptr }; assert_eq!(write_barrier.dst_value(), None); assert_eq!(write_barrier.used_values(), vec![ptr]); - assert!(write_barrier.effects().contains(super::super::effect::Effect::Barrier)); - assert!(write_barrier.effects().contains(super::super::effect::Effect::WriteHeap)); + assert!(write_barrier + .effects() + .contains(super::super::effect::Effect::Barrier)); + assert!(write_barrier + .effects() + .contains(super::super::effect::Effect::WriteHeap)); } - + #[test] fn test_extern_call_instruction() { let dst = ValueId::new(0); @@ -876,11 +946,11 @@ mod tests { args: vec![arg1, arg2], effects: super::super::effect::EffectMask::IO, }; - + assert_eq!(inst.dst_value(), Some(dst)); assert_eq!(inst.used_values(), vec![arg1, arg2]); assert_eq!(inst.effects(), super::super::effect::EffectMask::IO); - + // Test void extern call let void_inst = MirInstruction::ExternCall { dst: None, @@ -889,7 +959,7 @@ mod tests { args: vec![arg1], effects: super::super::effect::EffectMask::IO, }; - + assert_eq!(void_inst.dst_value(), None); assert_eq!(void_inst.used_values(), vec![arg1]); } diff --git a/src/mir/instruction_introspection.rs b/src/mir/instruction_introspection.rs index 52b6a345..18b7371c 100644 --- a/src/mir/instruction_introspection.rs +++ b/src/mir/instruction_introspection.rs @@ -32,9 +32,14 @@ mod tests { #[test] fn mir14_instruction_count_is_14() { let names = mir14_instruction_names(); - assert_eq!(names.len(), 14, "MIR14 must contain exactly 14 instructions"); + assert_eq!( + names.len(), + 14, + "MIR14 must contain exactly 14 instructions" + ); let set: BTreeSet<_> = names.iter().copied().collect(); - for must in ["Const", "UnaryOp", "BoxCall"] { assert!(set.contains(must), "missing '{}'", must); } + for must in ["Const", "UnaryOp", "BoxCall"] { + assert!(set.contains(must), "missing '{}'", must); + } } } - diff --git a/src/mir/loop_api.rs b/src/mir/loop_api.rs index 049d716f..590ad5bd 100644 --- a/src/mir/loop_api.rs +++ b/src/mir/loop_api.rs @@ -26,11 +26,22 @@ pub trait LoopBuilderApi { fn new_value(&mut self) -> ValueId; /// Add predecessor edge to a block (CFG maintenance) - fn add_predecessor(&mut self, _block: BasicBlockId, _pred: BasicBlockId) -> Result<(), String> { Err("add_predecessor not implemented".into()) } + fn add_predecessor(&mut self, _block: BasicBlockId, _pred: BasicBlockId) -> Result<(), String> { + Err("add_predecessor not implemented".into()) + } /// Seal a block when all predecessors are known - fn seal_block(&mut self, _block: BasicBlockId) -> Result<(), String> { Err("seal_block not implemented".into()) } + fn seal_block(&mut self, _block: BasicBlockId) -> Result<(), String> { + Err("seal_block not implemented".into()) + } /// Insert a phi at block start - fn insert_phi_at_block_start(&mut self, _block: BasicBlockId, _dst: ValueId, _inputs: Vec<(BasicBlockId, ValueId)>) -> Result<(), String> { Err("insert_phi_at_block_start not implemented".into()) } + fn insert_phi_at_block_start( + &mut self, + _block: BasicBlockId, + _dst: ValueId, + _inputs: Vec<(BasicBlockId, ValueId)>, + ) -> Result<(), String> { + Err("insert_phi_at_block_start not implemented".into()) + } } /// Helper: simplified loop lowering usable by any LoopBuilderApi implementor @@ -48,7 +59,11 @@ pub fn build_simple_loop( // Header: branch on provided condition lb.start_new_block(header)?; - lb.emit(MirInstruction::Branch { condition, then_bb: body, else_bb: after })?; + lb.emit(MirInstruction::Branch { + condition, + then_bb: body, + else_bb: after, + })?; // Body lb.start_new_block(body)?; @@ -58,15 +73,21 @@ pub fn build_simple_loop( // After: return void value lb.start_new_block(after)?; let void_id = lb.new_value(); - lb.emit(MirInstruction::Const { dst: void_id, value: super::instruction::ConstValue::Void })?; + lb.emit(MirInstruction::Const { + dst: void_id, + value: super::instruction::ConstValue::Void, + })?; Ok(void_id) } // === Legacy wiring: implement LoopBuilderApi for mir::builder::MirBuilder === impl LoopBuilderApi for super::builder::MirBuilder { - fn new_block(&mut self) -> BasicBlockId { self.block_gen.next() } + fn new_block(&mut self) -> BasicBlockId { + self.block_gen.next() + } fn current_block(&self) -> Result { - self.current_block.ok_or_else(|| "No current block".to_string()) + self.current_block + .ok_or_else(|| "No current block".to_string()) } fn start_new_block(&mut self, block: BasicBlockId) -> Result<(), String> { super::builder::MirBuilder::start_new_block(self, block) @@ -74,15 +95,21 @@ impl LoopBuilderApi for super::builder::MirBuilder { fn emit(&mut self, inst: MirInstruction) -> Result<(), String> { super::builder::MirBuilder::emit_instruction(self, inst) } - fn new_value(&mut self) -> ValueId { self.value_gen.next() } + fn new_value(&mut self) -> ValueId { + self.value_gen.next() + } fn add_predecessor(&mut self, block: BasicBlockId, pred: BasicBlockId) -> Result<(), String> { if let Some(ref mut f) = self.current_function { if let Some(bb) = f.get_block_mut(block) { bb.add_predecessor(pred); Ok(()) - } else { Err(format!("Block {} not found", block.as_u32())) } - } else { Err("No current function".into()) } + } else { + Err(format!("Block {} not found", block.as_u32())) + } + } else { + Err("No current function".into()) + } } fn seal_block(&mut self, block: BasicBlockId) -> Result<(), String> { @@ -90,11 +117,20 @@ impl LoopBuilderApi for super::builder::MirBuilder { if let Some(bb) = f.get_block_mut(block) { bb.seal(); Ok(()) - } else { Err(format!("Block {} not found", block.as_u32())) } - } else { Err("No current function".into()) } + } else { + Err(format!("Block {} not found", block.as_u32())) + } + } else { + Err("No current function".into()) + } } - fn insert_phi_at_block_start(&mut self, block: BasicBlockId, dst: ValueId, inputs: Vec<(BasicBlockId, ValueId)>) -> Result<(), String> { + fn insert_phi_at_block_start( + &mut self, + block: BasicBlockId, + dst: ValueId, + inputs: Vec<(BasicBlockId, ValueId)>, + ) -> Result<(), String> { if let Some(ref mut f) = self.current_function { if let Some(bb) = f.get_block_mut(block) { let inst = MirInstruction::Phi { dst, inputs }; @@ -102,7 +138,11 @@ impl LoopBuilderApi for super::builder::MirBuilder { bb.effects = bb.effects | inst.effects(); bb.instructions.insert(0, inst); Ok(()) - } else { Err(format!("Block {} not found", block.as_u32())) } - } else { Err("No current function".into()) } + } else { + Err(format!("Block {} not found", block.as_u32())) + } + } else { + Err("No current function".into()) + } } } diff --git a/src/mir/loop_builder.rs b/src/mir/loop_builder.rs index d956510e..7fcebe38 100644 --- a/src/mir/loop_builder.rs +++ b/src/mir/loop_builder.rs @@ -1,14 +1,11 @@ /*! * MIR Loop Builder - SSA形式でのループ構築専用モジュール - * + * * Sealed/Unsealed blockとPhi nodeを使った正しいループ実装 * Based on Gemini's recommendation for proper SSA loop handling */ -use super::{ - MirInstruction, BasicBlockId, ValueId, - ConstValue -}; +use super::{BasicBlockId, ConstValue, MirInstruction, ValueId}; use crate::ast::ASTNode; use std::collections::{HashMap, HashSet}; @@ -27,10 +24,10 @@ struct IncompletePhi { pub struct LoopBuilder<'a> { /// 親のMIRビルダーへの参照 parent_builder: &'a mut super::builder::MirBuilder, - + /// ループ内で追跡する変数の不完全Phi node incomplete_phis: HashMap>, - + /// ブロックごとの変数マップ(スコープ管理) #[allow(dead_code)] block_var_maps: HashMap>, @@ -49,14 +46,30 @@ pub struct LoopBuilder<'a> { fn extract_assigned_var_local(ast: &ASTNode) -> Option { match ast { ASTNode::Assignment { target, .. } => { - if let ASTNode::Variable { name, .. } = target.as_ref() { Some(name.clone()) } else { None } + if let ASTNode::Variable { name, .. } = target.as_ref() { + Some(name.clone()) + } else { + None + } } - ASTNode::Program { statements, .. } => statements.last().and_then(|st| extract_assigned_var_local(st)), - ASTNode::If { then_body, else_body, .. } => { - let then_prog = ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown() }; + ASTNode::Program { statements, .. } => statements + .last() + .and_then(|st| extract_assigned_var_local(st)), + ASTNode::If { + then_body, + else_body, + .. + } => { + let then_prog = ASTNode::Program { + statements: then_body.clone(), + span: crate::ast::Span::unknown(), + }; let tvar = extract_assigned_var_local(&then_prog); let evar = else_body.as_ref().and_then(|eb| { - let ep = ASTNode::Program { statements: eb.clone(), span: crate::ast::Span::unknown() }; + let ep = ASTNode::Program { + statements: eb.clone(), + span: crate::ast::Span::unknown(), + }; extract_assigned_var_local(&ep) }); match (tvar, evar) { @@ -81,7 +94,7 @@ impl<'a> LoopBuilder<'a> { no_phi_mode, } } - + /// SSA形式でループを構築 pub fn build_loop( &mut self, @@ -96,33 +109,41 @@ impl<'a> LoopBuilder<'a> { self.loop_header = Some(header_id); self.continue_snapshots.clear(); // Push loop context to parent builder (for nested break/continue lowering) - crate::mir::builder::loops::push_loop_context(self.parent_builder, header_id, after_loop_id); - + crate::mir::builder::loops::push_loop_context( + self.parent_builder, + header_id, + after_loop_id, + ); + // 2. Preheader -> Header へのジャンプ self.emit_jump(header_id)?; let _ = self.add_predecessor(header_id, preheader_id); - + // 3. Headerブロックの準備(unsealed状態) self.set_current_block(header_id)?; let _ = self.mark_block_unsealed(header_id); - + // 4. ループ変数のPhi nodeを準備 // ここでは、ループ内で変更される可能性のある変数を事前に検出するか、 // または変数アクセス時に遅延生成する self.prepare_loop_variables(header_id, preheader_id)?; - + // 5. 条件評価(Phi nodeの結果を使用) let condition_value = self.build_expression_with_phis(condition)?; - + // 6. 条件分岐 self.emit_branch(condition_value, body_id, after_loop_id)?; let _ = self.add_predecessor(body_id, header_id); let _ = self.add_predecessor(after_loop_id, header_id); - + // 7. ループボディの構築 self.set_current_block(body_id)?; // Optional safepoint per loop-iteration - if std::env::var("NYASH_BUILDER_SAFEPOINT_LOOP").ok().as_deref() == Some("1") { + if std::env::var("NYASH_BUILDER_SAFEPOINT_LOOP") + .ok() + .as_deref() + == Some("1") + { self.emit_safepoint()?; } @@ -139,10 +160,10 @@ impl<'a> LoopBuilder<'a> { self.block_var_maps.insert(latch_id, latch_snapshot); self.emit_jump(header_id)?; let _ = self.add_predecessor(header_id, latch_id); - + // 9. Headerブロックをシール(全predecessors確定) self.seal_block(header_id, latch_id)?; - + // 10. ループ後の処理 self.set_current_block(after_loop_id)?; // Pop loop context @@ -154,7 +175,7 @@ impl<'a> LoopBuilder<'a> { Ok(void_dst) } - + /// ループ変数の準備(事前検出または遅延生成) fn prepare_loop_variables( &mut self, @@ -164,24 +185,26 @@ impl<'a> LoopBuilder<'a> { // 現在の変数マップから、ループで使用される可能性のある変数を取得 let current_vars = self.get_current_variable_map(); // preheader時点のスナップショット(後でphi入力の解析に使う) - self.block_var_maps.insert(preheader_id, current_vars.clone()); - + self.block_var_maps + .insert(preheader_id, current_vars.clone()); + // 各変数に対して不完全なPhi nodeを作成 let mut incomplete_phis = Vec::new(); for (var_name, &value_before) in ¤t_vars { let phi_id = self.new_value(); - + // 不完全なPhi nodeを作成(preheaderからの値のみ設定) let incomplete_phi = IncompletePhi { phi_id, var_name: var_name.clone(), known_inputs: vec![(preheader_id, value_before)], }; - + incomplete_phis.push(incomplete_phi); - + if self.no_phi_mode { - self.parent_builder.insert_edge_copy(preheader_id, phi_id, value_before)?; + self.parent_builder + .insert_edge_copy(preheader_id, phi_id, value_before)?; } // 変数マップを更新(Phi nodeの結果を使用) @@ -190,16 +213,12 @@ impl<'a> LoopBuilder<'a> { // 不完全なPhi nodeを記録 self.incomplete_phis.insert(header_id, incomplete_phis); - + Ok(()) } - + /// ブロックをシールし、不完全なPhi nodeを完成させる - fn seal_block( - &mut self, - block_id: BasicBlockId, - latch_id: BasicBlockId, - ) -> Result<(), String> { + fn seal_block(&mut self, block_id: BasicBlockId, latch_id: BasicBlockId) -> Result<(), String> { // 不完全なPhi nodeを取得 if let Some(incomplete_phis) = self.incomplete_phis.remove(&block_id) { for mut phi in incomplete_phis { @@ -211,9 +230,7 @@ impl<'a> LoopBuilder<'a> { let value_after = self .get_variable_at_block(&phi.var_name, latch_id) - .ok_or_else(|| { - format!("Variable {} not found at latch block", phi.var_name) - })?; + .ok_or_else(|| format!("Variable {} not found at latch block", phi.var_name))?; phi.known_inputs.push((latch_id, value_after)); @@ -221,7 +238,8 @@ impl<'a> LoopBuilder<'a> { let mut seen: HashSet = HashSet::new(); for &(pred, val) in &phi.known_inputs { if seen.insert(pred) { - self.parent_builder.insert_edge_copy(pred, phi.phi_id, val)?; + self.parent_builder + .insert_edge_copy(pred, phi.phi_id, val)?; } } } else { @@ -230,57 +248,62 @@ impl<'a> LoopBuilder<'a> { self.update_variable(phi.var_name.clone(), phi.phi_id); } } - + // ブロックをシール済みとしてマーク self.mark_block_sealed(block_id)?; - + Ok(()) } - + // --- ヘルパーメソッド(親ビルダーへの委譲) --- - + fn current_block(&self) -> Result { - self.parent_builder.current_block + self.parent_builder + .current_block .ok_or_else(|| "No current block".to_string()) } - + fn new_block(&mut self) -> BasicBlockId { self.parent_builder.block_gen.next() } - + fn new_value(&mut self) -> ValueId { self.parent_builder.value_gen.next() } - + fn set_current_block(&mut self, block_id: BasicBlockId) -> Result<(), String> { self.parent_builder.start_new_block(block_id) } - + fn emit_jump(&mut self, target: BasicBlockId) -> Result<(), String> { - self.parent_builder.emit_instruction(MirInstruction::Jump { target }) + self.parent_builder + .emit_instruction(MirInstruction::Jump { target }) } - + fn emit_branch( &mut self, condition: ValueId, then_bb: BasicBlockId, else_bb: BasicBlockId, ) -> Result<(), String> { - self.parent_builder.emit_instruction(MirInstruction::Branch { - condition, - then_bb, - else_bb, - }) + self.parent_builder + .emit_instruction(MirInstruction::Branch { + condition, + then_bb, + else_bb, + }) } - + fn emit_safepoint(&mut self) -> Result<(), String> { - self.parent_builder.emit_instruction(MirInstruction::Safepoint) + self.parent_builder + .emit_instruction(MirInstruction::Safepoint) } - + fn emit_const(&mut self, dst: ValueId, value: ConstValue) -> Result<(), String> { - self.parent_builder.emit_instruction(MirInstruction::Const { dst, value }) + self.parent_builder + .emit_instruction(MirInstruction::Const { dst, value }) } - + fn emit_phi_at_block_start( &mut self, block_id: BasicBlockId, @@ -301,7 +324,7 @@ impl<'a> LoopBuilder<'a> { Err("No current function".to_string()) } } - + fn add_predecessor(&mut self, block: BasicBlockId, pred: BasicBlockId) -> Result<(), String> { if let Some(ref mut function) = self.parent_builder.current_function { if let Some(block) = function.get_block_mut(block) { @@ -314,13 +337,13 @@ impl<'a> LoopBuilder<'a> { Err("No current function".to_string()) } } - + fn mark_block_unsealed(&mut self, _block_id: BasicBlockId) -> Result<(), String> { // ブロックはデフォルトでunsealedなので、特に何もしない // (既にBasicBlock::newでsealed: falseに初期化されている) Ok(()) } - + fn mark_block_sealed(&mut self, block_id: BasicBlockId) -> Result<(), String> { if let Some(ref mut function) = self.parent_builder.current_function { if let Some(block) = function.get_block_mut(block_id) { @@ -333,32 +356,39 @@ impl<'a> LoopBuilder<'a> { Err("No current function".to_string()) } } - + fn get_current_variable_map(&self) -> HashMap { self.parent_builder.variable_map.clone() } - + fn update_variable(&mut self, name: String, value: ValueId) { self.parent_builder.variable_map.insert(name, value); } - + fn get_variable_at_block(&self, name: &str, block_id: BasicBlockId) -> Option { // まずブロックごとのスナップショットを優先 if let Some(map) = self.block_var_maps.get(&block_id) { - if let Some(v) = map.get(name) { return Some(*v); } + if let Some(v) = map.get(name) { + return Some(*v); + } } // フォールバック:現在の変数マップ(単純ケース用) self.parent_builder.variable_map.get(name).copied() } - + fn build_expression_with_phis(&mut self, expr: ASTNode) -> Result { // Phi nodeの結果を考慮しながら式を構築 self.parent_builder.build_expression(expr) } - + fn build_statement(&mut self, stmt: ASTNode) -> Result { match stmt { - ASTNode::If { condition, then_body, else_body, .. } => { + ASTNode::If { + condition, + then_body, + else_body, + .. + } => { // Lower a simple if inside loop, ensuring continue/break inside branches are handled let cond_val = self.parent_builder.build_expression(*condition.clone())?; let then_bb = self.new_block(); @@ -378,10 +408,18 @@ impl<'a> LoopBuilder<'a> { let cur_id = self.current_block()?; let terminated = { if let Some(ref fun_ro) = self.parent_builder.current_function { - if let Some(bb) = fun_ro.get_block(cur_id) { bb.is_terminated() } else { false } - } else { false } + if let Some(bb) = fun_ro.get_block(cur_id) { + bb.is_terminated() + } else { + false + } + } else { + false + } }; - if terminated { break; } + if terminated { + break; + } } let then_var_map_end = self.get_current_variable_map(); // Only jump to merge if not already terminated (e.g., continue/break) @@ -389,25 +427,43 @@ impl<'a> LoopBuilder<'a> { let cur_id = self.current_block()?; let need_jump = { if let Some(ref fun_ro) = self.parent_builder.current_function { - if let Some(bb) = fun_ro.get_block(cur_id) { !bb.is_terminated() } else { false } - } else { false } + if let Some(bb) = fun_ro.get_block(cur_id) { + !bb.is_terminated() + } else { + false + } + } else { + false + } }; - if need_jump { self.emit_jump(merge_bb)?; } + if need_jump { + self.emit_jump(merge_bb)?; + } } // else self.set_current_block(else_bb)?; - let mut else_var_map_end_opt: Option> = None; + let mut else_var_map_end_opt: Option< + std::collections::HashMap, + > = None; if let Some(es) = else_body.clone() { for s in es.into_iter() { let _ = self.build_statement(s)?; let cur_id = self.current_block()?; let terminated = { if let Some(ref fun_ro) = self.parent_builder.current_function { - if let Some(bb) = fun_ro.get_block(cur_id) { bb.is_terminated() } else { false } - } else { false } + if let Some(bb) = fun_ro.get_block(cur_id) { + bb.is_terminated() + } else { + false + } + } else { + false + } }; - if terminated { break; } + if terminated { + break; + } } else_var_map_end_opt = Some(self.get_current_variable_map()); } @@ -415,26 +471,45 @@ impl<'a> LoopBuilder<'a> { let cur_id = self.current_block()?; let need_jump = { if let Some(ref fun_ro) = self.parent_builder.current_function { - if let Some(bb) = fun_ro.get_block(cur_id) { !bb.is_terminated() } else { false } - } else { false } + if let Some(bb) = fun_ro.get_block(cur_id) { + !bb.is_terminated() + } else { + false + } + } else { + false + } }; - if need_jump { self.emit_jump(merge_bb)?; } + if need_jump { + self.emit_jump(merge_bb)?; + } } // Continue at merge self.set_current_block(merge_bb)?; // If both branches assign the same variable, emit phi and bind it - let then_prog = ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown() }; + let then_prog = ASTNode::Program { + statements: then_body.clone(), + span: crate::ast::Span::unknown(), + }; let assigned_then = extract_assigned_var_local(&then_prog); let assigned_else = else_body.as_ref().and_then(|es| { - let ep = ASTNode::Program { statements: es.clone(), span: crate::ast::Span::unknown() }; + let ep = ASTNode::Program { + statements: es.clone(), + span: crate::ast::Span::unknown(), + }; extract_assigned_var_local(&ep) }); if let Some(var_name) = assigned_then { - let else_assigns_same = assigned_else.as_ref().map(|s| s == &var_name).unwrap_or(false); + let else_assigns_same = assigned_else + .as_ref() + .map(|s| s == &var_name) + .unwrap_or(false); let then_value_for_var = then_var_map_end.get(&var_name).copied(); let else_value_for_var = if else_assigns_same { - else_var_map_end_opt.as_ref().and_then(|m| m.get(&var_name).copied()) + else_var_map_end_opt + .as_ref() + .and_then(|m| m.get(&var_name).copied()) } else { pre_then_var_value.get(&var_name).copied() }; @@ -444,7 +519,11 @@ impl<'a> LoopBuilder<'a> { self.parent_builder.insert_edge_copy(then_bb, phi_id, tv)?; self.parent_builder.insert_edge_copy(else_bb, phi_id, ev)?; } else { - self.emit_phi_at_block_start(merge_bb, phi_id, vec![(then_bb, tv), (else_bb, ev)])?; + self.emit_phi_at_block_start( + merge_bb, + phi_id, + vec![(then_bb, tv), (else_bb, ev)], + )?; } // Reset to pre-if map and bind the phi result self.parent_builder.variable_map = pre_if_var_map.clone(); @@ -464,7 +543,8 @@ impl<'a> LoopBuilder<'a> { // In this builder, after_loop_id was created above; record it for nested lowering // We approximate by using the next block id minus 1 (after_loop) which we set below before branch } - if let Some(exit_bb) = crate::mir::builder::loops::current_exit(self.parent_builder) { + if let Some(exit_bb) = crate::mir::builder::loops::current_exit(self.parent_builder) + { self.emit_jump(exit_bb)?; let _ = self.add_predecessor(exit_bb, cur_block); } diff --git a/src/mir/mod.rs b/src/mir/mod.rs index 5df9305a..2dda230d 100644 --- a/src/mir/mod.rs +++ b/src/mir/mod.rs @@ -1,42 +1,45 @@ /*! * Nyash MIR (Mid-level Intermediate Representation) - Stage 1 Implementation - * + * * ChatGPT5-designed MIR infrastructure for native compilation support * Based on SSA form with effect tracking and Box-aware optimizations */ -pub mod instruction; -pub mod instruction_introspection; // Introspection helpers for tests (instruction names) -pub mod basic_block; -pub mod function; -pub mod builder; -pub mod loop_builder; // SSA loop construction with phi nodes -pub mod loop_api; // Minimal LoopBuilder facade (adapter-ready) -pub mod verification; -pub mod verification_types; // extracted error types -pub mod printer; -pub mod value_id; -pub mod effect; -pub mod optimizer; -pub mod optimizer_stats; // extracted stats struct -pub mod optimizer_passes; // optimizer passes (normalize/diagnostics) -pub mod slot_registry; // Phase 9.79b.1: method slot resolution (IDs) #[cfg(feature = "aot-plan-import")] pub mod aot_plan_import; -pub mod passes; // Optimization subpasses (e.g., type_hints) +pub mod basic_block; +pub mod builder; +pub mod effect; +pub mod function; +pub mod instruction; +pub mod instruction_introspection; // Introspection helpers for tests (instruction names) +pub mod loop_api; // Minimal LoopBuilder facade (adapter-ready) +pub mod loop_builder; // SSA loop construction with phi nodes +pub mod optimizer; +pub mod optimizer_passes; // optimizer passes (normalize/diagnostics) +pub mod optimizer_stats; // extracted stats struct +pub mod passes; +pub mod printer; +pub mod slot_registry; // Phase 9.79b.1: method slot resolution (IDs) +pub mod value_id; +pub mod verification; +pub mod verification_types; // extracted error types // Optimization subpasses (e.g., type_hints) // Re-export main types for easy access -pub use instruction::{MirInstruction, BinaryOp, CompareOp, UnaryOp, ConstValue, MirType, TypeOpKind, WeakRefOp, BarrierOp}; pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator}; -pub use function::{MirFunction, MirModule, FunctionSignature}; pub use builder::MirBuilder; +pub use effect::{Effect, EffectMask}; +pub use function::{FunctionSignature, MirFunction, MirModule}; +pub use instruction::{ + BarrierOp, BinaryOp, CompareOp, ConstValue, MirInstruction, MirType, TypeOpKind, UnaryOp, + WeakRefOp, +}; +pub use optimizer::MirOptimizer; +pub use printer::MirPrinter; +pub use slot_registry::{BoxTypeId, MethodSlot}; +pub use value_id::{LocalId, ValueId, ValueIdGenerator}; pub use verification::MirVerifier; pub use verification_types::VerificationError; -pub use printer::MirPrinter; -pub use value_id::{ValueId, LocalId, ValueIdGenerator}; -pub use effect::{EffectMask, Effect}; -pub use optimizer::MirOptimizer; -pub use slot_registry::{BoxTypeId, MethodSlot}; /// MIR compilation result #[derive(Debug, Clone)] @@ -69,31 +72,34 @@ impl MirCompiler { optimize, } } - + /// Compile AST to MIR module with verification pub fn compile(&mut self, ast: crate::ast::ASTNode) -> Result { // Convert AST to MIR using builder let mut module = self.builder.build_module(ast)?; - + if self.optimize { let mut optimizer = MirOptimizer::new(); let stats = optimizer.optimize_module(&mut module); - if (crate::config::env::opt_diag_fail() - || crate::config::env::opt_diag_forbid_legacy()) - && stats.diagnostics_reported > 0 { - return Err(format!("Diagnostic failure: {} issues detected (unlowered/legacy)", stats.diagnostics_reported)); + if (crate::config::env::opt_diag_fail() || crate::config::env::opt_diag_forbid_legacy()) + && stats.diagnostics_reported > 0 + { + return Err(format!( + "Diagnostic failure: {} issues detected (unlowered/legacy)", + stats.diagnostics_reported + )); } } // Verify the generated MIR let verification_result = self.verifier.verify_module(&module); - + Ok(MirCompileResult { module, verification_result, }) } - + /// Dump MIR to string for debugging pub fn dump_mir(&self, module: &MirModule) -> String { MirPrinter::new().print_module(module) @@ -110,39 +116,45 @@ impl Default for MirCompiler { mod tests { use super::*; use crate::ast::{ASTNode, LiteralValue}; - + #[test] fn test_basic_mir_compilation() { let mut compiler = MirCompiler::new(); - + // Create a simple literal AST node - let ast = ASTNode::Literal { + let ast = ASTNode::Literal { value: LiteralValue::Integer(42), - span: crate::ast::Span::unknown() + span: crate::ast::Span::unknown(), }; - + // Compile to MIR let result = compiler.compile(ast); assert!(result.is_ok(), "Basic MIR compilation should succeed"); - + let compile_result = result.unwrap(); - assert!(!compile_result.module.functions.is_empty(), "Module should contain at least one function"); + assert!( + !compile_result.module.functions.is_empty(), + "Module should contain at least one function" + ); } - + #[test] fn test_mir_dump() { let mut compiler = MirCompiler::new(); - - let ast = ASTNode::Literal { + + let ast = ASTNode::Literal { value: LiteralValue::Integer(42), - span: crate::ast::Span::unknown() + span: crate::ast::Span::unknown(), }; - + let result = compiler.compile(ast).unwrap(); let mir_dump = compiler.dump_mir(&result.module); - + assert!(!mir_dump.is_empty(), "MIR dump should not be empty"); - assert!(mir_dump.contains("define"), "MIR dump should contain function definition"); + assert!( + mir_dump.contains("define"), + "MIR dump should contain function definition" + ); } #[test] @@ -152,8 +164,14 @@ mod tests { expression: Box::new(ASTNode::FunctionCall { name: "isType".to_string(), arguments: vec![ - ASTNode::Literal { value: LiteralValue::Integer(42), span: crate::ast::Span::unknown() }, - ASTNode::Literal { value: LiteralValue::String("Integer".to_string()), span: crate::ast::Span::unknown() }, + ASTNode::Literal { + value: LiteralValue::Integer(42), + span: crate::ast::Span::unknown(), + }, + ASTNode::Literal { + value: LiteralValue::String("Integer".to_string()), + span: crate::ast::Span::unknown(), + }, ], span: crate::ast::Span::unknown(), }), @@ -165,9 +183,15 @@ mod tests { // Ensure TypeOp exists in the resulting MIR let has_typeop = result.module.functions.values().any(|f| { - f.blocks.values().any(|b| b.all_instructions().any(|i| matches!(i, MirInstruction::TypeOp { .. }))) + f.blocks.values().any(|b| { + b.all_instructions() + .any(|i| matches!(i, MirInstruction::TypeOp { .. })) + }) }); - assert!(has_typeop, "Expected TypeOp lowering for print(isType(...))"); + assert!( + has_typeop, + "Expected TypeOp lowering for print(isType(...))" + ); } #[test] @@ -175,11 +199,15 @@ mod tests { // Build AST: print( (42).is("Integer") ) let ast = ASTNode::Print { expression: Box::new(ASTNode::MethodCall { - object: Box::new(ASTNode::Literal { value: LiteralValue::Integer(42), span: crate::ast::Span::unknown() }), + object: Box::new(ASTNode::Literal { + value: LiteralValue::Integer(42), + span: crate::ast::Span::unknown(), + }), method: "is".to_string(), - arguments: vec![ - ASTNode::Literal { value: LiteralValue::String("Integer".to_string()), span: crate::ast::Span::unknown() }, - ], + arguments: vec![ASTNode::Literal { + value: LiteralValue::String("Integer".to_string()), + span: crate::ast::Span::unknown(), + }], span: crate::ast::Span::unknown(), }), span: crate::ast::Span::unknown(), @@ -190,9 +218,15 @@ mod tests { // Ensure TypeOp exists in the resulting MIR let has_typeop = result.module.functions.values().any(|f| { - f.blocks.values().any(|b| b.all_instructions().any(|i| matches!(i, MirInstruction::TypeOp { .. }))) + f.blocks.values().any(|b| { + b.all_instructions() + .any(|i| matches!(i, MirInstruction::TypeOp { .. })) + }) }); - assert!(has_typeop, "Expected TypeOp lowering for print(obj.is(...))"); + assert!( + has_typeop, + "Expected TypeOp lowering for print(obj.is(...))" + ); } #[test] @@ -200,9 +234,15 @@ mod tests { fn test_lowering_extern_console_log() { // Build AST: console.log("hi") → ExternCall env.console.log let ast = ASTNode::MethodCall { - object: Box::new(ASTNode::Variable { name: "console".to_string(), span: crate::ast::Span::unknown() }), + object: Box::new(ASTNode::Variable { + name: "console".to_string(), + span: crate::ast::Span::unknown(), + }), method: "log".to_string(), - arguments: vec![ ASTNode::Literal { value: LiteralValue::String("hi".to_string()), span: crate::ast::Span::unknown() } ], + arguments: vec![ASTNode::Literal { + value: LiteralValue::String("hi".to_string()), + span: crate::ast::Span::unknown(), + }], span: crate::ast::Span::unknown(), }; @@ -210,16 +250,28 @@ mod tests { let result = compiler.compile(ast).expect("compile should succeed"); let dump = MirPrinter::verbose().print_module(&result.module); - assert!(dump.contains("extern_call env.console.log"), "Expected extern_call env.console.log in MIR dump. Got:\n{}", dump); + assert!( + dump.contains("extern_call env.console.log"), + "Expected extern_call env.console.log in MIR dump. Got:\n{}", + dump + ); } #[test] fn test_lowering_boxcall_array_push() { // Build AST: (new ArrayBox()).push(1) let ast = ASTNode::MethodCall { - object: Box::new(ASTNode::New { class: "ArrayBox".to_string(), arguments: vec![], type_arguments: vec![], span: crate::ast::Span::unknown() }), + object: Box::new(ASTNode::New { + class: "ArrayBox".to_string(), + arguments: vec![], + type_arguments: vec![], + span: crate::ast::Span::unknown(), + }), method: "push".to_string(), - arguments: vec![ ASTNode::Literal { value: LiteralValue::Integer(1), span: crate::ast::Span::unknown() } ], + arguments: vec![ASTNode::Literal { + value: LiteralValue::Integer(1), + span: crate::ast::Span::unknown(), + }], span: crate::ast::Span::unknown(), }; @@ -227,7 +279,11 @@ mod tests { let result = compiler.compile(ast).expect("compile should succeed"); let dump = MirPrinter::new().print_module(&result.module); // Expect a BoxCall to push (printer formats as `call .(...)`) - assert!(dump.contains(".push("), "Expected BoxCall to .push(...). Got:\n{}", dump); + assert!( + dump.contains(".push("), + "Expected BoxCall to .push(...). Got:\n{}", + dump + ); } #[test] @@ -235,7 +291,12 @@ mod tests { fn test_boxcall_method_id_on_universal_slot() { // Build AST: (new ArrayBox()).toString() let ast = ASTNode::MethodCall { - object: Box::new(ASTNode::New { class: "ArrayBox".to_string(), arguments: vec![], type_arguments: vec![], span: crate::ast::Span::unknown() }), + object: Box::new(ASTNode::New { + class: "ArrayBox".to_string(), + arguments: vec![], + type_arguments: vec![], + span: crate::ast::Span::unknown(), + }), method: "toString".to_string(), arguments: vec![], span: crate::ast::Span::unknown(), @@ -245,60 +306,117 @@ mod tests { let result = compiler.compile(ast).expect("compile should succeed"); let dump = MirPrinter::new().print_module(&result.module); // Expect a BoxCall with numeric method id [#0] for toString universal slot - assert!(dump.contains("toString[#0]"), "Expected method_id #0 for toString. Dump:\n{}", dump); + assert!( + dump.contains("toString[#0]"), + "Expected method_id #0 for toString. Dump:\n{}", + dump + ); } #[test] fn test_lowering_await_expression() { - if crate::config::env::mir_core13_pure() { eprintln!("[TEST] skip await under Core-13 pure mode"); return; } + if crate::config::env::mir_core13_pure() { + eprintln!("[TEST] skip await under Core-13 pure mode"); + return; + } // Build AST: await 1 (semantic is nonsensical but should emit Await) - let ast = ASTNode::AwaitExpression { expression: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: crate::ast::Span::unknown() }), span: crate::ast::Span::unknown() }; + let ast = ASTNode::AwaitExpression { + expression: Box::new(ASTNode::Literal { + value: LiteralValue::Integer(1), + span: crate::ast::Span::unknown(), + }), + span: crate::ast::Span::unknown(), + }; let mut compiler = MirCompiler::new(); let result = compiler.compile(ast).expect("compile should succeed"); let dump = MirPrinter::new().print_module(&result.module); - assert!(dump.contains("await"), "Expected await in MIR dump. Got:\n{}", dump); + assert!( + dump.contains("await"), + "Expected await in MIR dump. Got:\n{}", + dump + ); } #[test] fn test_await_has_checkpoints() { - if crate::config::env::mir_core13_pure() { eprintln!("[TEST] skip await under Core-13 pure mode"); return; } + if crate::config::env::mir_core13_pure() { + eprintln!("[TEST] skip await under Core-13 pure mode"); + return; + } use crate::ast::{LiteralValue, Span}; // Build: await 1 - let ast = ASTNode::AwaitExpression { expression: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }), span: Span::unknown() }; + let ast = ASTNode::AwaitExpression { + expression: Box::new(ASTNode::Literal { + value: LiteralValue::Integer(1), + span: Span::unknown(), + }), + span: Span::unknown(), + }; let mut compiler = MirCompiler::new(); let result = compiler.compile(ast).expect("compile"); // Verifier should pass (await flanked by safepoints) - assert!(result.verification_result.is_ok(), "Verifier failed for await checkpoints: {:?}", result.verification_result); + assert!( + result.verification_result.is_ok(), + "Verifier failed for await checkpoints: {:?}", + result.verification_result + ); let dump = compiler.dump_mir(&result.module); // Expect at least two safepoints in the function (before/after await) let sp_count = dump.matches("safepoint").count(); - assert!(sp_count >= 2, "Expected >=2 safepoints around await, got {}. Dump:\n{}", sp_count, dump); + assert!( + sp_count >= 2, + "Expected >=2 safepoints around await, got {}. Dump:\n{}", + sp_count, + dump + ); } #[test] fn test_rewritten_await_still_checkpoints() { - if crate::config::env::mir_core13_pure() { eprintln!("[TEST] skip await under Core-13 pure mode"); return; } + if crate::config::env::mir_core13_pure() { + eprintln!("[TEST] skip await under Core-13 pure mode"); + return; + } use crate::ast::{LiteralValue, Span}; // Enable rewrite so Await → ExternCall(env.future.await) std::env::set_var("NYASH_REWRITE_FUTURE", "1"); - let ast = ASTNode::AwaitExpression { expression: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }), span: Span::unknown() }; + let ast = ASTNode::AwaitExpression { + expression: Box::new(ASTNode::Literal { + value: LiteralValue::Integer(1), + span: Span::unknown(), + }), + span: Span::unknown(), + }; let mut compiler = MirCompiler::new(); let result = compiler.compile(ast).expect("compile"); // Verifier should still pass (checkpoint verification includes ExternCall await) - assert!(result.verification_result.is_ok(), "Verifier failed for rewritten await checkpoints: {:?}", result.verification_result); + assert!( + result.verification_result.is_ok(), + "Verifier failed for rewritten await checkpoints: {:?}", + result.verification_result + ); let dump = compiler.dump_mir(&result.module); - assert!(dump.contains("env.future.await"), "Expected rewritten await extern call. Dump:\n{}", dump); + assert!( + dump.contains("env.future.await"), + "Expected rewritten await extern call. Dump:\n{}", + dump + ); let sp_count = dump.matches("safepoint").count(); - assert!(sp_count >= 2, "Expected >=2 safepoints around rewritten await, got {}. Dump:\n{}", sp_count, dump); + assert!( + sp_count >= 2, + "Expected >=2 safepoints around rewritten await, got {}. Dump:\n{}", + sp_count, + dump + ); // Cleanup env std::env::remove_var("NYASH_REWRITE_FUTURE"); } - + #[test] #[ignore = "MIR13 migration: throw/safepoint expectations pending"] fn test_throw_compilation() { let mut compiler = MirCompiler::new(); - + let throw_ast = ASTNode::Throw { expression: Box::new(ASTNode::Literal { value: LiteralValue::String("Test exception".to_string()), @@ -306,48 +424,58 @@ mod tests { }), span: crate::ast::Span::unknown(), }; - + let result = compiler.compile(throw_ast); assert!(result.is_ok(), "Throw compilation should succeed"); - + let compile_result = result.unwrap(); let mir_dump = compiler.dump_mir(&compile_result.module); - assert!(mir_dump.contains("throw"), "MIR should contain throw instruction"); - assert!(mir_dump.contains("safepoint"), "MIR should contain safepoint instruction"); + assert!( + mir_dump.contains("throw"), + "MIR should contain throw instruction" + ); + assert!( + mir_dump.contains("safepoint"), + "MIR should contain safepoint instruction" + ); } - + #[test] #[ignore = "MIR13 migration: loop safepoint expectation pending"] fn test_loop_compilation() { let mut compiler = MirCompiler::new(); - + let loop_ast = ASTNode::Loop { condition: Box::new(ASTNode::Literal { value: LiteralValue::Bool(true), span: crate::ast::Span::unknown(), }), - body: vec![ - ASTNode::Print { - expression: Box::new(ASTNode::Literal { - value: LiteralValue::String("Loop body".to_string()), - span: crate::ast::Span::unknown(), - }), + body: vec![ASTNode::Print { + expression: Box::new(ASTNode::Literal { + value: LiteralValue::String("Loop body".to_string()), span: crate::ast::Span::unknown(), - } - ], + }), + span: crate::ast::Span::unknown(), + }], span: crate::ast::Span::unknown(), }; - + let result = compiler.compile(loop_ast); assert!(result.is_ok(), "Loop compilation should succeed"); - + let compile_result = result.unwrap(); let mir_dump = compiler.dump_mir(&compile_result.module); - assert!(mir_dump.contains("br"), "MIR should contain branch instructions"); - assert!(mir_dump.contains("safepoint"), "MIR should contain safepoint instructions"); + assert!( + mir_dump.contains("br"), + "MIR should contain branch instructions" + ); + assert!( + mir_dump.contains("safepoint"), + "MIR should contain safepoint instructions" + ); } - - #[test] + + #[test] fn test_try_catch_compilation() { // Core-13 pure モードでは Try/Catch 命令は許容集合外のためスキップ if crate::config::env::mir_core13_pure() { @@ -355,42 +483,39 @@ mod tests { return; } let mut compiler = MirCompiler::new(); - + let try_catch_ast = ASTNode::TryCatch { - try_body: vec![ - ASTNode::Print { + try_body: vec![ASTNode::Print { + expression: Box::new(ASTNode::Literal { + value: LiteralValue::String("Try block".to_string()), + span: crate::ast::Span::unknown(), + }), + span: crate::ast::Span::unknown(), + }], + catch_clauses: vec![crate::ast::CatchClause { + exception_type: Some("Exception".to_string()), + variable_name: Some("e".to_string()), + body: vec![ASTNode::Print { expression: Box::new(ASTNode::Literal { - value: LiteralValue::String("Try block".to_string()), + value: LiteralValue::String("Catch block".to_string()), span: crate::ast::Span::unknown(), }), span: crate::ast::Span::unknown(), - } - ], - catch_clauses: vec![ - crate::ast::CatchClause { - exception_type: Some("Exception".to_string()), - variable_name: Some("e".to_string()), - body: vec![ - ASTNode::Print { - expression: Box::new(ASTNode::Literal { - value: LiteralValue::String("Catch block".to_string()), - span: crate::ast::Span::unknown(), - }), - span: crate::ast::Span::unknown(), - } - ], - span: crate::ast::Span::unknown(), - } - ], + }], + span: crate::ast::Span::unknown(), + }], finally_body: None, span: crate::ast::Span::unknown(), }; - + let result = compiler.compile(try_catch_ast); assert!(result.is_ok(), "TryCatch compilation should succeed"); - + let compile_result = result.unwrap(); let mir_dump = compiler.dump_mir(&compile_result.module); - assert!(mir_dump.contains("catch"), "MIR should contain catch instruction"); + assert!( + mir_dump.contains("catch"), + "MIR should contain catch instruction" + ); } } diff --git a/src/mir/optimizer.rs b/src/mir/optimizer.rs index 2aa1c6ae..be9e57ea 100644 --- a/src/mir/optimizer.rs +++ b/src/mir/optimizer.rs @@ -1,6 +1,6 @@ /*! * MIR Optimizer - Phase 3 Implementation - * + * * Implements Effect System based optimizations for the new 26-instruction MIR * - Pure instruction reordering and CSE (Common Subexpression Elimination) * - BoxFieldLoad/Store dependency analysis @@ -8,7 +8,7 @@ * - Dead code elimination */ -use super::{MirModule, MirFunction, MirInstruction, ValueId, MirType, EffectMask, Effect}; +use super::{Effect, EffectMask, MirFunction, MirInstruction, MirModule, MirType, ValueId}; use crate::mir::optimizer_stats::OptimizationStats; use std::collections::{HashMap, HashSet}; @@ -21,77 +21,87 @@ pub struct MirOptimizer { impl MirOptimizer { /// Create new optimizer pub fn new() -> Self { - Self { - debug: false, - } + Self { debug: false } } - + /// Enable debug output pub fn with_debug(mut self) -> Self { self.debug = true; self } - - + /// Run all optimization passes on a MIR module pub fn optimize_module(&mut self, module: &mut MirModule) -> OptimizationStats { let mut stats = OptimizationStats::new(); - + if self.debug { println!("🚀 Starting MIR optimization passes"); } - + // Env toggles for phased MIR cleanup let core13 = crate::config::env::mir_core13(); let mut ref_to_boxcall = crate::config::env::mir_ref_boxcall(); - if core13 { ref_to_boxcall = true; } + if core13 { + ref_to_boxcall = true; + } // Pass 0: Normalize legacy instructions to unified forms // - Includes optional Array→BoxCall guarded by env (inside the pass) - stats.merge(crate::mir::optimizer_passes::normalize::normalize_legacy_instructions(self, module)); + stats.merge( + crate::mir::optimizer_passes::normalize::normalize_legacy_instructions(self, module), + ); // Pass 0.1: RefGet/RefSet → BoxCall(getField/setField) (guarded) if ref_to_boxcall { - stats.merge(crate::mir::optimizer_passes::normalize::normalize_ref_field_access(self, module)); + stats.merge( + crate::mir::optimizer_passes::normalize::normalize_ref_field_access(self, module), + ); } - + // Option: Force BoxCall → PluginInvoke (env) - if crate::config::env::mir_plugin_invoke() - || crate::config::env::plugin_only() { + if crate::config::env::mir_plugin_invoke() || crate::config::env::plugin_only() { stats.merge(crate::mir::optimizer_passes::normalize::force_plugin_invoke(self, module)); } // Normalize Python helper form: py.getattr(obj, name) → obj.getattr(name) - stats.merge(crate::mir::optimizer_passes::normalize::normalize_python_helper_calls(self, module)); + stats.merge( + crate::mir::optimizer_passes::normalize::normalize_python_helper_calls(self, module), + ); // Pass 1: Dead code elimination (modularized pass) { let eliminated = crate::mir::passes::dce::eliminate_dead_code(module); stats.dead_code_eliminated += eliminated; } - + // Pass 2: Pure instruction CSE (modularized) { let eliminated = crate::mir::passes::cse::eliminate_common_subexpressions(module); stats.cse_eliminated += eliminated; } - + // Pass 3: Pure instruction reordering for better locality stats.merge(crate::mir::optimizer_passes::reorder::reorder_pure_instructions(self, module)); - + // Pass 4: Intrinsic function optimization - stats.merge(crate::mir::optimizer_passes::intrinsics::optimize_intrinsic_calls(self, module)); + stats.merge( + crate::mir::optimizer_passes::intrinsics::optimize_intrinsic_calls(self, module), + ); // Safety-net passesは削除(Phase 2: 変換の一本化)。診断のみ後段で実施。 - + // Pass 5: BoxField dependency optimization - stats.merge(crate::mir::optimizer_passes::boxfield::optimize_boxfield_operations(self, module)); + stats.merge( + crate::mir::optimizer_passes::boxfield::optimize_boxfield_operations(self, module), + ); // Pass 6: 受け手型ヒントの伝搬(callsite→callee) // 目的: helper(arr){ return arr.length() } のようなケースで、 // 呼び出し元の引数型(String/Integer/Bool/Float)を callee の params に反映し、 // Lowererがより正確にBox種別を選べるようにする。 let updates = crate::mir::passes::type_hints::propagate_param_type_hints(module); - if updates > 0 { stats.intrinsic_optimizations += updates as usize; } + if updates > 0 { + stats.intrinsic_optimizations += updates as usize; + } // Pass 7 (optional): Core-13 pure normalization if crate::config::env::mir_core13_pure() { @@ -102,29 +112,30 @@ impl MirOptimizer { println!("✅ Optimization complete: {}", stats); } // Diagnostics (informational): report unlowered patterns - let diag1 = crate::mir::optimizer_passes::diagnostics::diagnose_unlowered_type_ops(self, module); + let diag1 = + crate::mir::optimizer_passes::diagnostics::diagnose_unlowered_type_ops(self, module); stats.merge(diag1); // Diagnostics (policy): detect legacy (pre-unified) instructions when requested - let diag2 = crate::mir::optimizer_passes::diagnostics::diagnose_legacy_instructions(self, module); + let diag2 = + crate::mir::optimizer_passes::diagnostics::diagnose_legacy_instructions(self, module); stats.merge(diag2); - + stats } - /// Eliminate dead code (unused values) fn eliminate_dead_code(&mut self, module: &mut MirModule) -> OptimizationStats { let mut stats = OptimizationStats::new(); - + for (func_name, function) in &mut module.functions { if self.debug { println!(" 🗑️ Dead code elimination in function: {}", func_name); } - + let eliminated = self.eliminate_dead_code_in_function(function); stats.dead_code_eliminated += eliminated; } - + stats } @@ -138,7 +149,7 @@ impl MirOptimizer { /// BitNot x => BinOp(BitXor, x, Const(-1)) fn normalize_pure_core13(&mut self, module: &mut MirModule) -> OptimizationStats { use super::instruction::ConstValue; - use super::{MirInstruction as I, BinaryOp, CompareOp}; + use super::{BinaryOp, CompareOp, MirInstruction as I}; let mut stats = OptimizationStats::new(); for (_fname, function) in &mut module.functions { for (_bb, block) in &mut function.blocks { @@ -148,48 +159,91 @@ impl MirOptimizer { match inst { I::Load { dst, ptr } => { out.push(I::ExternCall { - dst: Some(dst), iface_name: "env.local".to_string(), method_name: "get".to_string(), - args: vec![ptr], effects: super::EffectMask::READ, + dst: Some(dst), + iface_name: "env.local".to_string(), + method_name: "get".to_string(), + args: vec![ptr], + effects: super::EffectMask::READ, }); stats.intrinsic_optimizations += 1; } I::Store { value, ptr } => { out.push(I::ExternCall { - dst: None, iface_name: "env.local".to_string(), method_name: "set".to_string(), - args: vec![ptr, value], effects: super::EffectMask::WRITE, + dst: None, + iface_name: "env.local".to_string(), + method_name: "set".to_string(), + args: vec![ptr, value], + effects: super::EffectMask::WRITE, }); stats.intrinsic_optimizations += 1; } - I::NewBox { dst, box_type, mut args } => { + I::NewBox { + dst, + box_type, + mut args, + } => { // prepend type name as Const String let ty_id = super::ValueId::new(function.next_value_id); function.next_value_id += 1; - out.push(I::Const { dst: ty_id, value: ConstValue::String(box_type) }); + out.push(I::Const { + dst: ty_id, + value: ConstValue::String(box_type), + }); let mut call_args = Vec::with_capacity(1 + args.len()); call_args.push(ty_id); call_args.append(&mut args); out.push(I::ExternCall { - dst: Some(dst), iface_name: "env.box".to_string(), method_name: "new".to_string(), - args: call_args, effects: super::EffectMask::PURE, // constructor is logically alloc; conservatively PURE here + dst: Some(dst), + iface_name: "env.box".to_string(), + method_name: "new".to_string(), + args: call_args, + effects: super::EffectMask::PURE, // constructor is logically alloc; conservatively PURE here }); stats.intrinsic_optimizations += 1; } I::UnaryOp { dst, op, operand } => { match op { super::UnaryOp::Neg => { - let zero = super::ValueId::new(function.next_value_id); function.next_value_id += 1; - out.push(I::Const { dst: zero, value: ConstValue::Integer(0) }); - out.push(I::BinOp { dst, op: BinaryOp::Sub, lhs: zero, rhs: operand }); + let zero = super::ValueId::new(function.next_value_id); + function.next_value_id += 1; + out.push(I::Const { + dst: zero, + value: ConstValue::Integer(0), + }); + out.push(I::BinOp { + dst, + op: BinaryOp::Sub, + lhs: zero, + rhs: operand, + }); } super::UnaryOp::Not => { - let f = super::ValueId::new(function.next_value_id); function.next_value_id += 1; - out.push(I::Const { dst: f, value: ConstValue::Bool(false) }); - out.push(I::Compare { dst, op: CompareOp::Eq, lhs: operand, rhs: f }); + let f = super::ValueId::new(function.next_value_id); + function.next_value_id += 1; + out.push(I::Const { + dst: f, + value: ConstValue::Bool(false), + }); + out.push(I::Compare { + dst, + op: CompareOp::Eq, + lhs: operand, + rhs: f, + }); } super::UnaryOp::BitNot => { - let all1 = super::ValueId::new(function.next_value_id); function.next_value_id += 1; - out.push(I::Const { dst: all1, value: ConstValue::Integer(-1) }); - out.push(I::BinOp { dst, op: BinaryOp::BitXor, lhs: operand, rhs: all1 }); + let all1 = super::ValueId::new(function.next_value_id); + function.next_value_id += 1; + out.push(I::Const { + dst: all1, + value: ConstValue::Integer(-1), + }); + out.push(I::BinOp { + dst, + op: BinaryOp::BitXor, + lhs: operand, + rhs: all1, + }); } } stats.intrinsic_optimizations += 1; @@ -200,40 +254,86 @@ impl MirOptimizer { block.instructions = out; if let Some(term) = block.terminator.take() { block.terminator = Some(match term { - I::Load { dst, ptr } => { - I::ExternCall { dst: Some(dst), iface_name: "env.local".to_string(), method_name: "get".to_string(), args: vec![ptr], effects: super::EffectMask::READ } - } - I::Store { value, ptr } => { - I::ExternCall { dst: None, iface_name: "env.local".to_string(), method_name: "set".to_string(), args: vec![ptr, value], effects: super::EffectMask::WRITE } - } - I::NewBox { dst, box_type, mut args } => { + I::Load { dst, ptr } => I::ExternCall { + dst: Some(dst), + iface_name: "env.local".to_string(), + method_name: "get".to_string(), + args: vec![ptr], + effects: super::EffectMask::READ, + }, + I::Store { value, ptr } => I::ExternCall { + dst: None, + iface_name: "env.local".to_string(), + method_name: "set".to_string(), + args: vec![ptr, value], + effects: super::EffectMask::WRITE, + }, + I::NewBox { + dst, + box_type, + mut args, + } => { let ty_id = super::ValueId::new(function.next_value_id); function.next_value_id += 1; - block.instructions.push(I::Const { dst: ty_id, value: ConstValue::String(box_type) }); + block.instructions.push(I::Const { + dst: ty_id, + value: ConstValue::String(box_type), + }); let mut call_args = Vec::with_capacity(1 + args.len()); call_args.push(ty_id); call_args.append(&mut args); - I::ExternCall { dst: Some(dst), iface_name: "env.box".to_string(), method_name: "new".to_string(), args: call_args, effects: super::EffectMask::PURE } - } - I::UnaryOp { dst, op, operand } => { - match op { - super::UnaryOp::Neg => { - let zero = super::ValueId::new(function.next_value_id); function.next_value_id += 1; - block.instructions.push(I::Const { dst: zero, value: ConstValue::Integer(0) }); - I::BinOp { dst, op: BinaryOp::Sub, lhs: zero, rhs: operand } - } - super::UnaryOp::Not => { - let f = super::ValueId::new(function.next_value_id); function.next_value_id += 1; - block.instructions.push(I::Const { dst: f, value: ConstValue::Bool(false) }); - I::Compare { dst, op: CompareOp::Eq, lhs: operand, rhs: f } - } - super::UnaryOp::BitNot => { - let all1 = super::ValueId::new(function.next_value_id); function.next_value_id += 1; - block.instructions.push(I::Const { dst: all1, value: ConstValue::Integer(-1) }); - I::BinOp { dst, op: BinaryOp::BitXor, lhs: operand, rhs: all1 } - } + I::ExternCall { + dst: Some(dst), + iface_name: "env.box".to_string(), + method_name: "new".to_string(), + args: call_args, + effects: super::EffectMask::PURE, } } + I::UnaryOp { dst, op, operand } => match op { + super::UnaryOp::Neg => { + let zero = super::ValueId::new(function.next_value_id); + function.next_value_id += 1; + block.instructions.push(I::Const { + dst: zero, + value: ConstValue::Integer(0), + }); + I::BinOp { + dst, + op: BinaryOp::Sub, + lhs: zero, + rhs: operand, + } + } + super::UnaryOp::Not => { + let f = super::ValueId::new(function.next_value_id); + function.next_value_id += 1; + block.instructions.push(I::Const { + dst: f, + value: ConstValue::Bool(false), + }); + I::Compare { + dst, + op: CompareOp::Eq, + lhs: operand, + rhs: f, + } + } + super::UnaryOp::BitNot => { + let all1 = super::ValueId::new(function.next_value_id); + function.next_value_id += 1; + block.instructions.push(I::Const { + dst: all1, + value: ConstValue::Integer(-1), + }); + I::BinOp { + dst, + op: BinaryOp::BitXor, + lhs: operand, + rhs: all1, + } + } + }, other => other, }); } @@ -241,12 +341,12 @@ impl MirOptimizer { } stats } - + /// Eliminate dead code in a single function fn eliminate_dead_code_in_function(&mut self, function: &mut MirFunction) -> usize { // Collect all used values let mut used_values = HashSet::new(); - + // Mark values used in terminators and side-effect instructions for (_, block) in &function.blocks { for instruction in &block.instructions { @@ -260,7 +360,7 @@ impl MirOptimizer { } } } - + // Mark values used in terminators if let Some(terminator) = &block.terminator { for used in terminator.used_values() { @@ -268,7 +368,7 @@ impl MirOptimizer { } } } - + // Propagate usage backwards let mut changed = true; while changed { @@ -287,7 +387,7 @@ impl MirOptimizer { } } } - + // Remove unused pure instructions let mut eliminated = 0; for (bbid, block) in &mut function.blocks { @@ -304,38 +404,38 @@ impl MirOptimizer { true }); } - + eliminated } - + /// Common Subexpression Elimination for pure instructions fn common_subexpression_elimination(&mut self, module: &mut MirModule) -> OptimizationStats { let mut stats = OptimizationStats::new(); - + for (func_name, function) in &mut module.functions { if self.debug { println!(" 🔄 CSE in function: {}", func_name); } - + let eliminated = self.cse_in_function(function); stats.cse_eliminated += eliminated; } - + stats } - + /// CSE in a single function fn cse_in_function(&mut self, function: &mut MirFunction) -> usize { let mut expression_map: HashMap = HashMap::new(); let mut replacements: HashMap = HashMap::new(); let mut eliminated = 0; - + for (_, block) in &mut function.blocks { for instruction in &mut block.instructions { // Only optimize pure instructions if instruction.effects().is_pure() { let expr_key = self.instruction_to_key(instruction); - + if let Some(&existing_value) = expression_map.get(&expr_key) { // Found common subexpression if let Some(dst) = instruction.dst_value() { @@ -351,33 +451,43 @@ impl MirOptimizer { } } } - + // Apply replacements (simplified - in full implementation would need proper SSA update) eliminated } - + /// Convert instruction to string key for CSE fn instruction_to_key(&self, instruction: &MirInstruction) -> String { match instruction { MirInstruction::Const { value, .. } => format!("const_{:?}", value), - MirInstruction::BinOp { op, lhs, rhs, .. } => format!("binop_{:?}_{}_{}", op, lhs.as_u32(), rhs.as_u32()), - MirInstruction::Compare { op, lhs, rhs, .. } => format!("cmp_{:?}_{}_{}", op, lhs.as_u32(), rhs.as_u32()), + MirInstruction::BinOp { op, lhs, rhs, .. } => { + format!("binop_{:?}_{}_{}", op, lhs.as_u32(), rhs.as_u32()) + } + MirInstruction::Compare { op, lhs, rhs, .. } => { + format!("cmp_{:?}_{}_{}", op, lhs.as_u32(), rhs.as_u32()) + } // BoxFieldLoad removed from instruction set // MirInstruction::BoxFieldLoad { box_val, field, .. } => format!("boxload_{}_{}", box_val.as_u32(), field), MirInstruction::Call { func, args, .. } => { - let args_str = args.iter().map(|v| v.as_u32().to_string()).collect::>().join(","); + let args_str = args + .iter() + .map(|v| v.as_u32().to_string()) + .collect::>() + .join(","); format!("call_{}_{}", func.as_u32(), args_str) - }, + } _ => format!("other_{:?}", instruction), } } - + // Reorder/Intrinsics/BoxField passes moved to optimizer_passes/* modules } impl MirOptimizer { /// Expose debug flag for helper modules - pub(crate) fn debug_enabled(&self) -> bool { self.debug } + pub(crate) fn debug_enabled(&self) -> bool { + self.debug + } } impl MirOptimizer { @@ -399,7 +509,7 @@ impl MirOptimizer { /// - BarrierRead/BarrierWrite → Barrier(Read/Write) /// - Print → ExternCall(env.console.log) fn normalize_legacy_instructions(&mut self, module: &mut MirModule) -> OptimizationStats { - use super::{TypeOpKind, WeakRefOp, BarrierOp, MirInstruction as I, MirType}; + use super::{BarrierOp, MirInstruction as I, MirType, TypeOpKind, WeakRefOp}; let mut stats = OptimizationStats::new(); let rw_dbg = crate::config::env::rewrite_debug(); let rw_sp = crate::config::env::rewrite_safepoint(); @@ -407,75 +517,192 @@ impl MirOptimizer { // Phase 11.8 toggles let core13 = crate::config::env::mir_core13(); let mut array_to_boxcall = crate::config::env::mir_array_boxcall(); - if core13 { array_to_boxcall = true; } + if core13 { + array_to_boxcall = true; + } for (_fname, function) in &mut module.functions { for (_bb, block) in &mut function.blocks { // Rewrite in-place for normal instructions for inst in &mut block.instructions { match inst { - I::TypeCheck { dst, value, expected_type } => { + I::TypeCheck { + dst, + value, + expected_type, + } => { let ty = MirType::Box(expected_type.clone()); - *inst = I::TypeOp { dst: *dst, op: TypeOpKind::Check, value: *value, ty }; + *inst = I::TypeOp { + dst: *dst, + op: TypeOpKind::Check, + value: *value, + ty, + }; stats.reorderings += 0; // no-op; keep stats structure alive } - I::Cast { dst, value, target_type } => { + I::Cast { + dst, + value, + target_type, + } => { let ty = target_type.clone(); - *inst = I::TypeOp { dst: *dst, op: TypeOpKind::Cast, value: *value, ty }; + *inst = I::TypeOp { + dst: *dst, + op: TypeOpKind::Cast, + value: *value, + ty, + }; } I::WeakNew { dst, box_val } => { let val = *box_val; - *inst = I::WeakRef { dst: *dst, op: WeakRefOp::New, value: val }; + *inst = I::WeakRef { + dst: *dst, + op: WeakRefOp::New, + value: val, + }; } I::WeakLoad { dst, weak_ref } => { let val = *weak_ref; - *inst = I::WeakRef { dst: *dst, op: WeakRefOp::Load, value: val }; + *inst = I::WeakRef { + dst: *dst, + op: WeakRefOp::Load, + value: val, + }; } I::BarrierRead { ptr } => { let val = *ptr; - *inst = I::Barrier { op: BarrierOp::Read, ptr: val }; + *inst = I::Barrier { + op: BarrierOp::Read, + ptr: val, + }; } I::BarrierWrite { ptr } => { let val = *ptr; - *inst = I::Barrier { op: BarrierOp::Write, ptr: val }; + *inst = I::Barrier { + op: BarrierOp::Write, + ptr: val, + }; } I::Print { value, .. } => { let v = *value; - *inst = I::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![v], effects: EffectMask::PURE.add(Effect::Io) }; + *inst = I::ExternCall { + dst: None, + iface_name: "env.console".to_string(), + method_name: "log".to_string(), + args: vec![v], + effects: EffectMask::PURE.add(Effect::Io), + }; + } + I::RefGet { .. } | I::RefSet { .. } => { /* handled in normalize_ref_field_access pass (guarded) */ } - I::RefGet { .. } | I::RefSet { .. } => { /* handled in normalize_ref_field_access pass (guarded) */ } I::ArrayGet { dst, array, index } if array_to_boxcall => { - let d = *dst; let a = *array; let i = *index; - let mid = crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "get"); - *inst = I::BoxCall { dst: Some(d), box_val: a, method: "get".to_string(), method_id: mid, args: vec![i], effects: EffectMask::READ }; + let d = *dst; + let a = *array; + let i = *index; + let mid = crate::mir::slot_registry::resolve_slot_by_type_name( + "ArrayBox", "get", + ); + *inst = I::BoxCall { + dst: Some(d), + box_val: a, + method: "get".to_string(), + method_id: mid, + args: vec![i], + effects: EffectMask::READ, + }; } - I::ArraySet { array, index, value } if array_to_boxcall => { - let a = *array; let i = *index; let v = *value; - let mid = crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "set"); - *inst = I::BoxCall { dst: None, box_val: a, method: "set".to_string(), method_id: mid, args: vec![i, v], effects: EffectMask::WRITE }; + I::ArraySet { + array, + index, + value, + } if array_to_boxcall => { + let a = *array; + let i = *index; + let v = *value; + let mid = crate::mir::slot_registry::resolve_slot_by_type_name( + "ArrayBox", "set", + ); + *inst = I::BoxCall { + dst: None, + box_val: a, + method: "set".to_string(), + method_id: mid, + args: vec![i, v], + effects: EffectMask::WRITE, + }; } - I::PluginInvoke { dst, box_val, method, args, effects } => { - let d = *dst; let recv = *box_val; let m = method.clone(); let as_ = args.clone(); let eff = *effects; - *inst = I::BoxCall { dst: d, box_val: recv, method: m, method_id: None, args: as_, effects: eff }; + I::PluginInvoke { + dst, + box_val, + method, + args, + effects, + } => { + let d = *dst; + let recv = *box_val; + let m = method.clone(); + let as_ = args.clone(); + let eff = *effects; + *inst = I::BoxCall { + dst: d, + box_val: recv, + method: m, + method_id: None, + args: as_, + effects: eff, + }; } I::Debug { value, .. } if rw_dbg => { let v = *value; - *inst = I::ExternCall { dst: None, iface_name: "env.debug".to_string(), method_name: "trace".to_string(), args: vec![v], effects: EffectMask::PURE.add(Effect::Debug) }; + *inst = I::ExternCall { + dst: None, + iface_name: "env.debug".to_string(), + method_name: "trace".to_string(), + args: vec![v], + effects: EffectMask::PURE.add(Effect::Debug), + }; } I::Safepoint if rw_sp => { - *inst = I::ExternCall { dst: None, iface_name: "env.runtime".to_string(), method_name: "checkpoint".to_string(), args: vec![], effects: EffectMask::PURE }; + *inst = I::ExternCall { + dst: None, + iface_name: "env.runtime".to_string(), + method_name: "checkpoint".to_string(), + args: vec![], + effects: EffectMask::PURE, + }; } // Future/Await の段階移行: ExternCall(env.future.*) に書き換え(トグル) I::FutureNew { dst, value } if rw_future => { - let d = *dst; let v = *value; - *inst = I::ExternCall { dst: Some(d), iface_name: "env.future".to_string(), method_name: "new".to_string(), args: vec![v], effects: EffectMask::PURE.add(Effect::Io) }; + let d = *dst; + let v = *value; + *inst = I::ExternCall { + dst: Some(d), + iface_name: "env.future".to_string(), + method_name: "new".to_string(), + args: vec![v], + effects: EffectMask::PURE.add(Effect::Io), + }; } I::FutureSet { future, value } if rw_future => { - let f = *future; let v = *value; - *inst = I::ExternCall { dst: None, iface_name: "env.future".to_string(), method_name: "set".to_string(), args: vec![f, v], effects: EffectMask::PURE.add(Effect::Io) }; + let f = *future; + let v = *value; + *inst = I::ExternCall { + dst: None, + iface_name: "env.future".to_string(), + method_name: "set".to_string(), + args: vec![f, v], + effects: EffectMask::PURE.add(Effect::Io), + }; } I::Await { dst, future } if rw_future => { - let d = *dst; let f = *future; - *inst = I::ExternCall { dst: Some(d), iface_name: "env.future".to_string(), method_name: "await".to_string(), args: vec![f], effects: EffectMask::PURE.add(Effect::Io) }; + let d = *dst; + let f = *future; + *inst = I::ExternCall { + dst: Some(d), + iface_name: "env.future".to_string(), + method_name: "await".to_string(), + args: vec![f], + effects: EffectMask::PURE.add(Effect::Io), + }; } _ => {} } @@ -483,65 +710,172 @@ impl MirOptimizer { // Rewrite terminator, if any if let Some(term) = &mut block.terminator { match term { - I::TypeCheck { dst, value, expected_type } => { + I::TypeCheck { + dst, + value, + expected_type, + } => { let ty = MirType::Box(expected_type.clone()); - *term = I::TypeOp { dst: *dst, op: TypeOpKind::Check, value: *value, ty }; + *term = I::TypeOp { + dst: *dst, + op: TypeOpKind::Check, + value: *value, + ty, + }; } - I::Cast { dst, value, target_type } => { + I::Cast { + dst, + value, + target_type, + } => { let ty = target_type.clone(); - *term = I::TypeOp { dst: *dst, op: TypeOpKind::Cast, value: *value, ty }; + *term = I::TypeOp { + dst: *dst, + op: TypeOpKind::Cast, + value: *value, + ty, + }; } I::WeakNew { dst, box_val } => { let val = *box_val; - *term = I::WeakRef { dst: *dst, op: WeakRefOp::New, value: val }; + *term = I::WeakRef { + dst: *dst, + op: WeakRefOp::New, + value: val, + }; } I::WeakLoad { dst, weak_ref } => { let val = *weak_ref; - *term = I::WeakRef { dst: *dst, op: WeakRefOp::Load, value: val }; + *term = I::WeakRef { + dst: *dst, + op: WeakRefOp::Load, + value: val, + }; } I::BarrierRead { ptr } => { let val = *ptr; - *term = I::Barrier { op: BarrierOp::Read, ptr: val }; + *term = I::Barrier { + op: BarrierOp::Read, + ptr: val, + }; } I::BarrierWrite { ptr } => { let val = *ptr; - *term = I::Barrier { op: BarrierOp::Write, ptr: val }; + *term = I::Barrier { + op: BarrierOp::Write, + ptr: val, + }; } I::Print { value, .. } => { let v = *value; - *term = I::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![v], effects: EffectMask::PURE.add(Effect::Io) }; + *term = I::ExternCall { + dst: None, + iface_name: "env.console".to_string(), + method_name: "log".to_string(), + args: vec![v], + effects: EffectMask::PURE.add(Effect::Io), + }; + } + I::RefGet { .. } | I::RefSet { .. } => { /* handled in normalize_ref_field_access pass (guarded) */ } - I::RefGet { .. } | I::RefSet { .. } => { /* handled in normalize_ref_field_access pass (guarded) */ } I::ArrayGet { dst, array, index } if array_to_boxcall => { - let mid = crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "get"); - *term = I::BoxCall { dst: Some(*dst), box_val: *array, method: "get".to_string(), method_id: mid, args: vec![*index], effects: EffectMask::READ }; + let mid = crate::mir::slot_registry::resolve_slot_by_type_name( + "ArrayBox", "get", + ); + *term = I::BoxCall { + dst: Some(*dst), + box_val: *array, + method: "get".to_string(), + method_id: mid, + args: vec![*index], + effects: EffectMask::READ, + }; } - I::ArraySet { array, index, value } if array_to_boxcall => { - let mid = crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "set"); - *term = I::BoxCall { dst: None, box_val: *array, method: "set".to_string(), method_id: mid, args: vec![*index, *value], effects: EffectMask::WRITE }; + I::ArraySet { + array, + index, + value, + } if array_to_boxcall => { + let mid = crate::mir::slot_registry::resolve_slot_by_type_name( + "ArrayBox", "set", + ); + *term = I::BoxCall { + dst: None, + box_val: *array, + method: "set".to_string(), + method_id: mid, + args: vec![*index, *value], + effects: EffectMask::WRITE, + }; } - I::PluginInvoke { dst, box_val, method, args, effects } => { - *term = I::BoxCall { dst: *dst, box_val: *box_val, method: method.clone(), method_id: None, args: args.clone(), effects: *effects }; + I::PluginInvoke { + dst, + box_val, + method, + args, + effects, + } => { + *term = I::BoxCall { + dst: *dst, + box_val: *box_val, + method: method.clone(), + method_id: None, + args: args.clone(), + effects: *effects, + }; } I::Debug { value, .. } if rw_dbg => { let v = *value; - *term = I::ExternCall { dst: None, iface_name: "env.debug".to_string(), method_name: "trace".to_string(), args: vec![v], effects: EffectMask::PURE.add(Effect::Debug) }; + *term = I::ExternCall { + dst: None, + iface_name: "env.debug".to_string(), + method_name: "trace".to_string(), + args: vec![v], + effects: EffectMask::PURE.add(Effect::Debug), + }; } I::Safepoint if rw_sp => { - *term = I::ExternCall { dst: None, iface_name: "env.runtime".to_string(), method_name: "checkpoint".to_string(), args: vec![], effects: EffectMask::PURE }; + *term = I::ExternCall { + dst: None, + iface_name: "env.runtime".to_string(), + method_name: "checkpoint".to_string(), + args: vec![], + effects: EffectMask::PURE, + }; } // Future/Await (終端側) I::FutureNew { dst, value } if rw_future => { - let d = *dst; let v = *value; - *term = I::ExternCall { dst: Some(d), iface_name: "env.future".to_string(), method_name: "new".to_string(), args: vec![v], effects: EffectMask::PURE.add(Effect::Io) }; + let d = *dst; + let v = *value; + *term = I::ExternCall { + dst: Some(d), + iface_name: "env.future".to_string(), + method_name: "new".to_string(), + args: vec![v], + effects: EffectMask::PURE.add(Effect::Io), + }; } I::FutureSet { future, value } if rw_future => { - let f = *future; let v = *value; - *term = I::ExternCall { dst: None, iface_name: "env.future".to_string(), method_name: "set".to_string(), args: vec![f, v], effects: EffectMask::PURE.add(Effect::Io) }; + let f = *future; + let v = *value; + *term = I::ExternCall { + dst: None, + iface_name: "env.future".to_string(), + method_name: "set".to_string(), + args: vec![f, v], + effects: EffectMask::PURE.add(Effect::Io), + }; } I::Await { dst, future } if rw_future => { - let d = *dst; let f = *future; - *term = I::ExternCall { dst: Some(d), iface_name: "env.future".to_string(), method_name: "await".to_string(), args: vec![f], effects: EffectMask::PURE.add(Effect::Io) }; + let d = *dst; + let f = *future; + *term = I::ExternCall { + dst: Some(d), + iface_name: "env.future".to_string(), + method_name: "await".to_string(), + args: vec![f], + effects: EffectMask::PURE.add(Effect::Io), + }; } _ => {} } @@ -553,7 +887,7 @@ impl MirOptimizer { /// Normalize RefGet/RefSet to BoxCall("getField"/"setField") with Const String field argument. fn normalize_ref_field_access(&mut self, module: &mut MirModule) -> OptimizationStats { - use super::{MirInstruction as I, BarrierOp}; + use super::{BarrierOp, MirInstruction as I}; let mut stats = OptimizationStats::new(); for (_fname, function) in &mut module.functions { for (_bb, block) in &mut function.blocks { @@ -561,20 +895,51 @@ impl MirOptimizer { let old = std::mem::take(&mut block.instructions); for inst in old.into_iter() { match inst { - I::RefGet { dst, reference, field } => { + I::RefGet { + dst, + reference, + field, + } => { let new_id = super::ValueId::new(function.next_value_id); function.next_value_id += 1; - out.push(I::Const { dst: new_id, value: super::instruction::ConstValue::String(field) }); - out.push(I::BoxCall { dst: Some(dst), box_val: reference, method: "getField".to_string(), method_id: None, args: vec![new_id], effects: super::EffectMask::READ }); + out.push(I::Const { + dst: new_id, + value: super::instruction::ConstValue::String(field), + }); + out.push(I::BoxCall { + dst: Some(dst), + box_val: reference, + method: "getField".to_string(), + method_id: None, + args: vec![new_id], + effects: super::EffectMask::READ, + }); stats.intrinsic_optimizations += 1; } - I::RefSet { reference, field, value } => { + I::RefSet { + reference, + field, + value, + } => { let new_id = super::ValueId::new(function.next_value_id); function.next_value_id += 1; - out.push(I::Const { dst: new_id, value: super::instruction::ConstValue::String(field) }); + out.push(I::Const { + dst: new_id, + value: super::instruction::ConstValue::String(field), + }); // Prepend an explicit write barrier before setField to make side-effects visible - out.push(I::Barrier { op: BarrierOp::Write, ptr: reference }); - out.push(I::BoxCall { dst: None, box_val: reference, method: "setField".to_string(), method_id: None, args: vec![new_id, value], effects: super::EffectMask::WRITE }); + out.push(I::Barrier { + op: BarrierOp::Write, + ptr: reference, + }); + out.push(I::BoxCall { + dst: None, + box_val: reference, + method: "setField".to_string(), + method_id: None, + args: vec![new_id, value], + effects: super::EffectMask::WRITE, + }); stats.intrinsic_optimizations += 1; } other => out.push(other), @@ -584,18 +949,49 @@ impl MirOptimizer { if let Some(term) = block.terminator.take() { block.terminator = Some(match term { - I::RefGet { dst, reference, field } => { + I::RefGet { + dst, + reference, + field, + } => { let new_id = super::ValueId::new(function.next_value_id); function.next_value_id += 1; - block.instructions.push(I::Const { dst: new_id, value: super::instruction::ConstValue::String(field) }); - I::BoxCall { dst: Some(dst), box_val: reference, method: "getField".to_string(), method_id: None, args: vec![new_id], effects: super::EffectMask::READ } + block.instructions.push(I::Const { + dst: new_id, + value: super::instruction::ConstValue::String(field), + }); + I::BoxCall { + dst: Some(dst), + box_val: reference, + method: "getField".to_string(), + method_id: None, + args: vec![new_id], + effects: super::EffectMask::READ, + } } - I::RefSet { reference, field, value } => { + I::RefSet { + reference, + field, + value, + } => { let new_id = super::ValueId::new(function.next_value_id); function.next_value_id += 1; - block.instructions.push(I::Const { dst: new_id, value: super::instruction::ConstValue::String(field) }); - block.instructions.push(I::Barrier { op: BarrierOp::Write, ptr: reference }); - I::BoxCall { dst: None, box_val: reference, method: "setField".to_string(), method_id: None, args: vec![new_id, value], effects: super::EffectMask::WRITE } + block.instructions.push(I::Const { + dst: new_id, + value: super::instruction::ConstValue::String(field), + }); + block.instructions.push(I::Barrier { + op: BarrierOp::Write, + ptr: reference, + }); + I::BoxCall { + dst: None, + box_val: reference, + method: "setField".to_string(), + method_id: None, + args: vec![new_id, value], + effects: super::EffectMask::WRITE, + } } other => other, }); @@ -618,8 +1014,14 @@ fn map_type_name(name: &str) -> MirType { } } -fn opt_debug_enabled() -> bool { crate::config::env::opt_debug() } -fn opt_debug(msg: &str) { if opt_debug_enabled() { eprintln!("[OPT] {}", msg); } } +fn opt_debug_enabled() -> bool { + crate::config::env::opt_debug() +} +fn opt_debug(msg: &str) { + if opt_debug_enabled() { + eprintln!("[OPT] {}", msg); + } +} /// Resolve a MIR type from a value id that should represent a type name /// Supports: Const String("T") and NewBox(StringBox, Const String("T")) @@ -633,15 +1035,24 @@ fn resolve_type_from_value( if let Some(block) = function.blocks.get(&bb) { if idx < block.instructions.len() { match &block.instructions[idx] { - MirInstruction::Const { value: ConstValue::String(s), .. } => { + MirInstruction::Const { + value: ConstValue::String(s), + .. + } => { return Some(map_type_name(s)); } - MirInstruction::NewBox { box_type, args, .. } if box_type == "StringBox" && args.len() == 1 => { + MirInstruction::NewBox { box_type, args, .. } + if box_type == "StringBox" && args.len() == 1 => + { let inner = args[0]; if let Some((sbb, sidx)) = def_map.get(&inner).copied() { if let Some(sblock) = function.blocks.get(&sbb) { if sidx < sblock.instructions.len() { - if let MirInstruction::Const { value: ConstValue::String(s), .. } = &sblock.instructions[sidx] { + if let MirInstruction::Const { + value: ConstValue::String(s), + .. + } = &sblock.instructions[sidx] + { return Some(map_type_name(s)); } } @@ -671,24 +1082,46 @@ impl MirOptimizer { let diag_on = self.debug || crate::config::env::opt_diag(); for (fname, function) in &module.functions { // def map for resolving constants - let mut def_map: std::collections::HashMap = std::collections::HashMap::new(); + let mut def_map: std::collections::HashMap< + ValueId, + (super::basic_block::BasicBlockId, usize), + > = std::collections::HashMap::new(); for (bb_id, block) in &function.blocks { for (i, inst) in block.instructions.iter().enumerate() { - if let Some(dst) = inst.dst_value() { def_map.insert(dst, (*bb_id, i)); } + if let Some(dst) = inst.dst_value() { + def_map.insert(dst, (*bb_id, i)); + } + } + if let Some(term) = &block.terminator { + if let Some(dst) = term.dst_value() { + def_map.insert(dst, (*bb_id, usize::MAX)); + } } - if let Some(term) = &block.terminator { if let Some(dst) = term.dst_value() { def_map.insert(dst, (*bb_id, usize::MAX)); } } } let mut count = 0usize; for (_bb, block) in &function.blocks { for inst in &block.instructions { match inst { - MirInstruction::BoxCall { method, .. } if method == "is" || method == "as" || method == "isType" || method == "asType" => { count += 1; } + MirInstruction::BoxCall { method, .. } + if method == "is" + || method == "as" + || method == "isType" + || method == "asType" => + { + count += 1; + } MirInstruction::Call { func, .. } => { if let Some((bb, idx)) = def_map.get(func).copied() { if let Some(b) = function.blocks.get(&bb) { if idx < b.instructions.len() { - if let MirInstruction::Const { value: super::instruction::ConstValue::String(s), .. } = &b.instructions[idx] { - if s == "isType" || s == "asType" { count += 1; } + if let MirInstruction::Const { + value: super::instruction::ConstValue::String(s), + .. + } = &b.instructions[idx] + { + if s == "isType" || s == "asType" { + count += 1; + } } } } @@ -701,7 +1134,10 @@ impl MirOptimizer { if count > 0 { stats.diagnostics_reported += count; if diag_on { - eprintln!("[OPT][DIAG] Function '{}' has {} unlowered type-op calls", fname, count); + eprintln!( + "[OPT][DIAG] Function '{}' has {} unlowered type-op calls", + fname, count + ); } } } @@ -731,7 +1167,9 @@ impl MirOptimizer { | MirInstruction::ArraySet { .. } | MirInstruction::RefGet { .. } | MirInstruction::RefSet { .. } - | MirInstruction::PluginInvoke { .. } => { count += 1; } + | MirInstruction::PluginInvoke { .. } => { + count += 1; + } _ => {} } } @@ -747,7 +1185,9 @@ impl MirOptimizer { | MirInstruction::ArraySet { .. } | MirInstruction::RefGet { .. } | MirInstruction::RefSet { .. } - | MirInstruction::PluginInvoke { .. } => { count += 1; } + | MirInstruction::PluginInvoke { .. } => { + count += 1; + } _ => {} } } @@ -760,7 +1200,10 @@ impl MirOptimizer { fname, count ); if crate::config::env::opt_diag_forbid_legacy() { - panic!("NYASH_OPT_DIAG_FORBID_LEGACY=1: legacy MIR ops detected in '{}': {}", fname, count); + panic!( + "NYASH_OPT_DIAG_FORBID_LEGACY=1: legacy MIR ops detected in '{}': {}", + fname, count + ); } } } @@ -772,47 +1215,50 @@ impl MirOptimizer { #[cfg(test)] mod tests { use super::*; - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirType, TypeOpKind, BasicBlock, BasicBlockId, ValueId, ConstValue}; - + use crate::mir::{ + BasicBlock, BasicBlockId, ConstValue, FunctionSignature, MirFunction, MirModule, MirType, + TypeOpKind, ValueId, + }; + #[test] fn test_optimizer_creation() { let optimizer = MirOptimizer::new(); assert!(!optimizer.debug); - + let debug_optimizer = MirOptimizer::new().with_debug(); assert!(debug_optimizer.debug); } - + #[test] fn test_optimization_stats() { let mut stats = OptimizationStats::new(); assert_eq!(stats.total_optimizations(), 0); - + stats.dead_code_eliminated = 5; stats.cse_eliminated = 3; assert_eq!(stats.total_optimizations(), 8); - + let other_stats = OptimizationStats { dead_code_eliminated: 2, cse_eliminated: 1, ..Default::default() }; - + stats.merge(other_stats); assert_eq!(stats.dead_code_eliminated, 7); assert_eq!(stats.cse_eliminated, 4); assert_eq!(stats.total_optimizations(), 11); } - + #[test] fn test_instruction_to_key() { let optimizer = MirOptimizer::new(); - + let const_instr = MirInstruction::Const { dst: ValueId::new(1), value: ConstValue::Integer(42), }; - + let key = optimizer.instruction_to_key(&const_instr); assert!(key.contains("const")); assert!(key.contains("42")); @@ -832,9 +1278,24 @@ mod tests { let mut b0 = BasicBlock::new(bb0); let v0 = ValueId::new(0); let v1 = ValueId::new(1); - b0.add_instruction(MirInstruction::NewBox { dst: v0, box_type: "IntegerBox".to_string(), args: vec![] }); - b0.add_instruction(MirInstruction::TypeOp { dst: v1, op: TypeOpKind::Check, value: v0, ty: MirType::Integer }); - b0.add_instruction(MirInstruction::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![v1], effects: super::super::effect::EffectMask::IO }); + b0.add_instruction(MirInstruction::NewBox { + dst: v0, + box_type: "IntegerBox".to_string(), + args: vec![], + }); + b0.add_instruction(MirInstruction::TypeOp { + dst: v1, + op: TypeOpKind::Check, + value: v0, + ty: MirType::Integer, + }); + b0.add_instruction(MirInstruction::ExternCall { + dst: None, + iface_name: "env.console".to_string(), + method_name: "log".to_string(), + args: vec![v1], + effects: super::super::effect::EffectMask::IO, + }); b0.add_instruction(MirInstruction::Return { value: None }); func.add_block(b0); let mut module = MirModule::new("test".to_string()); @@ -846,7 +1307,12 @@ mod tests { // Ensure TypeOp remains in bb0 let f = module.get_function("main").unwrap(); let block = f.get_block(bb0).unwrap(); - let has_typeop = block.all_instructions().any(|i| matches!(i, MirInstruction::TypeOp { .. })); - assert!(has_typeop, "TypeOp should not be dropped by DCE when used by console.log (ExternCall)"); + let has_typeop = block + .all_instructions() + .any(|i| matches!(i, MirInstruction::TypeOp { .. })); + assert!( + has_typeop, + "TypeOp should not be dropped by DCE when used by console.log (ExternCall)" + ); } } diff --git a/src/mir/optimizer_passes/boxfield.rs b/src/mir/optimizer_passes/boxfield.rs index a9f77ada..289cfe85 100644 --- a/src/mir/optimizer_passes/boxfield.rs +++ b/src/mir/optimizer_passes/boxfield.rs @@ -1,12 +1,17 @@ -use crate::mir::{MirModule, MirInstruction as I, EffectMask}; use crate::mir::optimizer::MirOptimizer; use crate::mir::optimizer_stats::OptimizationStats; +use crate::mir::{EffectMask, MirInstruction as I, MirModule}; /// Optimize BoxField operations (scaffolding) -pub fn optimize_boxfield_operations(opt: &mut MirOptimizer, module: &mut MirModule) -> OptimizationStats { +pub fn optimize_boxfield_operations( + opt: &mut MirOptimizer, + module: &mut MirModule, +) -> OptimizationStats { let mut stats = OptimizationStats::new(); for (func_name, function) in &mut module.functions { - if opt.debug_enabled() { println!(" 📦 BoxField optimization in function: {}", func_name); } + if opt.debug_enabled() { + println!(" 📦 BoxField optimization in function: {}", func_name); + } for (_bb_id, block) in &mut function.blocks { let mut changed = 0usize; let mut out: Vec = Vec::with_capacity(block.instructions.len()); @@ -16,15 +21,31 @@ pub fn optimize_boxfield_operations(opt: &mut MirOptimizer, module: &mut MirModu if i + 1 < block.instructions.len() { match (&block.instructions[i], &block.instructions[i + 1]) { ( - I::BoxCall { box_val: b1, method: m1, args: a1, .. }, - I::BoxCall { dst: Some(dst2), box_val: b2, method: m2, args: a2, .. }, - ) if (m1 == "set" || m1 == "setField") && (m2 == "get" || m2 == "getField") => { + I::BoxCall { + box_val: b1, + method: m1, + args: a1, + .. + }, + I::BoxCall { + dst: Some(dst2), + box_val: b2, + method: m2, + args: a2, + .. + }, + ) if (m1 == "set" || m1 == "setField") + && (m2 == "get" || m2 == "getField") => + { // set(arg0=index/key, arg1=value), then get(arg0=index/key) if b1 == b2 && a1.len() == 2 && a2.len() == 1 && a1[0] == a2[0] { // Replace the second with Copy from just-stored value let src_val = a1[1]; out.push(block.instructions[i].clone()); - out.push(I::Copy { dst: *dst2, src: src_val }); + out.push(I::Copy { + dst: *dst2, + src: src_val, + }); changed += 1; i += 2; continue; diff --git a/src/mir/optimizer_passes/diagnostics.rs b/src/mir/optimizer_passes/diagnostics.rs index a6f9c781..4950e931 100644 --- a/src/mir/optimizer_passes/diagnostics.rs +++ b/src/mir/optimizer_passes/diagnostics.rs @@ -1,30 +1,53 @@ -use crate::mir::{MirModule, MirInstruction, BasicBlockId, function::MirFunction, ValueId}; -use crate::mir::optimizer_stats::OptimizationStats; use crate::mir::optimizer::MirOptimizer; +use crate::mir::optimizer_stats::OptimizationStats; +use crate::mir::{function::MirFunction, BasicBlockId, MirInstruction, MirModule, ValueId}; /// Diagnostic: detect unlowered is/as/isType/asType after Builder -pub fn diagnose_unlowered_type_ops(opt: &mut MirOptimizer, module: &MirModule) -> OptimizationStats { +pub fn diagnose_unlowered_type_ops( + opt: &mut MirOptimizer, + module: &MirModule, +) -> OptimizationStats { let mut stats = OptimizationStats::new(); let diag_on = opt.debug_enabled() || crate::config::env::opt_diag(); for (fname, function) in &module.functions { - let mut def_map: std::collections::HashMap = std::collections::HashMap::new(); + let mut def_map: std::collections::HashMap = + std::collections::HashMap::new(); for (bb_id, block) in &function.blocks { for (i, inst) in block.instructions.iter().enumerate() { - if let Some(dst) = inst.dst_value() { def_map.insert(dst, (*bb_id, i)); } + if let Some(dst) = inst.dst_value() { + def_map.insert(dst, (*bb_id, i)); + } + } + if let Some(term) = &block.terminator { + if let Some(dst) = term.dst_value() { + def_map.insert(dst, (*bb_id, usize::MAX)); + } } - if let Some(term) = &block.terminator { if let Some(dst) = term.dst_value() { def_map.insert(dst, (*bb_id, usize::MAX)); } } } let mut count = 0usize; for (_bb, block) in &function.blocks { for inst in &block.instructions { match inst { - MirInstruction::BoxCall { method, .. } if method == "is" || method == "as" || method == "isType" || method == "asType" => { count += 1; } + MirInstruction::BoxCall { method, .. } + if method == "is" + || method == "as" + || method == "isType" + || method == "asType" => + { + count += 1; + } MirInstruction::Call { func, .. } => { if let Some((bb, idx)) = def_map.get(func).copied() { if let Some(b) = function.blocks.get(&bb) { if idx < b.instructions.len() { - if let MirInstruction::Const { value: crate::mir::instruction::ConstValue::String(s), .. } = &b.instructions[idx] { - if s == "isType" || s == "asType" { count += 1; } + if let MirInstruction::Const { + value: crate::mir::instruction::ConstValue::String(s), + .. + } = &b.instructions[idx] + { + if s == "isType" || s == "asType" { + count += 1; + } } } } @@ -37,7 +60,10 @@ pub fn diagnose_unlowered_type_ops(opt: &mut MirOptimizer, module: &MirModule) - if count > 0 { stats.diagnostics_reported += count; if diag_on { - eprintln!("[OPT][DIAG] Function '{}' has {} unlowered type-op calls", fname, count); + eprintln!( + "[OPT][DIAG] Function '{}' has {} unlowered type-op calls", + fname, count + ); } } } @@ -46,7 +72,10 @@ pub fn diagnose_unlowered_type_ops(opt: &mut MirOptimizer, module: &MirModule) - /// Diagnostic: detect legacy instructions that should be unified /// Legacy set: TypeCheck/Cast/WeakNew/WeakLoad/BarrierRead/BarrierWrite/ArrayGet/ArraySet/RefGet/RefSet/PluginInvoke -pub fn diagnose_legacy_instructions(opt: &mut MirOptimizer, module: &MirModule) -> OptimizationStats { +pub fn diagnose_legacy_instructions( + opt: &mut MirOptimizer, + module: &MirModule, +) -> OptimizationStats { let mut stats = OptimizationStats::new(); let diag_on = opt.debug_enabled() || crate::config::env::opt_diag() @@ -66,7 +95,9 @@ pub fn diagnose_legacy_instructions(opt: &mut MirOptimizer, module: &MirModule) | MirInstruction::ArraySet { .. } | MirInstruction::RefGet { .. } | MirInstruction::RefSet { .. } - | MirInstruction::PluginInvoke { .. } => { count += 1; } + | MirInstruction::PluginInvoke { .. } => { + count += 1; + } _ => {} } } @@ -82,7 +113,9 @@ pub fn diagnose_legacy_instructions(opt: &mut MirOptimizer, module: &MirModule) | MirInstruction::ArraySet { .. } | MirInstruction::RefGet { .. } | MirInstruction::RefSet { .. } - | MirInstruction::PluginInvoke { .. } => { count += 1; } + | MirInstruction::PluginInvoke { .. } => { + count += 1; + } _ => {} } } @@ -95,11 +128,13 @@ pub fn diagnose_legacy_instructions(opt: &mut MirOptimizer, module: &MirModule) fname, count ); if crate::config::env::opt_diag_forbid_legacy() { - panic!("NYASH_OPT_DIAG_FORBID_LEGACY=1: legacy MIR ops detected in '{}': {}", fname, count); + panic!( + "NYASH_OPT_DIAG_FORBID_LEGACY=1: legacy MIR ops detected in '{}': {}", + fname, count + ); } } } } stats } - diff --git a/src/mir/optimizer_passes/intrinsics.rs b/src/mir/optimizer_passes/intrinsics.rs index efc9f7da..fa667851 100644 --- a/src/mir/optimizer_passes/intrinsics.rs +++ b/src/mir/optimizer_passes/intrinsics.rs @@ -1,11 +1,14 @@ -use crate::mir::MirModule; use crate::mir::optimizer::MirOptimizer; use crate::mir::optimizer_stats::OptimizationStats; +use crate::mir::MirModule; /// Intrinsic optimization pass (scaffolding) /// Keeps behavior identical for now (no transforms), but centralizes /// debug printing and future hooks. -pub fn optimize_intrinsic_calls(opt: &mut MirOptimizer, module: &mut MirModule) -> OptimizationStats { +pub fn optimize_intrinsic_calls( + opt: &mut MirOptimizer, + module: &mut MirModule, +) -> OptimizationStats { let mut stats = OptimizationStats::new(); for (func_name, _function) in &mut module.functions { if opt.debug_enabled() { @@ -15,4 +18,3 @@ pub fn optimize_intrinsic_calls(opt: &mut MirOptimizer, module: &mut MirModule) } stats } - diff --git a/src/mir/optimizer_passes/mod.rs b/src/mir/optimizer_passes/mod.rs index b36d88f5..9af61a65 100644 --- a/src/mir/optimizer_passes/mod.rs +++ b/src/mir/optimizer_passes/mod.rs @@ -1,5 +1,5 @@ -pub mod normalize; -pub mod diagnostics; -pub mod reorder; pub mod boxfield; +pub mod diagnostics; pub mod intrinsics; +pub mod normalize; +pub mod reorder; diff --git a/src/mir/optimizer_passes/normalize.rs b/src/mir/optimizer_passes/normalize.rs index cc88becc..25d7e7b8 100644 --- a/src/mir/optimizer_passes/normalize.rs +++ b/src/mir/optimizer_passes/normalize.rs @@ -1,6 +1,6 @@ -use crate::mir::{MirModule, MirInstruction, TypeOpKind, WeakRefOp, BarrierOp, ValueId}; use crate::mir::optimizer::MirOptimizer; use crate::mir::optimizer_stats::OptimizationStats; +use crate::mir::{BarrierOp, MirInstruction, MirModule, TypeOpKind, ValueId, WeakRefOp}; pub fn force_plugin_invoke(_opt: &mut MirOptimizer, module: &mut MirModule) -> OptimizationStats { use crate::mir::MirInstruction as I; @@ -8,8 +8,22 @@ pub fn force_plugin_invoke(_opt: &mut MirOptimizer, module: &mut MirModule) -> O for (_fname, function) in &mut module.functions { for (_bb, block) in &mut function.blocks { for inst in &mut block.instructions { - if let I::BoxCall { dst, box_val, method, args, effects, .. } = inst.clone() { - *inst = I::PluginInvoke { dst, box_val, method, args, effects }; + if let I::BoxCall { + dst, + box_val, + method, + args, + effects, + .. + } = inst.clone() + { + *inst = I::PluginInvoke { + dst, + box_val, + method, + args, + effects, + }; stats.intrinsic_optimizations += 1; } } @@ -18,13 +32,22 @@ pub fn force_plugin_invoke(_opt: &mut MirOptimizer, module: &mut MirModule) -> O stats } -pub fn normalize_python_helper_calls(_opt: &mut MirOptimizer, module: &mut MirModule) -> OptimizationStats { +pub fn normalize_python_helper_calls( + _opt: &mut MirOptimizer, + module: &mut MirModule, +) -> OptimizationStats { use crate::mir::MirInstruction as I; let mut stats = OptimizationStats::new(); for (_fname, function) in &mut module.functions { for (_bb, block) in &mut function.blocks { for inst in &mut block.instructions { - if let I::PluginInvoke { box_val, method, args, .. } = inst { + if let I::PluginInvoke { + box_val, + method, + args, + .. + } = inst + { if method == "getattr" && args.len() >= 2 { let new_recv = args[0]; args.remove(0); @@ -43,7 +66,10 @@ pub fn normalize_python_helper_calls(_opt: &mut MirOptimizer, module: &mut MirMo stats } -pub fn normalize_legacy_instructions(_opt: &mut MirOptimizer, module: &mut MirModule) -> OptimizationStats { +pub fn normalize_legacy_instructions( + _opt: &mut MirOptimizer, + module: &mut MirModule, +) -> OptimizationStats { use crate::mir::MirInstruction as I; let mut stats = OptimizationStats::new(); let rw_dbg = crate::config::env::rewrite_debug(); @@ -51,49 +77,116 @@ pub fn normalize_legacy_instructions(_opt: &mut MirOptimizer, module: &mut MirMo let rw_future = crate::config::env::rewrite_future(); let core13 = crate::config::env::mir_core13(); let mut array_to_boxcall = crate::config::env::mir_array_boxcall(); - if core13 { array_to_boxcall = true; } + if core13 { + array_to_boxcall = true; + } for (_fname, function) in &mut module.functions { for (_bb, block) in &mut function.blocks { for inst in &mut block.instructions { match inst { I::WeakNew { dst, box_val } => { - let d = *dst; let v = *box_val; - *inst = I::WeakRef { dst: d, op: WeakRefOp::New, value: v }; + let d = *dst; + let v = *box_val; + *inst = I::WeakRef { + dst: d, + op: WeakRefOp::New, + value: v, + }; stats.intrinsic_optimizations += 1; } I::WeakLoad { dst, weak_ref } => { - let d = *dst; let v = *weak_ref; - *inst = I::WeakRef { dst: d, op: WeakRefOp::Load, value: v }; + let d = *dst; + let v = *weak_ref; + *inst = I::WeakRef { + dst: d, + op: WeakRefOp::Load, + value: v, + }; stats.intrinsic_optimizations += 1; } I::BarrierRead { ptr } => { - let p = *ptr; *inst = I::Barrier { op: BarrierOp::Read, ptr: p }; + let p = *ptr; + *inst = I::Barrier { + op: BarrierOp::Read, + ptr: p, + }; stats.intrinsic_optimizations += 1; } I::BarrierWrite { ptr } => { - let p = *ptr; *inst = I::Barrier { op: BarrierOp::Write, ptr: p }; + let p = *ptr; + *inst = I::Barrier { + op: BarrierOp::Write, + ptr: p, + }; stats.intrinsic_optimizations += 1; } I::Print { value, .. } => { let v = *value; - *inst = I::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![v], effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io) }; + *inst = I::ExternCall { + dst: None, + iface_name: "env.console".to_string(), + method_name: "log".to_string(), + args: vec![v], + effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io), + }; stats.intrinsic_optimizations += 1; } I::ArrayGet { dst, array, index } if array_to_boxcall => { - let d = *dst; let a = *array; let i = *index; - let mid = crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "get"); - *inst = I::BoxCall { dst: Some(d), box_val: a, method: "get".to_string(), method_id: mid, args: vec![i], effects: crate::mir::EffectMask::READ }; + let d = *dst; + let a = *array; + let i = *index; + let mid = + crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "get"); + *inst = I::BoxCall { + dst: Some(d), + box_val: a, + method: "get".to_string(), + method_id: mid, + args: vec![i], + effects: crate::mir::EffectMask::READ, + }; stats.intrinsic_optimizations += 1; } - I::ArraySet { array, index, value } if array_to_boxcall => { - let a = *array; let i = *index; let v = *value; - let mid = crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "set"); - *inst = I::BoxCall { dst: None, box_val: a, method: "set".to_string(), method_id: mid, args: vec![i, v], effects: crate::mir::EffectMask::WRITE }; + I::ArraySet { + array, + index, + value, + } if array_to_boxcall => { + let a = *array; + let i = *index; + let v = *value; + let mid = + crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "set"); + *inst = I::BoxCall { + dst: None, + box_val: a, + method: "set".to_string(), + method_id: mid, + args: vec![i, v], + effects: crate::mir::EffectMask::WRITE, + }; stats.intrinsic_optimizations += 1; } - I::PluginInvoke { dst, box_val, method, args, effects } => { - let d = *dst; let recv = *box_val; let m = method.clone(); let as_ = args.clone(); let eff = *effects; - *inst = I::BoxCall { dst: d, box_val: recv, method: m, method_id: None, args: as_, effects: eff }; + I::PluginInvoke { + dst, + box_val, + method, + args, + effects, + } => { + let d = *dst; + let recv = *box_val; + let m = method.clone(); + let as_ = args.clone(); + let eff = *effects; + *inst = I::BoxCall { + dst: d, + box_val: recv, + method: m, + method_id: None, + args: as_, + effects: eff, + }; stats.intrinsic_optimizations += 1; } I::Debug { .. } if !rw_dbg => { @@ -103,16 +196,37 @@ pub fn normalize_legacy_instructions(_opt: &mut MirOptimizer, module: &mut MirMo *inst = I::Nop; } I::FutureNew { dst, value } if rw_future => { - let d = *dst; let v = *value; - *inst = I::ExternCall { dst: Some(d), iface_name: "env.future".to_string(), method_name: "new".to_string(), args: vec![v], effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io) }; + let d = *dst; + let v = *value; + *inst = I::ExternCall { + dst: Some(d), + iface_name: "env.future".to_string(), + method_name: "new".to_string(), + args: vec![v], + effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io), + }; } I::FutureSet { future, value } if rw_future => { - let f = *future; let v = *value; - *inst = I::ExternCall { dst: None, iface_name: "env.future".to_string(), method_name: "set".to_string(), args: vec![f, v], effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io) }; + let f = *future; + let v = *value; + *inst = I::ExternCall { + dst: None, + iface_name: "env.future".to_string(), + method_name: "set".to_string(), + args: vec![f, v], + effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io), + }; } I::Await { dst, future } if rw_future => { - let d = *dst; let f = *future; - *inst = I::ExternCall { dst: Some(d), iface_name: "env.future".to_string(), method_name: "await".to_string(), args: vec![f], effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io) }; + let d = *dst; + let f = *future; + *inst = I::ExternCall { + dst: Some(d), + iface_name: "env.future".to_string(), + method_name: "await".to_string(), + args: vec![f], + effects: crate::mir::EffectMask::PURE.add(crate::mir::Effect::Io), + }; } _ => {} } @@ -120,46 +234,111 @@ pub fn normalize_legacy_instructions(_opt: &mut MirOptimizer, module: &mut MirMo // terminator rewrite (subset migrated as needed) if let Some(term) = &mut block.terminator { match term { - I::TypeCheck { dst, value, expected_type } => { + I::TypeCheck { + dst, + value, + expected_type, + } => { let ty = crate::mir::instruction::MirType::Box(expected_type.clone()); - *term = I::TypeOp { dst: *dst, op: TypeOpKind::Check, value: *value, ty }; + *term = I::TypeOp { + dst: *dst, + op: TypeOpKind::Check, + value: *value, + ty, + }; stats.intrinsic_optimizations += 1; } - I::Cast { dst, value, target_type } => { + I::Cast { + dst, + value, + target_type, + } => { let ty = target_type.clone(); - *term = I::TypeOp { dst: *dst, op: TypeOpKind::Cast, value: *value, ty }; + *term = I::TypeOp { + dst: *dst, + op: TypeOpKind::Cast, + value: *value, + ty, + }; stats.intrinsic_optimizations += 1; } I::WeakNew { dst, box_val } => { - let d = *dst; let v = *box_val; - *term = I::WeakRef { dst: d, op: WeakRefOp::New, value: v }; + let d = *dst; + let v = *box_val; + *term = I::WeakRef { + dst: d, + op: WeakRefOp::New, + value: v, + }; stats.intrinsic_optimizations += 1; } I::WeakLoad { dst, weak_ref } => { - let d = *dst; let v = *weak_ref; - *term = I::WeakRef { dst: d, op: WeakRefOp::Load, value: v }; + let d = *dst; + let v = *weak_ref; + *term = I::WeakRef { + dst: d, + op: WeakRefOp::Load, + value: v, + }; stats.intrinsic_optimizations += 1; } I::BarrierRead { ptr } => { - let p = *ptr; *term = I::Barrier { op: BarrierOp::Read, ptr: p }; + let p = *ptr; + *term = I::Barrier { + op: BarrierOp::Read, + ptr: p, + }; stats.intrinsic_optimizations += 1; } I::BarrierWrite { ptr } => { - let p = *ptr; *term = I::Barrier { op: BarrierOp::Write, ptr: p }; + let p = *ptr; + *term = I::Barrier { + op: BarrierOp::Write, + ptr: p, + }; stats.intrinsic_optimizations += 1; } I::Print { value, .. } => { - let v = *value; *term = I::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![v], effects: crate::mir::EffectMask::PURE }; + let v = *value; + *term = I::ExternCall { + dst: None, + iface_name: "env.console".to_string(), + method_name: "log".to_string(), + args: vec![v], + effects: crate::mir::EffectMask::PURE, + }; stats.intrinsic_optimizations += 1; } I::ArrayGet { dst, array, index } if array_to_boxcall => { - let d = *dst; let a = *array; let i = *index; - *term = I::BoxCall { dst: Some(d), box_val: a, method: "get".to_string(), method_id: None, args: vec![i], effects: crate::mir::EffectMask::READ }; + let d = *dst; + let a = *array; + let i = *index; + *term = I::BoxCall { + dst: Some(d), + box_val: a, + method: "get".to_string(), + method_id: None, + args: vec![i], + effects: crate::mir::EffectMask::READ, + }; stats.intrinsic_optimizations += 1; } - I::ArraySet { array, index, value } if array_to_boxcall => { - let a = *array; let i = *index; let v = *value; - *term = I::BoxCall { dst: None, box_val: a, method: "set".to_string(), method_id: None, args: vec![i, v], effects: crate::mir::EffectMask::WRITE }; + I::ArraySet { + array, + index, + value, + } if array_to_boxcall => { + let a = *array; + let i = *index; + let v = *value; + *term = I::BoxCall { + dst: None, + box_val: a, + method: "set".to_string(), + method_id: None, + args: vec![i, v], + effects: crate::mir::EffectMask::WRITE, + }; stats.intrinsic_optimizations += 1; } _ => {} @@ -170,7 +349,10 @@ pub fn normalize_legacy_instructions(_opt: &mut MirOptimizer, module: &mut MirMo stats } -pub fn normalize_ref_field_access(_opt: &mut MirOptimizer, module: &mut MirModule) -> OptimizationStats { +pub fn normalize_ref_field_access( + _opt: &mut MirOptimizer, + module: &mut MirModule, +) -> OptimizationStats { use crate::mir::MirInstruction as I; let mut stats = OptimizationStats::new(); for (_fname, function) in &mut module.functions { @@ -179,19 +361,50 @@ pub fn normalize_ref_field_access(_opt: &mut MirOptimizer, module: &mut MirModul let old = std::mem::take(&mut block.instructions); for inst in old.into_iter() { match inst { - I::RefGet { dst, reference, field } => { + I::RefGet { + dst, + reference, + field, + } => { let new_id = ValueId::new(function.next_value_id); function.next_value_id += 1; - out.push(I::Const { dst: new_id, value: crate::mir::instruction::ConstValue::String(field) }); - out.push(I::BoxCall { dst: Some(dst), box_val: reference, method: "getField".to_string(), method_id: None, args: vec![new_id], effects: crate::mir::EffectMask::READ }); + out.push(I::Const { + dst: new_id, + value: crate::mir::instruction::ConstValue::String(field), + }); + out.push(I::BoxCall { + dst: Some(dst), + box_val: reference, + method: "getField".to_string(), + method_id: None, + args: vec![new_id], + effects: crate::mir::EffectMask::READ, + }); stats.intrinsic_optimizations += 1; } - I::RefSet { reference, field, value } => { + I::RefSet { + reference, + field, + value, + } => { let new_id = ValueId::new(function.next_value_id); function.next_value_id += 1; - out.push(I::Const { dst: new_id, value: crate::mir::instruction::ConstValue::String(field) }); - out.push(I::Barrier { op: BarrierOp::Write, ptr: reference }); - out.push(I::BoxCall { dst: None, box_val: reference, method: "setField".to_string(), method_id: None, args: vec![new_id, value], effects: crate::mir::EffectMask::WRITE }); + out.push(I::Const { + dst: new_id, + value: crate::mir::instruction::ConstValue::String(field), + }); + out.push(I::Barrier { + op: BarrierOp::Write, + ptr: reference, + }); + out.push(I::BoxCall { + dst: None, + box_val: reference, + method: "setField".to_string(), + method_id: None, + args: vec![new_id, value], + effects: crate::mir::EffectMask::WRITE, + }); stats.intrinsic_optimizations += 1; } other => out.push(other), @@ -201,18 +414,49 @@ pub fn normalize_ref_field_access(_opt: &mut MirOptimizer, module: &mut MirModul if let Some(term) = block.terminator.take() { block.terminator = Some(match term { - I::RefGet { dst, reference, field } => { + I::RefGet { + dst, + reference, + field, + } => { let new_id = ValueId::new(function.next_value_id); function.next_value_id += 1; - block.instructions.push(I::Const { dst: new_id, value: crate::mir::instruction::ConstValue::String(field) }); - I::BoxCall { dst: Some(dst), box_val: reference, method: "getField".to_string(), method_id: None, args: vec![new_id], effects: crate::mir::EffectMask::READ } + block.instructions.push(I::Const { + dst: new_id, + value: crate::mir::instruction::ConstValue::String(field), + }); + I::BoxCall { + dst: Some(dst), + box_val: reference, + method: "getField".to_string(), + method_id: None, + args: vec![new_id], + effects: crate::mir::EffectMask::READ, + } } - I::RefSet { reference, field, value } => { + I::RefSet { + reference, + field, + value, + } => { let new_id = ValueId::new(function.next_value_id); function.next_value_id += 1; - block.instructions.push(I::Const { dst: new_id, value: crate::mir::instruction::ConstValue::String(field) }); - block.instructions.push(I::Barrier { op: BarrierOp::Write, ptr: reference }); - I::BoxCall { dst: None, box_val: reference, method: "setField".to_string(), method_id: None, args: vec![new_id, value], effects: crate::mir::EffectMask::WRITE } + block.instructions.push(I::Const { + dst: new_id, + value: crate::mir::instruction::ConstValue::String(field), + }); + block.instructions.push(I::Barrier { + op: BarrierOp::Write, + ptr: reference, + }); + I::BoxCall { + dst: None, + box_val: reference, + method: "setField".to_string(), + method_id: None, + args: vec![new_id, value], + effects: crate::mir::EffectMask::WRITE, + } } other => other, }); diff --git a/src/mir/optimizer_passes/reorder.rs b/src/mir/optimizer_passes/reorder.rs index a073eb73..46e20a69 100644 --- a/src/mir/optimizer_passes/reorder.rs +++ b/src/mir/optimizer_passes/reorder.rs @@ -1,17 +1,22 @@ -use crate::mir::{MirModule}; use crate::mir::optimizer::MirOptimizer; use crate::mir::optimizer_stats::OptimizationStats; +use crate::mir::MirModule; /// Reorder pure instructions for better locality (scaffolding) -pub fn reorder_pure_instructions(opt: &mut MirOptimizer, module: &mut MirModule) -> OptimizationStats { +pub fn reorder_pure_instructions( + opt: &mut MirOptimizer, + module: &mut MirModule, +) -> OptimizationStats { let mut stats = OptimizationStats::new(); for (func_name, _function) in &mut module.functions { if opt.debug_enabled() { - println!(" 🔀 Pure instruction reordering in function: {}", func_name); + println!( + " 🔀 Pure instruction reordering in function: {}", + func_name + ); } // Placeholder: keep behavior identical (no reordering yet) // When implemented, set stats.reorderings += N per function. } stats } - diff --git a/src/mir/optimizer_stats.rs b/src/mir/optimizer_stats.rs index d706a172..3322f3ad 100644 --- a/src/mir/optimizer_stats.rs +++ b/src/mir/optimizer_stats.rs @@ -50,4 +50,3 @@ impl std::fmt::Display for OptimizationStats { ) } } - diff --git a/src/mir/passes/cse.rs b/src/mir/passes/cse.rs index 9d035785..47818f92 100644 --- a/src/mir/passes/cse.rs +++ b/src/mir/passes/cse.rs @@ -4,7 +4,7 @@ //! counts eliminations without rewriting uses (SSA update is TODO). This keeps //! behavior identical while modularizing the pass for future enhancement. -use crate::mir::{MirInstruction, MirModule, MirFunction, ValueId}; +use crate::mir::{MirFunction, MirInstruction, MirModule, ValueId}; use std::collections::HashMap; /// Run CSE across the module. Returns the number of eliminated expressions. @@ -42,15 +42,20 @@ fn cse_in_function(function: &mut MirFunction) -> usize { fn instruction_key(i: &MirInstruction) -> String { match i { MirInstruction::Const { value, .. } => format!("const_{:?}", value), - MirInstruction::BinOp { op, lhs, rhs, .. } => - format!("binop_{:?}_{}_{}", op, lhs.as_u32(), rhs.as_u32()), - MirInstruction::Compare { op, lhs, rhs, .. } => - format!("cmp_{:?}_{}_{}", op, lhs.as_u32(), rhs.as_u32()), + MirInstruction::BinOp { op, lhs, rhs, .. } => { + format!("binop_{:?}_{}_{}", op, lhs.as_u32(), rhs.as_u32()) + } + MirInstruction::Compare { op, lhs, rhs, .. } => { + format!("cmp_{:?}_{}_{}", op, lhs.as_u32(), rhs.as_u32()) + } MirInstruction::Call { func, args, .. } => { - let args_str = args.iter().map(|v| v.as_u32().to_string()).collect::>().join(","); + let args_str = args + .iter() + .map(|v| v.as_u32().to_string()) + .collect::>() + .join(","); format!("call_{}_{}", func.as_u32(), args_str) } other => format!("other_{:?}", other), } } - diff --git a/src/mir/passes/dce.rs b/src/mir/passes/dce.rs index 731d3708..bb966604 100644 --- a/src/mir/passes/dce.rs +++ b/src/mir/passes/dce.rs @@ -2,7 +2,7 @@ //! //! Extracted from the monolithic optimizer to enable modular pass composition. -use crate::mir::{MirModule, MirFunction, MirInstruction, ValueId}; +use crate::mir::{MirFunction, MirInstruction, MirModule, ValueId}; use std::collections::HashSet; /// Eliminate dead code (unused results of pure instructions) across the module. @@ -23,12 +23,18 @@ fn eliminate_dead_code_in_function(function: &mut MirFunction) -> usize { for (_bid, block) in &function.blocks { for instruction in &block.instructions { if !instruction.effects().is_pure() { - if let Some(dst) = instruction.dst_value() { used_values.insert(dst); } - for u in instruction.used_values() { used_values.insert(u); } + if let Some(dst) = instruction.dst_value() { + used_values.insert(dst); + } + for u in instruction.used_values() { + used_values.insert(u); + } } } if let Some(term) = &block.terminator { - for u in term.used_values() { used_values.insert(u); } + for u in term.used_values() { + used_values.insert(u); + } } } @@ -41,7 +47,9 @@ fn eliminate_dead_code_in_function(function: &mut MirFunction) -> usize { if let Some(dst) = instruction.dst_value() { if used_values.contains(&dst) { for u in instruction.used_values() { - if used_values.insert(u) { changed = true; } + if used_values.insert(u) { + changed = true; + } } } } @@ -66,7 +74,8 @@ fn eliminate_dead_code_in_function(function: &mut MirFunction) -> usize { true }); } - if eliminated > 0 { function.update_cfg(); } + if eliminated > 0 { + function.update_cfg(); + } eliminated } - diff --git a/src/mir/passes/escape.rs b/src/mir/passes/escape.rs index 74d9f966..ec7a6d3b 100644 --- a/src/mir/passes/escape.rs +++ b/src/mir/passes/escape.rs @@ -2,7 +2,7 @@ //! Conservative analysis to elide write/read barriers for definitely non-escaping boxes. //! Enabled for VM backend as a staging step before LLVM. -use crate::mir::{MirModule, MirFunction, MirInstruction, ValueId}; +use crate::mir::{MirFunction, MirInstruction, MirModule, ValueId}; use std::collections::{HashMap, HashSet}; /// Run a conservative escape analysis and remove Barrier(Read/Write) for non-escaping boxes. @@ -32,7 +32,9 @@ struct EscapeInfo { } impl EscapeInfo { - fn is_non_escaping(&self, v: &ValueId) -> bool { self.local_boxes.contains(v) && !self.escaping.contains(v) } + fn is_non_escaping(&self, v: &ValueId) -> bool { + self.local_boxes.contains(v) && !self.escaping.contains(v) + } } fn analyze_function(func: &MirFunction) -> EscapeInfo { @@ -40,25 +42,39 @@ fn analyze_function(func: &MirFunction) -> EscapeInfo { // Collect local boxes: results of NewBox in this function for block in func.blocks.values() { for ins in block.instructions.iter() { - if let MirInstruction::NewBox { dst, .. } = ins { info.local_boxes.insert(*dst); } + if let MirInstruction::NewBox { dst, .. } = ins { + info.local_boxes.insert(*dst); + } } if let Some(term) = &block.terminator { - if let MirInstruction::NewBox { dst, .. } = term { info.local_boxes.insert(*dst); } + if let MirInstruction::NewBox { dst, .. } = term { + info.local_boxes.insert(*dst); + } } } // Conservative escape marking for block in func.blocks.values() { for ins in block.all_instructions() { match ins { - MirInstruction::Return { value: Some(v) } => { if info.local_boxes.contains(v) { info.escaping.insert(*v); } } + MirInstruction::Return { value: Some(v) } => { + if info.local_boxes.contains(v) { + info.escaping.insert(*v); + } + } MirInstruction::Call { args, .. } | MirInstruction::BoxCall { args, .. } | MirInstruction::ExternCall { args, .. } | MirInstruction::PluginInvoke { args, .. } => { - for a in args { if info.local_boxes.contains(a) { info.escaping.insert(*a); } } + for a in args { + if info.local_boxes.contains(a) { + info.escaping.insert(*a); + } + } } MirInstruction::Store { value, .. } => { - if info.local_boxes.contains(value) { info.escaping.insert(*value); } + if info.local_boxes.contains(value) { + info.escaping.insert(*value); + } } _ => {} } @@ -98,7 +114,8 @@ fn elide_barriers_in_function(func: &mut MirFunction, info: &EscapeInfo) -> usiz } } } - if removed > 0 { func.update_cfg(); } + if removed > 0 { + func.update_cfg(); + } removed } - diff --git a/src/mir/passes/method_id_inject.rs b/src/mir/passes/method_id_inject.rs index 4b5a3f35..8b6b6709 100644 --- a/src/mir/passes/method_id_inject.rs +++ b/src/mir/passes/method_id_inject.rs @@ -10,7 +10,7 @@ * Scope: minimal dataflow (direct NewBox and Copy propagation). Phi/complex flows are TODO. */ -use crate::mir::{MirModule, MirInstruction as I, ValueId}; +use crate::mir::{MirInstruction as I, MirModule, ValueId}; pub fn inject_method_ids(module: &mut MirModule) -> usize { use crate::mir::slot_registry::resolve_slot_by_type_name; @@ -38,14 +38,19 @@ pub fn inject_method_ids(module: &mut MirModule) -> usize { origin.insert(*dst, bt); } } - I::BoxCall { box_val, method, method_id, .. } => { + I::BoxCall { + box_val, + method, + method_id, + .. + } => { if method_id.is_none() { if let Some(bt) = origin.get(box_val).cloned() { // First try plugin host if available, else builtin slots let mid_u16 = if let Some(h) = host_guard.as_ref() { // Try resolve via plugin config (may fail for builtins) match h.resolve_method(&bt, method) { - Ok(mh) => Some(mh.method_id as u16), + Ok(mh) => Some(mh.method_id as u16), Err(_) => resolve_slot_by_type_name(&bt, method), } } else { @@ -58,12 +63,18 @@ pub fn inject_method_ids(module: &mut MirModule) -> usize { } } } - I::PluginInvoke { dst, box_val, method, args, effects } => { + I::PluginInvoke { + dst, + box_val, + method, + args, + effects, + } => { if let Some(bt) = origin.get(box_val).cloned() { // Resolve id as above let mid_u16 = if let Some(h) = host_guard.as_ref() { match h.resolve_method(&bt, method) { - Ok(mh) => Some(mh.method_id as u16), + Ok(mh) => Some(mh.method_id as u16), Err(_) => resolve_slot_by_type_name(&bt, method), } } else { diff --git a/src/mir/passes/mod.rs b/src/mir/passes/mod.rs index 6ca30641..7663e380 100644 --- a/src/mir/passes/mod.rs +++ b/src/mir/passes/mod.rs @@ -1,11 +1,11 @@ // MIR optimization subpasses module // Minimal scaffold to unblock builds when type hint propagation is not yet implemented. -pub mod type_hints; +pub mod cse; +pub mod dce; pub mod escape; pub mod method_id_inject; -pub mod dce; -pub mod cse; +pub mod type_hints; /// Minimal pass trait for future expansion. Currently unused by the main /// optimizer pipeline but provided to guide modularization. diff --git a/src/mir/passes/type_hints.rs b/src/mir/passes/type_hints.rs index 52f4d5f4..b32858de 100644 --- a/src/mir/passes/type_hints.rs +++ b/src/mir/passes/type_hints.rs @@ -10,4 +10,3 @@ use crate::mir::MirModule; pub fn propagate_param_type_hints(_module: &mut MirModule) -> u32 { 0 } - diff --git a/src/mir/printer.rs b/src/mir/printer.rs index a35105c9..e3e87723 100644 --- a/src/mir/printer.rs +++ b/src/mir/printer.rs @@ -1,23 +1,23 @@ /*! * MIR Printer - Debug output and visualization - * + * * Implements pretty-printing for MIR modules and functions */ -use super::{MirModule, MirFunction, BasicBlock, MirInstruction, ValueId, MirType}; +use super::{BasicBlock, MirFunction, MirInstruction, MirModule, MirType, ValueId}; +use crate::debug::log as dlog; use std::collections::HashMap; use std::fmt::Write; -use crate::debug::log as dlog; /// MIR printer for debug output and visualization pub struct MirPrinter { /// Indentation level #[allow(dead_code)] indent_level: usize, - + /// Whether to show detailed information verbose: bool, - + /// Whether to show line numbers show_line_numbers: bool, @@ -35,7 +35,7 @@ impl MirPrinter { show_effects_inline: false, } } - + /// Create a verbose MIR printer pub fn verbose() -> Self { Self { @@ -45,13 +45,13 @@ impl MirPrinter { show_effects_inline: false, } } - + /// Set verbose mode pub fn set_verbose(&mut self, verbose: bool) -> &mut Self { self.verbose = verbose; self } - + /// Set line number display pub fn set_show_line_numbers(&mut self, show: bool) -> &mut Self { self.show_line_numbers = show; @@ -63,18 +63,18 @@ impl MirPrinter { self.show_effects_inline = show; self } - + /// Print a complete MIR module pub fn print_module(&self, module: &MirModule) -> String { let mut output = String::new(); - + // Module header writeln!(output, "; MIR Module: {}", module.name).unwrap(); if let Some(ref source) = module.metadata.source_file { writeln!(output, "; Source: {}", source).unwrap(); } writeln!(output).unwrap(); - + // Module statistics if self.verbose { let stats = module.stats(); @@ -82,11 +82,16 @@ impl MirPrinter { writeln!(output, "; Functions: {}", stats.function_count).unwrap(); writeln!(output, "; Globals: {}", stats.global_count).unwrap(); writeln!(output, "; Total Blocks: {}", stats.total_blocks).unwrap(); - writeln!(output, "; Total Instructions: {}", stats.total_instructions).unwrap(); + writeln!( + output, + "; Total Instructions: {}", + stats.total_instructions + ) + .unwrap(); writeln!(output, "; Pure Functions: {}", stats.pure_functions).unwrap(); writeln!(output).unwrap(); } - + // Global constants if !module.globals.is_empty() { writeln!(output, "; Global Constants:").unwrap(); @@ -95,25 +100,29 @@ impl MirPrinter { } writeln!(output).unwrap(); } - + // Functions for (_name, function) in &module.functions { output.push_str(&self.print_function(function)); output.push('\n'); } - + output } - + /// Print a single MIR function pub fn print_function(&self, function: &MirFunction) -> String { let mut output = String::new(); - + // Function signature - write!(output, "define {} @{}(", - self.format_type(&function.signature.return_type), - function.signature.name).unwrap(); - + write!( + output, + "define {} @{}(", + self.format_type(&function.signature.return_type), + function.signature.name + ) + .unwrap(); + for (i, param_type) in function.signature.params.iter().enumerate() { if i > 0 { write!(output, ", ").unwrap(); @@ -121,14 +130,14 @@ impl MirPrinter { write!(output, "{} %{}", self.format_type(param_type), i).unwrap(); } write!(output, ")").unwrap(); - + // Effects if !function.signature.effects.is_pure() { write!(output, " effects({})", function.signature.effects).unwrap(); } - + writeln!(output, " {{").unwrap(); - + // Function statistics if self.verbose { let stats = function.stats(); @@ -150,8 +159,16 @@ impl MirPrinter { for block in function.blocks.values() { for inst in &block.instructions { match inst { - MirInstruction::Throw { .. } => { if dlog::on("NYASH_DEBUG_MIR_PRINTER") { eprintln!("[PRINTER] found throw in {}", function.signature.name); } } - MirInstruction::Catch { .. } => { if dlog::on("NYASH_DEBUG_MIR_PRINTER") { eprintln!("[PRINTER] found catch in {}", function.signature.name); } } + MirInstruction::Throw { .. } => { + if dlog::on("NYASH_DEBUG_MIR_PRINTER") { + eprintln!("[PRINTER] found throw in {}", function.signature.name); + } + } + MirInstruction::Catch { .. } => { + if dlog::on("NYASH_DEBUG_MIR_PRINTER") { + eprintln!("[PRINTER] found catch in {}", function.signature.name); + } + } MirInstruction::TypeCheck { .. } => type_check += 1, MirInstruction::Cast { .. } => type_cast += 1, MirInstruction::TypeOp { op, .. } => match op { @@ -175,8 +192,22 @@ impl MirPrinter { } if let Some(term) = &block.terminator { match term { - MirInstruction::Throw { .. } => { if dlog::on("NYASH_DEBUG_MIR_PRINTER") { eprintln!("[PRINTER] found throw(term) in {}", function.signature.name); } } - MirInstruction::Catch { .. } => { if dlog::on("NYASH_DEBUG_MIR_PRINTER") { eprintln!("[PRINTER] found catch(term) in {}", function.signature.name); } } + MirInstruction::Throw { .. } => { + if dlog::on("NYASH_DEBUG_MIR_PRINTER") { + eprintln!( + "[PRINTER] found throw(term) in {}", + function.signature.name + ); + } + } + MirInstruction::Catch { .. } => { + if dlog::on("NYASH_DEBUG_MIR_PRINTER") { + eprintln!( + "[PRINTER] found catch(term) in {}", + function.signature.name + ); + } + } MirInstruction::TypeCheck { .. } => type_check += 1, MirInstruction::Cast { .. } => type_cast += 1, MirInstruction::TypeOp { op, .. } => match op { @@ -200,21 +231,42 @@ impl MirPrinter { } } if type_check + type_cast > 0 { - writeln!(output, " ; TypeOp: {} (check: {}, cast: {})", type_check + type_cast, type_check, type_cast).unwrap(); + writeln!( + output, + " ; TypeOp: {} (check: {}, cast: {})", + type_check + type_cast, + type_check, + type_cast + ) + .unwrap(); } if weak_new + weak_load > 0 { - writeln!(output, " ; WeakRef: {} (new: {}, load: {})", weak_new + weak_load, weak_new, weak_load).unwrap(); + writeln!( + output, + " ; WeakRef: {} (new: {}, load: {})", + weak_new + weak_load, + weak_new, + weak_load + ) + .unwrap(); } if barrier_read + barrier_write > 0 { - writeln!(output, " ; Barrier: {} (read: {}, write: {})", barrier_read + barrier_write, barrier_read, barrier_write).unwrap(); + writeln!( + output, + " ; Barrier: {} (read: {}, write: {})", + barrier_read + barrier_write, + barrier_read, + barrier_write + ) + .unwrap(); } writeln!(output).unwrap(); } - + // Print blocks in order let mut block_ids: Vec<_> = function.blocks.keys().copied().collect(); block_ids.sort(); - + for (i, block_id) in block_ids.iter().enumerate() { if let Some(block) = function.blocks.get(block_id) { if i > 0 { @@ -223,29 +275,35 @@ impl MirPrinter { output.push_str(&self.print_basic_block(block, &function.metadata.value_types)); } } - + writeln!(output, "}}").unwrap(); - + output } - + /// Print a basic block - pub fn print_basic_block(&self, block: &BasicBlock, types: &HashMap) -> String { + pub fn print_basic_block( + &self, + block: &BasicBlock, + types: &HashMap, + ) -> String { let mut output = String::new(); - + // Block header write!(output, "{}:", block.id).unwrap(); - + // Predecessors if !block.predecessors.is_empty() && self.verbose { - let preds: Vec = block.predecessors.iter() + let preds: Vec = block + .predecessors + .iter() .map(|p| format!("{}", p)) .collect(); write!(output, " ; preds({})", preds.join(", ")).unwrap(); } - + writeln!(output).unwrap(); - + // Instructions let mut line_num = 0; for instruction in block.all_instructions() { @@ -258,21 +316,27 @@ impl MirPrinter { let mut line = self.format_instruction(instruction, types); if self.show_effects_inline { let eff = instruction.effects(); - let cat = if eff.is_pure() { "pure" } else if eff.is_read_only() { "readonly" } else { "side" }; + let cat = if eff.is_pure() { + "pure" + } else if eff.is_read_only() { + "readonly" + } else { + "side" + }; line.push_str(&format!(" ; eff: {}", cat)); } writeln!(output, "{}", line).unwrap(); line_num += 1; } - + // Block effects (if verbose and not pure) if self.verbose && !block.effects.is_pure() { writeln!(output, " ; effects: {}", block.effects).unwrap(); } - + output } - + fn format_dst(&self, dst: &ValueId, types: &HashMap) -> String { if let Some(ty) = types.get(dst) { format!("{}: {:?} =", dst, ty) @@ -282,232 +346,395 @@ impl MirPrinter { } /// Format a single instruction - fn format_instruction(&self, instruction: &MirInstruction, types: &HashMap) -> String { + fn format_instruction( + &self, + instruction: &MirInstruction, + types: &HashMap, + ) -> String { match instruction { MirInstruction::Const { dst, value } => { format!("{} const {}", self.format_dst(dst, types), value) - }, + } MirInstruction::BinOp { dst, op, lhs, rhs } => { format!("{} {} {:?} {}", self.format_dst(dst, types), lhs, op, rhs) - }, + } MirInstruction::UnaryOp { dst, op, operand } => { format!("{} {:?} {}", self.format_dst(dst, types), op, operand) - }, + } MirInstruction::Compare { dst, op, lhs, rhs } => { - format!("{} icmp {:?} {}, {}", self.format_dst(dst, types), op, lhs, rhs) - }, + format!( + "{} icmp {:?} {}, {}", + self.format_dst(dst, types), + op, + lhs, + rhs + ) + } MirInstruction::Load { dst, ptr } => { format!("{} load {}", self.format_dst(dst, types), ptr) - }, + } MirInstruction::Store { value, ptr } => { format!("store {} -> {}", value, ptr) - }, - - MirInstruction::Call { dst, func, args, effects: _ } => { - let args_str = args.iter() + } + + MirInstruction::Call { + dst, + func, + args, + effects: _, + } => { + let args_str = args + .iter() .map(|v| format!("{}", v)) .collect::>() .join(", "); if let Some(dst) = dst { - format!("{} call {}({})", self.format_dst(dst, types), func, args_str) + format!( + "{} call {}({})", + self.format_dst(dst, types), + func, + args_str + ) } else { format!("call {}({})", func, args_str) } - }, - MirInstruction::FunctionNew { dst, params, body, captures, me } => { + } + MirInstruction::FunctionNew { + dst, + params, + body, + captures, + me, + } => { let p = params.join(", "); - let c = captures.iter().map(|(n, v)| format!("{}={}", n, v)).collect::>().join(", "); + let c = captures + .iter() + .map(|(n, v)| format!("{}={}", n, v)) + .collect::>() + .join(", "); let me_s = me.map(|m| format!(" me={}", m)).unwrap_or_default(); - let cap_s = if c.is_empty() { String::new() } else { format!(" [{}]", c) }; - format!("{} function_new ({}) {{...{}}}{}{}", self.format_dst(dst, types), p, body.len(), cap_s, me_s) - }, + let cap_s = if c.is_empty() { + String::new() + } else { + format!(" [{}]", c) + }; + format!( + "{} function_new ({}) {{...{}}}{}{}", + self.format_dst(dst, types), + p, + body.len(), + cap_s, + me_s + ) + } - MirInstruction::BoxCall { dst, box_val, method, method_id, args, effects: _ } => { - let args_str = args.iter() + MirInstruction::BoxCall { + dst, + box_val, + method, + method_id, + args, + effects: _, + } => { + let args_str = args + .iter() .map(|v| format!("{}", v)) .collect::>() .join(", "); let id_suffix = method_id.map(|id| format!("[#{}]", id)).unwrap_or_default(); if let Some(dst) = dst { - format!("{} call {}.{}{}({})", self.format_dst(dst, types), box_val, method, id_suffix, args_str) + format!( + "{} call {}.{}{}({})", + self.format_dst(dst, types), + box_val, + method, + id_suffix, + args_str + ) } else { format!("call {}.{}{}({})", box_val, method, id_suffix, args_str) } - }, - MirInstruction::PluginInvoke { dst, box_val, method, args, effects: _ } => { - let args_str = args.iter() + } + MirInstruction::PluginInvoke { + dst, + box_val, + method, + args, + effects: _, + } => { + let args_str = args + .iter() .map(|v| format!("{}", v)) .collect::>() .join(", "); if let Some(dst) = dst { - format!("{} plugin_invoke {}.{}({})", self.format_dst(dst, types), box_val, method, args_str) + format!( + "{} plugin_invoke {}.{}({})", + self.format_dst(dst, types), + box_val, + method, + args_str + ) } else { format!("plugin_invoke {}.{}({})", box_val, method, args_str) } - }, - - MirInstruction::Branch { condition, then_bb, else_bb } => { + } + + MirInstruction::Branch { + condition, + then_bb, + else_bb, + } => { format!("br {}, label {}, label {}", condition, then_bb, else_bb) - }, - + } + MirInstruction::Jump { target } => { format!("br label {}", target) - }, - + } + MirInstruction::Return { value } => { if let Some(value) = value { format!("ret {}", value) } else { "ret void".to_string() } - }, - + } + MirInstruction::Phi { dst, inputs } => { - let inputs_str = inputs.iter() + let inputs_str = inputs + .iter() .map(|(bb, val)| format!("[{}, {}]", val, bb)) .collect::>() .join(", "); format!("{} phi {}", self.format_dst(dst, types), inputs_str) - }, + } - MirInstruction::NewBox { dst, box_type, args } => { - let args_str = args.iter() + MirInstruction::NewBox { + dst, + box_type, + args, + } => { + let args_str = args + .iter() .map(|v| format!("{}", v)) .collect::>() .join(", "); - format!("{} new {}({})", self.format_dst(dst, types), box_type, args_str) - }, + format!( + "{} new {}({})", + self.format_dst(dst, types), + box_type, + args_str + ) + } // Legacy -> Unified print: TypeCheck as TypeOp(check) - MirInstruction::TypeCheck { dst, value, expected_type } => { + MirInstruction::TypeCheck { + dst, + value, + expected_type, + } => { // Print using unified TypeOp style to avoid naming divergence - format!("{} typeop check {} {}", self.format_dst(dst, types), value, expected_type) - }, + format!( + "{} typeop check {} {}", + self.format_dst(dst, types), + value, + expected_type + ) + } - MirInstruction::Cast { dst, value, target_type } => { - format!("{} cast {} to {:?}", self.format_dst(dst, types), value, target_type) - }, + MirInstruction::Cast { + dst, + value, + target_type, + } => { + format!( + "{} cast {} to {:?}", + self.format_dst(dst, types), + value, + target_type + ) + } MirInstruction::TypeOp { dst, op, value, ty } => { - let op_str = match op { super::TypeOpKind::Check => "check", super::TypeOpKind::Cast => "cast" }; - format!("{} typeop {} {} {:?}", self.format_dst(dst, types), op_str, value, ty) - }, + let op_str = match op { + super::TypeOpKind::Check => "check", + super::TypeOpKind::Cast => "cast", + }; + format!( + "{} typeop {} {} {:?}", + self.format_dst(dst, types), + op_str, + value, + ty + ) + } MirInstruction::ArrayGet { dst, array, index } => { format!("{} {}[{}]", self.format_dst(dst, types), array, index) - }, + } - MirInstruction::ArraySet { array, index, value } => { + MirInstruction::ArraySet { + array, + index, + value, + } => { format!("{}[{}] = {}", array, index, value) - }, + } MirInstruction::Copy { dst, src } => { format!("{} copy {}", self.format_dst(dst, types), src) - }, + } MirInstruction::Debug { value, message } => { format!("debug {} \"{}\"", value, message) - }, + } MirInstruction::Print { value, effects: _ } => { format!("print {}", value) - }, + } - MirInstruction::Nop => { - "nop".to_string() - }, + MirInstruction::Nop => "nop".to_string(), // Phase 5: Control flow & exception handling - MirInstruction::Throw { exception, effects: _ } => { + MirInstruction::Throw { + exception, + effects: _, + } => { format!("throw {}", exception) - }, + } - MirInstruction::Catch { exception_type, exception_value, handler_bb } => { + MirInstruction::Catch { + exception_type, + exception_value, + handler_bb, + } => { if let Some(ref exc_type) = exception_type { format!("catch {} {} -> {}", exc_type, exception_value, handler_bb) } else { format!("catch * {} -> {}", exception_value, handler_bb) } - }, - - MirInstruction::Safepoint => { - "safepoint".to_string() - }, - + } + + MirInstruction::Safepoint => "safepoint".to_string(), + // Phase 6: Box reference operations MirInstruction::RefNew { dst, box_val } => { format!("{} ref_new {}", self.format_dst(dst, types), box_val) - }, + } - MirInstruction::RefGet { dst, reference, field } => { - format!("{} ref_get {}.{}", self.format_dst(dst, types), reference, field) - }, - - MirInstruction::RefSet { reference, field, value } => { + MirInstruction::RefGet { + dst, + reference, + field, + } => { + format!( + "{} ref_get {}.{}", + self.format_dst(dst, types), + reference, + field + ) + } + + MirInstruction::RefSet { + reference, + field, + value, + } => { format!("ref_set {}.{} = {}", reference, field, value) - }, - + } + // Legacy -> Unified print: WeakNew as weakref new MirInstruction::WeakNew { dst, box_val } => { format!("{} weakref new {}", self.format_dst(dst, types), box_val) - }, + } // Legacy -> Unified print: WeakLoad as weakref load MirInstruction::WeakLoad { dst, weak_ref } => { format!("{} weakref load {}", self.format_dst(dst, types), weak_ref) - }, + } // Legacy -> Unified print: BarrierRead as barrier read MirInstruction::BarrierRead { ptr } => { format!("barrier read {}", ptr) - }, + } // Legacy -> Unified print: BarrierWrite as barrier write MirInstruction::BarrierWrite { ptr } => { format!("barrier write {}", ptr) - }, + } MirInstruction::WeakRef { dst, op, value } => { - let op_str = match op { super::WeakRefOp::New => "new", super::WeakRefOp::Load => "load" }; - format!("{} weakref {} {}", self.format_dst(dst, types), op_str, value) - }, + let op_str = match op { + super::WeakRefOp::New => "new", + super::WeakRefOp::Load => "load", + }; + format!( + "{} weakref {} {}", + self.format_dst(dst, types), + op_str, + value + ) + } MirInstruction::Barrier { op, ptr } => { - let op_str = match op { super::BarrierOp::Read => "read", super::BarrierOp::Write => "write" }; + let op_str = match op { + super::BarrierOp::Read => "read", + super::BarrierOp::Write => "write", + }; format!("barrier {} {}", op_str, ptr) - }, + } // Phase 7: Async/Future Operations MirInstruction::FutureNew { dst, value } => { format!("{} future_new {}", self.format_dst(dst, types), value) - }, + } MirInstruction::FutureSet { future, value } => { format!("future_set {} = {}", future, value) - }, + } MirInstruction::Await { dst, future } => { format!("{} await {}", self.format_dst(dst, types), future) - }, + } // Phase 9.7: External Function Calls - MirInstruction::ExternCall { dst, iface_name, method_name, args, effects } => { - let args_str = args.iter().map(|v| format!("{}", v)).collect::>().join(", "); + MirInstruction::ExternCall { + dst, + iface_name, + method_name, + args, + effects, + } => { + let args_str = args + .iter() + .map(|v| format!("{}", v)) + .collect::>() + .join(", "); if let Some(dst) = dst { - format!("{} extern_call {}.{}({}) [effects: {}]", self.format_dst(dst, types), iface_name, method_name, args_str, effects) + format!( + "{} extern_call {}.{}({}) [effects: {}]", + self.format_dst(dst, types), + iface_name, + method_name, + args_str, + effects + ) } else { - format!("extern_call {}.{}({}) [effects: {}]", iface_name, method_name, args_str, effects) + format!( + "extern_call {}.{}({}) [effects: {}]", + iface_name, method_name, args_str, effects + ) } - }, + } } } - + /// Format a MIR type fn format_type(&self, mir_type: &super::MirType) -> String { match mir_type { @@ -517,7 +744,9 @@ impl MirPrinter { super::MirType::String => "str".to_string(), super::MirType::Box(name) => format!("box<{}>", name), super::MirType::Array(elem_type) => format!("[{}]", self.format_type(elem_type)), - super::MirType::Future(inner_type) => format!("future<{}>", self.format_type(inner_type)), + super::MirType::Future(inner_type) => { + format!("future<{}>", self.format_type(inner_type)) + } super::MirType::Void => "void".to_string(), super::MirType::Unknown => "?".to_string(), } @@ -533,19 +762,21 @@ impl Default for MirPrinter { #[cfg(test)] mod tests { use super::*; - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirType, EffectMask, BasicBlockId}; - + use crate::mir::{ + BasicBlockId, EffectMask, FunctionSignature, MirFunction, MirModule, MirType, + }; + #[test] fn test_empty_module_printing() { let module = MirModule::new("test".to_string()); let printer = MirPrinter::new(); - + let output = printer.print_module(&module); - + assert!(output.contains("MIR Module: test")); assert!(!output.is_empty()); } - + #[test] fn test_function_printing() { let signature = FunctionSignature { @@ -554,23 +785,23 @@ mod tests { return_type: MirType::Void, effects: EffectMask::PURE, }; - + let function = MirFunction::new(signature, BasicBlockId::new(0)); let printer = MirPrinter::new(); - + let output = printer.print_function(&function); - + assert!(output.contains("define void @test_func(i64 %0)")); assert!(output.contains("bb0:")); } - + #[test] fn test_verbose_printing() { let module = MirModule::new("test".to_string()); let printer = MirPrinter::verbose(); - + let output = printer.print_module(&module); - + assert!(output.contains("Module Statistics")); } } diff --git a/src/mir/slot_registry.rs b/src/mir/slot_registry.rs index 85545004..91a3c7a8 100644 --- a/src/mir/slot_registry.rs +++ b/src/mir/slot_registry.rs @@ -23,24 +23,52 @@ static EXPLICIT_SLOTS: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); // Builtin type -> (method, slot) static table (slots start at 4; 0..3 are universal) -static BUILTIN_SLOTS: Lazy>> = Lazy::new(|| { - use std::iter::FromIterator; - let mut m = HashMap::new(); - m.insert("ArrayBox", vec![ - ("push", 4), ("pop", 5), ("length", 6), ("len", 6), ("get", 7), ("set", 8), - ("remove", 9), ("contains", 10), ("indexOf", 11), ("clear", 12), ("join", 13), - ("sort", 14), ("reverse", 15), ("slice", 16) - ]); - m.insert("MapBox", vec![ - ("set", 4), ("get", 5), ("has", 6), ("delete", 7), ("remove", 7), ("keys", 8), - ("values", 9), ("size", 10), ("clear", 11) - ]); - m.insert("IntegerBox", vec![("abs", 4)]); - m.insert("StringBox", vec![("substring", 4), ("concat", 5)]); - // Common plugin boxes (minimal seed) - m.insert("FileBox", vec![ ("open", 4), ("read", 5), ("write", 6), ("close", 7) ]); - HashMap::from_iter(m) -}); +static BUILTIN_SLOTS: Lazy>> = + Lazy::new(|| { + use std::iter::FromIterator; + let mut m = HashMap::new(); + m.insert( + "ArrayBox", + vec![ + ("push", 4), + ("pop", 5), + ("length", 6), + ("len", 6), + ("get", 7), + ("set", 8), + ("remove", 9), + ("contains", 10), + ("indexOf", 11), + ("clear", 12), + ("join", 13), + ("sort", 14), + ("reverse", 15), + ("slice", 16), + ], + ); + m.insert( + "MapBox", + vec![ + ("set", 4), + ("get", 5), + ("has", 6), + ("delete", 7), + ("remove", 7), + ("keys", 8), + ("values", 9), + ("size", 10), + ("clear", 11), + ], + ); + m.insert("IntegerBox", vec![("abs", 4)]); + m.insert("StringBox", vec![("substring", 4), ("concat", 5)]); + // Common plugin boxes (minimal seed) + m.insert( + "FileBox", + vec![("open", 4), ("read", 5), ("write", 6), ("close", 7)], + ); + HashMap::from_iter(m) + }); // Universal slots mapping for quick checks fn universal_slot(method: &str) -> Option { diff --git a/src/mir/value_id.rs b/src/mir/value_id.rs index b6949b40..9f6680bd 100644 --- a/src/mir/value_id.rs +++ b/src/mir/value_id.rs @@ -1,6 +1,6 @@ /*! * MIR Value ID System - SSA value tracking - * + * * Implements unique identifiers for SSA values with type safety */ @@ -15,17 +15,17 @@ impl ValueId { pub fn new(id: u32) -> Self { ValueId(id) } - + /// Get the raw ID value pub fn as_u32(self) -> u32 { self.0 } - + /// Create ValueId from usize (for array indexing) pub fn from_usize(id: usize) -> Self { ValueId(id as u32) } - + /// Convert to usize (for array indexing) pub fn to_usize(self) -> usize { self.0 as usize @@ -47,7 +47,7 @@ impl LocalId { pub fn new(id: u32) -> Self { LocalId(id) } - + /// Get the raw ID value pub fn as_u32(self) -> u32 { self.0 @@ -71,19 +71,19 @@ impl ValueIdGenerator { pub fn new() -> Self { Self { next_id: 0 } } - + /// Generate the next unique ValueId pub fn next(&mut self) -> ValueId { let id = ValueId(self.next_id); self.next_id += 1; id } - + /// Peek at the next ID without consuming it pub fn peek_next(&self) -> ValueId { ValueId(self.next_id) } - + /// Reset the generator (for testing) pub fn reset(&mut self) { self.next_id = 0; @@ -107,14 +107,14 @@ impl LocalIdGenerator { pub fn new() -> Self { Self { next_id: 0 } } - + /// Generate the next unique LocalId pub fn next(&mut self) -> LocalId { let id = LocalId(self.next_id); self.next_id += 1; id } - + /// Reset the generator (for testing) pub fn reset(&mut self) { self.next_id = 0; @@ -130,68 +130,68 @@ impl Default for LocalIdGenerator { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_value_id_creation() { let id1 = ValueId::new(0); let id2 = ValueId::new(1); - + assert_eq!(id1.as_u32(), 0); assert_eq!(id2.as_u32(), 1); assert_ne!(id1, id2); - + assert_eq!(format!("{}", id1), "%0"); assert_eq!(format!("{}", id2), "%1"); } - + #[test] fn test_value_id_generator() { let mut gen = ValueIdGenerator::new(); - + let id1 = gen.next(); let id2 = gen.next(); let id3 = gen.next(); - + assert_eq!(id1, ValueId(0)); assert_eq!(id2, ValueId(1)); assert_eq!(id3, ValueId(2)); - + assert_eq!(gen.peek_next(), ValueId(3)); } - + #[test] fn test_local_id_creation() { let local1 = LocalId::new(0); let local2 = LocalId::new(1); - + assert_eq!(format!("{}", local1), "local_0"); assert_eq!(format!("{}", local2), "local_1"); } - + #[test] fn test_local_id_generator() { let mut gen = LocalIdGenerator::new(); - + let local1 = gen.next(); let local2 = gen.next(); - + assert_eq!(local1, LocalId(0)); assert_eq!(local2, LocalId(1)); } - + #[test] fn test_value_id_ordering() { let id1 = ValueId(1); let id2 = ValueId(2); let id3 = ValueId(3); - + assert!(id1 < id2); assert!(id2 < id3); assert!(id1 < id3); - + let mut ids = vec![id3, id1, id2]; ids.sort(); - + assert_eq!(ids, vec![id1, id2, id3]); } -} \ No newline at end of file +} diff --git a/src/mir/verification.rs b/src/mir/verification.rs index 711da01b..148fc2d0 100644 --- a/src/mir/verification.rs +++ b/src/mir/verification.rs @@ -1,16 +1,16 @@ /*! * MIR Verification - SSA form and semantic verification - * + * * Implements dominance checking, SSA verification, and semantic analysis */ -use super::{MirModule, MirFunction, BasicBlockId, ValueId}; -use crate::mir::verification_types::VerificationError; +use super::{BasicBlockId, MirFunction, MirModule, ValueId}; use crate::debug::log as dlog; -use std::collections::{HashSet, HashMap}; -mod legacy; -mod barrier; +use crate::mir::verification_types::VerificationError; +use std::collections::{HashMap, HashSet}; mod awaits; +mod barrier; +mod legacy; mod utils; // VerificationError moved to crate::mir::verification_types @@ -24,15 +24,13 @@ pub struct MirVerifier { impl MirVerifier { /// Create a new MIR verifier pub fn new() -> Self { - Self { - errors: Vec::new(), - } + Self { errors: Vec::new() } } - + /// Verify an entire MIR module pub fn verify_module(&mut self, module: &MirModule) -> Result<(), Vec> { self.errors.clear(); - + for (_name, function) in &module.functions { if let Err(mut func_errors) = self.verify_function(function) { // Add function context to errors @@ -42,28 +40,31 @@ impl MirVerifier { self.errors.extend(func_errors); } } - + if self.errors.is_empty() { Ok(()) } else { Err(self.errors.clone()) } } - + /// Verify a single MIR function - pub fn verify_function(&mut self, function: &MirFunction) -> Result<(), Vec> { + pub fn verify_function( + &mut self, + function: &MirFunction, + ) -> Result<(), Vec> { let mut local_errors = Vec::new(); - + // 1. Check SSA form if let Err(mut ssa_errors) = self.verify_ssa_form(function) { local_errors.append(&mut ssa_errors); } - + // 2. Check dominance relations if let Err(mut dom_errors) = self.verify_dominance(function) { local_errors.append(&mut dom_errors); } - + // 3. Check control flow integrity if let Err(mut cfg_errors) = self.verify_control_flow(function) { local_errors.append(&mut cfg_errors); @@ -88,45 +89,75 @@ impl MirVerifier { if let Err(mut await_cp) = self.verify_await_checkpoints(function) { local_errors.append(&mut await_cp); } - + if local_errors.is_empty() { Ok(()) } else { if dlog::on("NYASH_DEBUG_VERIFIER") { - eprintln!("[VERIFY] {} errors in function {}", local_errors.len(), function.signature.name); + eprintln!( + "[VERIFY] {} errors in function {}", + local_errors.len(), + function.signature.name + ); for e in &local_errors { match e { - VerificationError::MergeUsesPredecessorValue { value, merge_block, pred_block } => { + VerificationError::MergeUsesPredecessorValue { + value, + merge_block, + pred_block, + } => { eprintln!( " • MergeUsesPredecessorValue: value=%{:?} merge_bb={:?} pred_bb={:?} -- hint: insert/use Phi in merge block for values from predecessors", value, merge_block, pred_block ); } - VerificationError::DominatorViolation { value, use_block, def_block } => { + VerificationError::DominatorViolation { + value, + use_block, + def_block, + } => { eprintln!( " • DominatorViolation: value=%{:?} use_bb={:?} def_bb={:?} -- hint: ensure definition dominates use, or route via Phi", value, use_block, def_block ); } - VerificationError::InvalidPhi { phi_value, block, reason } => { + VerificationError::InvalidPhi { + phi_value, + block, + reason, + } => { eprintln!( " • InvalidPhi: phi_dst=%{:?} in bb={:?} reason={} -- hint: check inputs cover all predecessors and placed at block start", phi_value, block, reason ); } - VerificationError::InvalidWeakRefSource { weak_ref, block, instruction_index, reason } => { + VerificationError::InvalidWeakRefSource { + weak_ref, + block, + instruction_index, + reason, + } => { eprintln!( " • InvalidWeakRefSource: weak=%{:?} at {}:{} reason='{}' -- hint: source must be WeakRef(new)/WeakNew; ensure creation precedes load and value flows correctly", weak_ref, block, instruction_index, reason ); } - VerificationError::InvalidBarrierPointer { ptr, block, instruction_index, reason } => { + VerificationError::InvalidBarrierPointer { + ptr, + block, + instruction_index, + reason, + } => { eprintln!( " • InvalidBarrierPointer: ptr=%{:?} at {}:{} reason='{}' -- hint: barrier pointer must be a valid ref (not void/null); ensure it is defined and non-void", ptr, block, instruction_index, reason ); } - VerificationError::SuspiciousBarrierContext { block, instruction_index, note } => { + VerificationError::SuspiciousBarrierContext { + block, + instruction_index, + note, + } => { eprintln!( " • SuspiciousBarrierContext: at {}:{} note='{}' -- hint: place barrier within ±2 of load/store/ref ops in same block or disable strict check", block, instruction_index, note @@ -151,12 +182,18 @@ impl MirVerifier { /// Ensure that each Await instruction (or ExternCall(env.future.await)) is immediately /// preceded and followed by a checkpoint. /// A checkpoint is either MirInstruction::Safepoint or ExternCall("env.runtime", "checkpoint"). - fn verify_await_checkpoints(&self, function: &MirFunction) -> Result<(), Vec> { + fn verify_await_checkpoints( + &self, + function: &MirFunction, + ) -> Result<(), Vec> { awaits::check_await_checkpoints(function) } /// Verify WeakRef/Barrier minimal semantics - fn verify_weakref_and_barrier(&self, function: &MirFunction) -> Result<(), Vec> { + fn verify_weakref_and_barrier( + &self, + function: &MirFunction, + ) -> Result<(), Vec> { barrier::check_weakref_and_barrier(function) } @@ -165,17 +202,16 @@ impl MirVerifier { fn verify_barrier_context(&self, function: &MirFunction) -> Result<(), Vec> { barrier::check_barrier_context(function) } - + /// Verify SSA form properties fn verify_ssa_form(&self, function: &MirFunction) -> Result<(), Vec> { // Allow non-SSA (edge-copy) mode for PHI-less MIR when enabled via env - if crate::config::env::verify_allow_no_phi() - { + if crate::config::env::verify_allow_no_phi() { return Ok(()); } let mut errors = Vec::new(); let mut definitions = HashMap::new(); - + // Check that each value is defined exactly once for (block_id, block) in &function.blocks { for (inst_idx, instruction) in block.all_instructions().enumerate() { @@ -190,7 +226,7 @@ impl MirVerifier { } } } - + // Check that all used values are defined for (block_id, block) in &function.blocks { for (inst_idx, instruction) in block.all_instructions().enumerate() { @@ -205,19 +241,18 @@ impl MirVerifier { } } } - + if errors.is_empty() { Ok(()) } else { Err(errors) } } - + /// Verify dominance relations (def must dominate use across blocks) fn verify_dominance(&self, function: &MirFunction) -> Result<(), Vec> { // Allow non-SSA (edge-copy) mode for PHI-less MIR when enabled via env - if crate::config::env::verify_allow_no_phi() - { + if crate::config::env::verify_allow_no_phi() { return Ok(()); } let mut errors = Vec::new(); @@ -229,7 +264,9 @@ impl MirVerifier { for (use_block_id, block) in &function.blocks { for instruction in block.all_instructions() { // Phi inputs are special: they are defined in predecessors; skip dominance check for them - if let super::MirInstruction::Phi { .. } = instruction { continue; } + if let super::MirInstruction::Phi { .. } = instruction { + continue; + } for used_value in instruction.used_values() { if let Some(&def_bb) = def_block.get(&used_value) { if def_bb != *use_block_id { @@ -247,13 +284,17 @@ impl MirVerifier { } } - if errors.is_empty() { Ok(()) } else { Err(errors) } + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } } - + /// Verify control flow graph integrity fn verify_control_flow(&self, function: &MirFunction) -> Result<(), Vec> { let mut errors = Vec::new(); - + // Check that all referenced blocks exist for (block_id, block) in &function.blocks { for successor in &block.successors { @@ -265,17 +306,15 @@ impl MirVerifier { } } } - + // Check that all blocks are reachable from entry let reachable = utils::compute_reachable_blocks(function); for block_id in function.blocks.keys() { if !reachable.contains(block_id) && *block_id != function.entry_block { - errors.push(VerificationError::UnreachableBlock { - block: *block_id, - }); + errors.push(VerificationError::UnreachableBlock { block: *block_id }); } } - + if errors.is_empty() { Ok(()) } else { @@ -287,8 +326,7 @@ impl MirVerifier { /// In merge blocks, values coming from predecessors must be routed through Phi. fn verify_merge_uses(&self, function: &MirFunction) -> Result<(), Vec> { // Allow non-SSA (edge-copy) mode for PHI-less MIR when enabled via env - if crate::config::env::verify_allow_no_phi() - { + if crate::config::env::verify_allow_no_phi() { return Ok(()); } let mut errors = Vec::new(); @@ -296,23 +334,34 @@ impl MirVerifier { let def_block = utils::compute_def_blocks(function); let dominators = utils::compute_dominators(function); // Helper: collect phi dsts in a block - let mut phi_dsts_in_block: std::collections::HashMap> = std::collections::HashMap::new(); + let mut phi_dsts_in_block: std::collections::HashMap< + BasicBlockId, + std::collections::HashSet, + > = std::collections::HashMap::new(); for (bid, block) in &function.blocks { let set = phi_dsts_in_block.entry(*bid).or_default(); for inst in block.all_instructions() { - if let super::MirInstruction::Phi { dst, .. } = inst { set.insert(*dst); } + if let super::MirInstruction::Phi { dst, .. } = inst { + set.insert(*dst); + } } } for (bid, block) in &function.blocks { - let Some(pred_list) = preds.get(bid) else { continue }; - if pred_list.len() < 2 { continue; } + let Some(pred_list) = preds.get(bid) else { + continue; + }; + if pred_list.len() < 2 { + continue; + } let phi_dsts = phi_dsts_in_block.get(bid); let doms_of_block = dominators.get(bid).unwrap(); // check instructions including terminator for inst in block.all_instructions() { // Skip Phi: its inputs are allowed to come from predecessors by SSA definition - if let super::MirInstruction::Phi { .. } = inst { continue; } + if let super::MirInstruction::Phi { .. } = inst { + continue; + } for used in inst.used_values() { if let Some(&db) = def_block.get(&used) { // If def doesn't dominate merge block, it must be routed via phi @@ -331,14 +380,18 @@ impl MirVerifier { } } - if errors.is_empty() { Ok(()) } else { Err(errors) } + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } } - + /// Get all verification errors from the last run pub fn get_errors(&self) -> &[VerificationError] { &self.errors } - + /// Clear verification errors pub fn clear_errors(&mut self) { self.errors.clear(); @@ -356,47 +409,124 @@ impl Default for MirVerifier { impl std::fmt::Display for VerificationError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - VerificationError::UndefinedValue { value, block, instruction_index } => { - write!(f, "Undefined value {} used in block {} at instruction {}", - value, block, instruction_index) - }, - VerificationError::MultipleDefinition { value, first_block, second_block } => { - write!(f, "Value {} defined multiple times: first in block {}, again in block {}", - value, first_block, second_block) - }, - VerificationError::InvalidPhi { phi_value, block, reason } => { - write!(f, "Invalid phi function {} in block {}: {}", - phi_value, block, reason) - }, + VerificationError::UndefinedValue { + value, + block, + instruction_index, + } => { + write!( + f, + "Undefined value {} used in block {} at instruction {}", + value, block, instruction_index + ) + } + VerificationError::MultipleDefinition { + value, + first_block, + second_block, + } => { + write!( + f, + "Value {} defined multiple times: first in block {}, again in block {}", + value, first_block, second_block + ) + } + VerificationError::InvalidPhi { + phi_value, + block, + reason, + } => { + write!( + f, + "Invalid phi function {} in block {}: {}", + phi_value, block, reason + ) + } VerificationError::UnreachableBlock { block } => { write!(f, "Unreachable block {}", block) - }, + } VerificationError::ControlFlowError { block, reason } => { write!(f, "Control flow error in block {}: {}", block, reason) - }, - VerificationError::DominatorViolation { value, use_block, def_block } => { - write!(f, "Value {} used in block {} but defined in non-dominating block {}", - value, use_block, def_block) - }, - VerificationError::MergeUsesPredecessorValue { value, merge_block, pred_block } => { - write!(f, "Merge block {} uses predecessor-defined value {} from block {} without Phi", - merge_block, value, pred_block) - }, - VerificationError::InvalidWeakRefSource { weak_ref, block, instruction_index, reason } => { - write!(f, "Invalid WeakRef source {} in block {} at {}: {}", weak_ref, block, instruction_index, reason) - }, - VerificationError::InvalidBarrierPointer { ptr, block, instruction_index, reason } => { - write!(f, "Invalid Barrier pointer {} in block {} at {}: {}", ptr, block, instruction_index, reason) - }, - VerificationError::SuspiciousBarrierContext { block, instruction_index, note } => { - write!(f, "Suspicious Barrier context in block {} at {}: {}", block, instruction_index, note) - }, - VerificationError::UnsupportedLegacyInstruction { block, instruction_index, name } => { - write!(f, "Unsupported legacy instruction '{}' in block {} at {} (enable rewrite passes)", name, block, instruction_index) - }, - VerificationError::MissingCheckpointAroundAwait { block, instruction_index, position } => { - write!(f, "Missing {} checkpoint around await in block {} at instruction {}", position, block, instruction_index) - }, + } + VerificationError::DominatorViolation { + value, + use_block, + def_block, + } => { + write!( + f, + "Value {} used in block {} but defined in non-dominating block {}", + value, use_block, def_block + ) + } + VerificationError::MergeUsesPredecessorValue { + value, + merge_block, + pred_block, + } => { + write!( + f, + "Merge block {} uses predecessor-defined value {} from block {} without Phi", + merge_block, value, pred_block + ) + } + VerificationError::InvalidWeakRefSource { + weak_ref, + block, + instruction_index, + reason, + } => { + write!( + f, + "Invalid WeakRef source {} in block {} at {}: {}", + weak_ref, block, instruction_index, reason + ) + } + VerificationError::InvalidBarrierPointer { + ptr, + block, + instruction_index, + reason, + } => { + write!( + f, + "Invalid Barrier pointer {} in block {} at {}: {}", + ptr, block, instruction_index, reason + ) + } + VerificationError::SuspiciousBarrierContext { + block, + instruction_index, + note, + } => { + write!( + f, + "Suspicious Barrier context in block {} at {}: {}", + block, instruction_index, note + ) + } + VerificationError::UnsupportedLegacyInstruction { + block, + instruction_index, + name, + } => { + write!( + f, + "Unsupported legacy instruction '{}' in block {} at {} (enable rewrite passes)", + name, block, instruction_index + ) + } + VerificationError::MissingCheckpointAroundAwait { + block, + instruction_index, + position, + } => { + write!( + f, + "Missing {} checkpoint around await in block {} at instruction {}", + position, block, instruction_index + ) + } } } } @@ -404,9 +534,11 @@ impl std::fmt::Display for VerificationError { #[cfg(test)] mod tests { use super::*; - use crate::mir::{MirFunction, FunctionSignature, MirType, EffectMask, BasicBlock, MirBuilder, MirPrinter}; - use crate::ast::{ASTNode, Span, LiteralValue}; - + use crate::ast::{ASTNode, LiteralValue, Span}; + use crate::mir::{ + BasicBlock, EffectMask, FunctionSignature, MirBuilder, MirFunction, MirPrinter, MirType, + }; + #[test] fn test_valid_function_verification() { let signature = FunctionSignature { @@ -415,16 +547,16 @@ mod tests { return_type: MirType::Void, effects: EffectMask::PURE, }; - + let entry_block = BasicBlockId::new(0); let function = MirFunction::new(signature, entry_block); - + let mut verifier = MirVerifier::new(); let result = verifier.verify_function(&function); - + assert!(result.is_ok(), "Valid function should pass verification"); } - + #[test] fn test_undefined_value_detection() { // This test would create a function with undefined value usage @@ -440,20 +572,38 @@ mod tests { let ast = ASTNode::Program { statements: vec![ ASTNode::If { - condition: Box::new(ASTNode::Literal { value: LiteralValue::Bool(true), span: Span::unknown() }), - then_body: vec![ ASTNode::Assignment { - target: Box::new(ASTNode::Variable { name: "result".to_string(), span: Span::unknown() }), - value: Box::new(ASTNode::Literal { value: LiteralValue::String("A".to_string()), span: Span::unknown() }), + condition: Box::new(ASTNode::Literal { + value: LiteralValue::Bool(true), + span: Span::unknown(), + }), + then_body: vec![ASTNode::Assignment { + target: Box::new(ASTNode::Variable { + name: "result".to_string(), + span: Span::unknown(), + }), + value: Box::new(ASTNode::Literal { + value: LiteralValue::String("A".to_string()), + span: Span::unknown(), + }), span: Span::unknown(), }], - else_body: Some(vec![ ASTNode::Assignment { - target: Box::new(ASTNode::Variable { name: "result".to_string(), span: Span::unknown() }), - value: Box::new(ASTNode::Literal { value: LiteralValue::String("B".to_string()), span: Span::unknown() }), + else_body: Some(vec![ASTNode::Assignment { + target: Box::new(ASTNode::Variable { + name: "result".to_string(), + span: Span::unknown(), + }), + value: Box::new(ASTNode::Literal { + value: LiteralValue::String("B".to_string()), + span: Span::unknown(), + }), span: Span::unknown(), }]), span: Span::unknown(), }, - ASTNode::Variable { name: "result".to_string(), span: Span::unknown() }, + ASTNode::Variable { + name: "result".to_string(), + span: Span::unknown(), + }, ], span: Span::unknown(), }; @@ -464,18 +614,24 @@ mod tests { // Verify: should be OK (no MergeUsesPredecessorValue) let mut verifier = MirVerifier::new(); let res = verifier.verify_module(&module); - if let Err(errs) = &res { eprintln!("Verifier errors: {:?}", errs); } + if let Err(errs) = &res { + eprintln!("Verifier errors: {:?}", errs); + } assert!(res.is_ok(), "MIR should pass merge-phi verification"); // Optional: ensure printer shows a phi in merge and ret returns a defined value let mut printer = MirPrinter::verbose(); let mir_text = printer.print_module(&module); - assert!(mir_text.contains("phi"), "Printed MIR should contain a phi in merge block\n{}", mir_text); + assert!( + mir_text.contains("phi"), + "Printed MIR should contain a phi in merge block\n{}", + mir_text + ); } #[test] fn test_merge_use_before_phi_detected() { - use crate::mir::{MirInstruction, ConstValue}; + use crate::mir::{ConstValue, MirInstruction}; // Construct a function with a bad merge use (no phi) let signature = FunctionSignature { @@ -495,19 +651,32 @@ mod tests { let cond = f.next_value_id(); // %0 { let b0 = f.get_block_mut(entry).unwrap(); - b0.add_instruction(MirInstruction::Const { dst: cond, value: ConstValue::Bool(true) }); - b0.add_instruction(MirInstruction::Branch { condition: cond, then_bb, else_bb }); + b0.add_instruction(MirInstruction::Const { + dst: cond, + value: ConstValue::Bool(true), + }); + b0.add_instruction(MirInstruction::Branch { + condition: cond, + then_bb, + else_bb, + }); } let v1 = f.next_value_id(); // %1 let mut b1 = BasicBlock::new(then_bb); - b1.add_instruction(MirInstruction::Const { dst: v1, value: ConstValue::String("A".to_string()) }); + b1.add_instruction(MirInstruction::Const { + dst: v1, + value: ConstValue::String("A".to_string()), + }); b1.add_instruction(MirInstruction::Jump { target: merge_bb }); f.add_block(b1); let v2 = f.next_value_id(); // %2 let mut b2 = BasicBlock::new(else_bb); - b2.add_instruction(MirInstruction::Const { dst: v2, value: ConstValue::String("B".to_string()) }); + b2.add_instruction(MirInstruction::Const { + dst: v2, + value: ConstValue::String("B".to_string()), + }); b2.add_instruction(MirInstruction::Jump { target: merge_bb }); f.add_block(b2); @@ -520,10 +689,20 @@ mod tests { let mut verifier = MirVerifier::new(); let res = verifier.verify_function(&f); - assert!(res.is_err(), "Verifier should error on merge use without phi"); + assert!( + res.is_err(), + "Verifier should error on merge use without phi" + ); let errs = res.err().unwrap(); - assert!(errs.iter().any(|e| matches!(e, VerificationError::MergeUsesPredecessorValue{..} | VerificationError::DominatorViolation{..})), - "Expected merge/dominator error, got: {:?}", errs); + assert!( + errs.iter().any(|e| matches!( + e, + VerificationError::MergeUsesPredecessorValue { .. } + | VerificationError::DominatorViolation { .. } + )), + "Expected merge/dominator error, got: {:?}", + errs + ); } #[test] @@ -536,19 +715,34 @@ mod tests { statements: vec![ ASTNode::Local { variables: vec!["i".to_string()], - initial_values: vec![Some(Box::new(ASTNode::Literal { value: LiteralValue::Integer(0), span: Span::unknown() }))], + initial_values: vec![Some(Box::new(ASTNode::Literal { + value: LiteralValue::Integer(0), + span: Span::unknown(), + }))], span: Span::unknown(), }, ASTNode::Loop { - condition: Box::new(ASTNode::Literal { value: LiteralValue::Bool(false), span: Span::unknown() }), - body: vec![ ASTNode::Assignment { - target: Box::new(ASTNode::Variable { name: "i".to_string(), span: Span::unknown() }), - value: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }), + condition: Box::new(ASTNode::Literal { + value: LiteralValue::Bool(false), + span: Span::unknown(), + }), + body: vec![ASTNode::Assignment { + target: Box::new(ASTNode::Variable { + name: "i".to_string(), + span: Span::unknown(), + }), + value: Box::new(ASTNode::Literal { + value: LiteralValue::Integer(1), + span: Span::unknown(), + }), span: Span::unknown(), }], span: Span::unknown(), }, - ASTNode::Variable { name: "i".to_string(), span: Span::unknown() }, + ASTNode::Variable { + name: "i".to_string(), + span: Span::unknown(), + }, ], span: Span::unknown(), }; @@ -559,13 +753,22 @@ mod tests { // Verify SSA/dominance: should pass let mut verifier = MirVerifier::new(); let res = verifier.verify_module(&module); - if let Err(errs) = &res { eprintln!("Verifier errors: {:?}", errs); } - assert!(res.is_ok(), "MIR loop with phi normalization should pass verification"); + if let Err(errs) = &res { + eprintln!("Verifier errors: {:?}", errs); + } + assert!( + res.is_ok(), + "MIR loop with phi normalization should pass verification" + ); // Ensure phi is printed (header phi for variable i) let printer = MirPrinter::verbose(); let mir_text = printer.print_module(&module); - assert!(mir_text.contains("phi"), "Printed MIR should contain a phi for loop header\n{}", mir_text); + assert!( + mir_text.contains("phi"), + "Printed MIR should contain a phi for loop header\n{}", + mir_text + ); } #[test] @@ -578,28 +781,52 @@ mod tests { statements: vec![ ASTNode::Local { variables: vec!["x".to_string()], - initial_values: vec![Some(Box::new(ASTNode::Literal { value: LiteralValue::Integer(0), span: Span::unknown() }))], + initial_values: vec![Some(Box::new(ASTNode::Literal { + value: LiteralValue::Integer(0), + span: Span::unknown(), + }))], span: Span::unknown(), }, ASTNode::Loop { - condition: Box::new(ASTNode::Literal { value: LiteralValue::Bool(false), span: Span::unknown() }), - body: vec![ ASTNode::If { - condition: Box::new(ASTNode::Literal { value: LiteralValue::Bool(true), span: Span::unknown() }), - then_body: vec![ ASTNode::Assignment { - target: Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }), - value: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }), + condition: Box::new(ASTNode::Literal { + value: LiteralValue::Bool(false), + span: Span::unknown(), + }), + body: vec![ASTNode::If { + condition: Box::new(ASTNode::Literal { + value: LiteralValue::Bool(true), + span: Span::unknown(), + }), + then_body: vec![ASTNode::Assignment { + target: Box::new(ASTNode::Variable { + name: "x".to_string(), + span: Span::unknown(), + }), + value: Box::new(ASTNode::Literal { + value: LiteralValue::Integer(1), + span: Span::unknown(), + }), span: Span::unknown(), }], - else_body: Some(vec![ ASTNode::Assignment { - target: Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }), - value: Box::new(ASTNode::Literal { value: LiteralValue::Integer(2), span: Span::unknown() }), + else_body: Some(vec![ASTNode::Assignment { + target: Box::new(ASTNode::Variable { + name: "x".to_string(), + span: Span::unknown(), + }), + value: Box::new(ASTNode::Literal { + value: LiteralValue::Integer(2), + span: Span::unknown(), + }), span: Span::unknown(), }]), span: Span::unknown(), }], span: Span::unknown(), }, - ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }, + ASTNode::Variable { + name: "x".to_string(), + span: Span::unknown(), + }, ], span: Span::unknown(), }; @@ -609,11 +836,20 @@ mod tests { let mut verifier = MirVerifier::new(); let res = verifier.verify_module(&module); - if let Err(errs) = &res { eprintln!("Verifier errors: {:?}", errs); } - assert!(res.is_ok(), "Nested if in loop should pass verification with proper phis"); + if let Err(errs) = &res { + eprintln!("Verifier errors: {:?}", errs); + } + assert!( + res.is_ok(), + "Nested if in loop should pass verification with proper phis" + ); let printer = MirPrinter::verbose(); let mir_text = printer.print_module(&module); - assert!(mir_text.contains("phi"), "Printed MIR should contain phi nodes for nested if/loop\n{}", mir_text); + assert!( + mir_text.contains("phi"), + "Printed MIR should contain phi nodes for nested if/loop\n{}", + mir_text + ); } } diff --git a/src/mir/verification/awaits.rs b/src/mir/verification/awaits.rs index d85d0944..610265ce 100644 --- a/src/mir/verification/awaits.rs +++ b/src/mir/verification/awaits.rs @@ -1,5 +1,5 @@ -use crate::mir::{function::MirFunction, MirInstruction}; use crate::mir::verification_types::VerificationError; +use crate::mir::{function::MirFunction, MirInstruction}; /// Ensure that each Await instruction (or ExternCall(env.future.await)) is immediately /// preceded and followed by a checkpoint. @@ -8,7 +8,11 @@ pub fn check_await_checkpoints(function: &MirFunction) -> Result<(), Vec true, - MirInstruction::ExternCall { iface_name, method_name, .. } => iface_name == "env.runtime" && method_name == "checkpoint", + MirInstruction::ExternCall { + iface_name, + method_name, + .. + } => iface_name == "env.runtime" && method_name == "checkpoint", _ => false, }; for (bid, block) in &function.blocks { @@ -16,19 +20,34 @@ pub fn check_await_checkpoints(function: &MirFunction) -> Result<(), Vec true, - MirInstruction::ExternCall { iface_name, method_name, .. } => iface_name == "env.future" && method_name == "await", + MirInstruction::ExternCall { + iface_name, + method_name, + .. + } => iface_name == "env.future" && method_name == "await", _ => false, }; if is_await_like { if idx == 0 || !is_cp(&instrs[idx - 1]) { - errors.push(VerificationError::MissingCheckpointAroundAwait { block: *bid, instruction_index: idx, position: "before" }); + errors.push(VerificationError::MissingCheckpointAroundAwait { + block: *bid, + instruction_index: idx, + position: "before", + }); } if idx + 1 >= instrs.len() || !is_cp(&instrs[idx + 1]) { - errors.push(VerificationError::MissingCheckpointAroundAwait { block: *bid, instruction_index: idx, position: "after" }); + errors.push(VerificationError::MissingCheckpointAroundAwait { + block: *bid, + instruction_index: idx, + position: "after", + }); } } } } - if errors.is_empty() { Ok(()) } else { Err(errors) } + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } } - diff --git a/src/mir/verification/barrier.rs b/src/mir/verification/barrier.rs index b040801f..8437c009 100644 --- a/src/mir/verification/barrier.rs +++ b/src/mir/verification/barrier.rs @@ -1,55 +1,82 @@ -use crate::mir::{function::MirFunction, MirInstruction}; use crate::mir::verification_types::VerificationError; +use crate::mir::{function::MirFunction, MirInstruction}; /// Verify WeakRef/Barrier minimal semantics pub fn check_weakref_and_barrier(function: &MirFunction) -> Result<(), Vec> { use crate::mir::{BasicBlockId, ValueId}; let mut errors = Vec::new(); // Build def map value -> (block, idx, &inst) - let mut def_map: std::collections::HashMap = std::collections::HashMap::new(); + let mut def_map: std::collections::HashMap = + std::collections::HashMap::new(); for (bid, block) in &function.blocks { for (idx, inst) in block.all_instructions().enumerate() { - if let Some(dst) = inst.dst_value() { def_map.insert(dst, (*bid, idx, inst)); } + if let Some(dst) = inst.dst_value() { + def_map.insert(dst, (*bid, idx, inst)); + } } } for (bid, block) in &function.blocks { for (idx, inst) in block.all_instructions().enumerate() { match inst { - MirInstruction::WeakRef { op: crate::mir::WeakRefOp::Load, value, .. } => { - match def_map.get(value) { - Some((_db, _di, def_inst)) => match def_inst { - MirInstruction::WeakRef { op: crate::mir::WeakRefOp::New, .. } | MirInstruction::WeakNew { .. } => {} - _ => errors.push(VerificationError::InvalidWeakRefSource { - weak_ref: *value, block: *bid, instruction_index: idx, - reason: "weakref.load source is not a weakref.new/weak_new".to_string(), - }), - }, - None => errors.push(VerificationError::InvalidWeakRefSource { - weak_ref: *value, block: *bid, instruction_index: idx, - reason: "weakref.load source is undefined".to_string(), + MirInstruction::WeakRef { + op: crate::mir::WeakRefOp::Load, + value, + .. + } => match def_map.get(value) { + Some((_db, _di, def_inst)) => match def_inst { + MirInstruction::WeakRef { + op: crate::mir::WeakRefOp::New, + .. + } + | MirInstruction::WeakNew { .. } => {} + _ => errors.push(VerificationError::InvalidWeakRefSource { + weak_ref: *value, + block: *bid, + instruction_index: idx, + reason: "weakref.load source is not a weakref.new/weak_new".to_string(), }), - } - } - MirInstruction::WeakLoad { weak_ref, .. } => { - match def_map.get(weak_ref) { - Some((_db, _di, def_inst)) => match def_inst { - MirInstruction::WeakNew { .. } | MirInstruction::WeakRef { op: crate::mir::WeakRefOp::New, .. } => {} - _ => errors.push(VerificationError::InvalidWeakRefSource { - weak_ref: *weak_ref, block: *bid, instruction_index: idx, - reason: "weak_load source is not a weak_new/weakref.new".to_string(), - }), - }, - None => errors.push(VerificationError::InvalidWeakRefSource { - weak_ref: *weak_ref, block: *bid, instruction_index: idx, - reason: "weak_load source is undefined".to_string(), + }, + None => errors.push(VerificationError::InvalidWeakRefSource { + weak_ref: *value, + block: *bid, + instruction_index: idx, + reason: "weakref.load source is undefined".to_string(), + }), + }, + MirInstruction::WeakLoad { weak_ref, .. } => match def_map.get(weak_ref) { + Some((_db, _di, def_inst)) => match def_inst { + MirInstruction::WeakNew { .. } + | MirInstruction::WeakRef { + op: crate::mir::WeakRefOp::New, + .. + } => {} + _ => errors.push(VerificationError::InvalidWeakRefSource { + weak_ref: *weak_ref, + block: *bid, + instruction_index: idx, + reason: "weak_load source is not a weak_new/weakref.new".to_string(), }), - } - } - MirInstruction::Barrier { ptr, .. } | MirInstruction::BarrierRead { ptr } | MirInstruction::BarrierWrite { ptr } => { + }, + None => errors.push(VerificationError::InvalidWeakRefSource { + weak_ref: *weak_ref, + block: *bid, + instruction_index: idx, + reason: "weak_load source is undefined".to_string(), + }), + }, + MirInstruction::Barrier { ptr, .. } + | MirInstruction::BarrierRead { ptr } + | MirInstruction::BarrierWrite { ptr } => { if let Some((_db, _di, def_inst)) = def_map.get(ptr) { - if let MirInstruction::Const { value: crate::mir::instruction::ConstValue::Void, .. } = def_inst { + if let MirInstruction::Const { + value: crate::mir::instruction::ConstValue::Void, + .. + } = def_inst + { errors.push(VerificationError::InvalidBarrierPointer { - ptr: *ptr, block: *bid, instruction_index: idx, + ptr: *ptr, + block: *bid, + instruction_index: idx, reason: "barrier pointer is void".to_string(), }); } @@ -59,41 +86,65 @@ pub fn check_weakref_and_barrier(function: &MirFunction) -> Result<(), Vec Result<(), Vec> { let strict = std::env::var("NYASH_VERIFY_BARRIER_STRICT").ok().as_deref() == Some("1"); - if !strict { return Ok(()); } + if !strict { + return Ok(()); + } let mut errors = Vec::new(); for (bid, block) in &function.blocks { - let mut insts: Vec<(usize, &MirInstruction)> = block.instructions.iter().enumerate().collect(); - if let Some(term) = &block.terminator { insts.push((usize::MAX, term)); } + let mut insts: Vec<(usize, &MirInstruction)> = + block.instructions.iter().enumerate().collect(); + if let Some(term) = &block.terminator { + insts.push((usize::MAX, term)); + } for (idx, inst) in &insts { - let is_barrier = matches!(inst, - MirInstruction::Barrier { .. } | - MirInstruction::BarrierRead { .. } | - MirInstruction::BarrierWrite { .. } + let is_barrier = matches!( + inst, + MirInstruction::Barrier { .. } + | MirInstruction::BarrierRead { .. } + | MirInstruction::BarrierWrite { .. } ); - if !is_barrier { continue; } + if !is_barrier { + continue; + } // Look around +-2 instructions for a memory op hint let mut has_mem_neighbor = false; for (j, other) in &insts { - if *j == *idx { continue; } - let dist = if *idx == usize::MAX || *j == usize::MAX { 99 } else { idx.max(j) - idx.min(j) }; - if dist > 2 { continue; } - if matches!(other, - MirInstruction::Load { .. } | - MirInstruction::Store { .. } | - MirInstruction::ArrayGet { .. } | - MirInstruction::ArraySet { .. } | - MirInstruction::RefGet { .. } | - MirInstruction::RefSet { .. } - ) { has_mem_neighbor = true; break; } + if *j == *idx { + continue; + } + let dist = if *idx == usize::MAX || *j == usize::MAX { + 99 + } else { + idx.max(j) - idx.min(j) + }; + if dist > 2 { + continue; + } + if matches!( + other, + MirInstruction::Load { .. } + | MirInstruction::Store { .. } + | MirInstruction::ArrayGet { .. } + | MirInstruction::ArraySet { .. } + | MirInstruction::RefGet { .. } + | MirInstruction::RefSet { .. } + ) { + has_mem_neighbor = true; + break; + } } if !has_mem_neighbor { errors.push(VerificationError::SuspiciousBarrierContext { @@ -104,5 +155,9 @@ pub fn check_barrier_context(function: &MirFunction) -> Result<(), Vec Result<(), Vec Some("TypeCheck"), // -> TypeOp(Check) - MirInstruction::Cast { .. } => Some("Cast"), // -> TypeOp(Cast) - MirInstruction::WeakNew { .. } => Some("WeakNew"), // -> WeakRef(New) - MirInstruction::WeakLoad { .. } => Some("WeakLoad"), // -> WeakRef(Load) - MirInstruction::BarrierRead { .. } => Some("BarrierRead"), // -> Barrier(Read) + MirInstruction::TypeCheck { .. } => Some("TypeCheck"), // -> TypeOp(Check) + MirInstruction::Cast { .. } => Some("Cast"), // -> TypeOp(Cast) + MirInstruction::WeakNew { .. } => Some("WeakNew"), // -> WeakRef(New) + MirInstruction::WeakLoad { .. } => Some("WeakLoad"), // -> WeakRef(Load) + MirInstruction::BarrierRead { .. } => Some("BarrierRead"), // -> Barrier(Read) MirInstruction::BarrierWrite { .. } => Some("BarrierWrite"), // -> Barrier(Write) - MirInstruction::Print { .. } => Some("Print"), // -> ExternCall(env.console.log) - MirInstruction::ArrayGet { .. } => Some("ArrayGet"), // -> BoxCall("get") - MirInstruction::ArraySet { .. } => Some("ArraySet"), // -> BoxCall("set") - MirInstruction::RefGet { .. } => Some("RefGet"), // -> BoxCall("getField") - MirInstruction::RefSet { .. } => Some("RefSet"), // -> BoxCall("setField") + MirInstruction::Print { .. } => Some("Print"), // -> ExternCall(env.console.log) + MirInstruction::ArrayGet { .. } => Some("ArrayGet"), // -> BoxCall("get") + MirInstruction::ArraySet { .. } => Some("ArraySet"), // -> BoxCall("set") + MirInstruction::RefGet { .. } => Some("RefGet"), // -> BoxCall("getField") + MirInstruction::RefSet { .. } => Some("RefSet"), // -> BoxCall("setField") MirInstruction::PluginInvoke { .. } => Some("PluginInvoke"), // -> BoxCall _ => None, }; @@ -34,6 +34,9 @@ pub fn check_no_legacy_ops(function: &MirFunction) -> Result<(), Vec HashMap = HashMap::new(); for (bid, block) in &function.blocks { for inst in block.all_instructions() { - if let Some(dst) = inst.dst_value() { def_block.insert(dst, *bid); } + if let Some(dst) = inst.dst_value() { + def_block.insert(dst, *bid); + } } } def_block @@ -40,7 +42,9 @@ pub fn compute_dominators(function: &MirFunction) -> HashMap HashMap HashSet } for instruction in &block.instructions { if let crate::mir::MirInstruction::Catch { handler_bb, .. } = instruction { - if !reachable.contains(handler_bb) { worklist.push(*handler_bb); } + if !reachable.contains(handler_bb) { + worklist.push(*handler_bb); + } } } if let Some(ref terminator) = block.terminator { if let crate::mir::MirInstruction::Catch { handler_bb, .. } = terminator { - if !reachable.contains(handler_bb) { worklist.push(*handler_bb); } + if !reachable.contains(handler_bb) { + worklist.push(*handler_bb); + } } } } @@ -83,4 +94,3 @@ pub fn compute_reachable_blocks(function: &MirFunction) -> HashSet } reachable } - diff --git a/src/mir/verification_types.rs b/src/mir/verification_types.rs index 2035d6d4..cbd78688 100644 --- a/src/mir/verification_types.rs +++ b/src/mir/verification_types.rs @@ -7,17 +7,63 @@ use super::{BasicBlockId, ValueId}; /// Verification error types #[derive(Debug, Clone, PartialEq)] pub enum VerificationError { - UndefinedValue { value: ValueId, block: BasicBlockId, instruction_index: usize }, - MultipleDefinition { value: ValueId, first_block: BasicBlockId, second_block: BasicBlockId }, - InvalidPhi { phi_value: ValueId, block: BasicBlockId, reason: String }, - UnreachableBlock { block: BasicBlockId }, - ControlFlowError { block: BasicBlockId, reason: String }, - DominatorViolation { value: ValueId, use_block: BasicBlockId, def_block: BasicBlockId }, - MergeUsesPredecessorValue { value: ValueId, merge_block: BasicBlockId, pred_block: BasicBlockId }, - InvalidWeakRefSource { weak_ref: ValueId, block: BasicBlockId, instruction_index: usize, reason: String }, - InvalidBarrierPointer { ptr: ValueId, block: BasicBlockId, instruction_index: usize, reason: String }, - SuspiciousBarrierContext { block: BasicBlockId, instruction_index: usize, note: String }, - UnsupportedLegacyInstruction { block: BasicBlockId, instruction_index: usize, name: String }, - MissingCheckpointAroundAwait { block: BasicBlockId, instruction_index: usize, position: &'static str }, + UndefinedValue { + value: ValueId, + block: BasicBlockId, + instruction_index: usize, + }, + MultipleDefinition { + value: ValueId, + first_block: BasicBlockId, + second_block: BasicBlockId, + }, + InvalidPhi { + phi_value: ValueId, + block: BasicBlockId, + reason: String, + }, + UnreachableBlock { + block: BasicBlockId, + }, + ControlFlowError { + block: BasicBlockId, + reason: String, + }, + DominatorViolation { + value: ValueId, + use_block: BasicBlockId, + def_block: BasicBlockId, + }, + MergeUsesPredecessorValue { + value: ValueId, + merge_block: BasicBlockId, + pred_block: BasicBlockId, + }, + InvalidWeakRefSource { + weak_ref: ValueId, + block: BasicBlockId, + instruction_index: usize, + reason: String, + }, + InvalidBarrierPointer { + ptr: ValueId, + block: BasicBlockId, + instruction_index: usize, + reason: String, + }, + SuspiciousBarrierContext { + block: BasicBlockId, + instruction_index: usize, + note: String, + }, + UnsupportedLegacyInstruction { + block: BasicBlockId, + instruction_index: usize, + name: String, + }, + MissingCheckpointAroundAwait { + block: BasicBlockId, + instruction_index: usize, + position: &'static str, + }, } - diff --git a/src/operator_traits.rs b/src/operator_traits.rs index 3ec12475..21659ea2 100644 --- a/src/operator_traits.rs +++ b/src/operator_traits.rs @@ -1,10 +1,10 @@ /*! * Nyash Operator Traits System - Rust-Style Trait-Based Overloading - * + * * This module implements the new operator overloading system based on the * AI consultation decision (2025-08-10). It follows Rust's trait pattern * with static/dynamic hybrid dispatch for maximum performance and flexibility. - * + * * Design Principles: * - Static dispatch when types are known at compile time * - Dynamic dispatch (vtable) when types are unknown @@ -24,7 +24,7 @@ use crate::box_trait::NyashBox; pub trait NyashAdd { /// The resulting type after applying the `+` operator type Output; - + /// Performs the `+` operation fn add(self, rhs: Rhs) -> Self::Output; } @@ -33,7 +33,7 @@ pub trait NyashAdd { pub trait NyashSub { /// The resulting type after applying the `-` operator type Output; - + /// Performs the `-` operation fn sub(self, rhs: Rhs) -> Self::Output; } @@ -42,7 +42,7 @@ pub trait NyashSub { pub trait NyashMul { /// The resulting type after applying the `*` operator type Output; - + /// Performs the `*` operation fn mul(self, rhs: Rhs) -> Self::Output; } @@ -51,7 +51,7 @@ pub trait NyashMul { pub trait NyashDiv { /// The resulting type after applying the `/` operator type Output; - + /// Performs the `/` operation fn div(self, rhs: Rhs) -> Self::Output; } @@ -64,7 +64,7 @@ pub trait DynamicAdd: NyashBox { /// Try to add this box with another box dynamically /// Returns None if the operation is not supported fn try_add(&self, other: &dyn NyashBox) -> Option>; - + /// Check if this box can be added with another box type fn can_add_with(&self, other_type: &str) -> bool; } @@ -73,7 +73,7 @@ pub trait DynamicAdd: NyashBox { pub trait DynamicSub: NyashBox { /// Try to subtract another box from this box dynamically fn try_sub(&self, other: &dyn NyashBox) -> Option>; - + /// Check if this box can be subtracted with another box type fn can_sub_with(&self, other_type: &str) -> bool; } @@ -82,7 +82,7 @@ pub trait DynamicSub: NyashBox { pub trait DynamicMul: NyashBox { /// Try to multiply this box with another box dynamically fn try_mul(&self, other: &dyn NyashBox) -> Option>; - + /// Check if this box can be multiplied with another box type fn can_mul_with(&self, other_type: &str) -> bool; } @@ -91,7 +91,7 @@ pub trait DynamicMul: NyashBox { pub trait DynamicDiv: NyashBox { /// Try to divide this box by another box dynamically fn try_div(&self, other: &dyn NyashBox) -> Option>; - + /// Check if this box can be divided by another box type fn can_div_with(&self, other_type: &str) -> bool; } @@ -107,10 +107,10 @@ pub enum OperatorError { left_type: String, right_type: String, }, - + /// Division by zero DivisionByZero, - + /// Ambiguous operation (multiple implementations match) AmbiguousOperation { operator: String, @@ -121,16 +121,30 @@ pub enum OperatorError { impl std::fmt::Display for OperatorError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - OperatorError::UnsupportedOperation { operator, left_type, right_type } => { - write!(f, "Operator '{}' is not supported between {} and {}", - operator, left_type, right_type) + OperatorError::UnsupportedOperation { + operator, + left_type, + right_type, + } => { + write!( + f, + "Operator '{}' is not supported between {} and {}", + operator, left_type, right_type + ) } OperatorError::DivisionByZero => { write!(f, "Division by zero") } - OperatorError::AmbiguousOperation { operator, candidates } => { - write!(f, "Ambiguous operator '{}': multiple candidates found: {}", - operator, candidates.join(", ")) + OperatorError::AmbiguousOperation { + operator, + candidates, + } => { + write!( + f, + "Ambiguous operator '{}': multiple candidates found: {}", + operator, + candidates.join(", ") + ) } } } @@ -140,4 +154,3 @@ impl std::error::Error for OperatorError {} // Note: OperatorResolver is now defined in box_operators.rs // Import it directly from there if needed - diff --git a/src/parser/common.rs b/src/parser/common.rs index c4e1e044..fbaeeb9f 100644 --- a/src/parser/common.rs +++ b/src/parser/common.rs @@ -1,20 +1,20 @@ /*! * Parser Common Utilities - * + * * パーサーモジュール間で共有されるヘルパー関数や型定義 * Extracted from parser/mod.rs as part of modularization */ -use crate::tokenizer::{Token, TokenType}; -use crate::ast::Span; use super::ParseError; +use crate::ast::Span; +use crate::tokenizer::{Token, TokenType}; /// Parser utility methods pub trait ParserUtils { fn tokens(&self) -> &Vec; fn current(&self) -> usize; fn current_mut(&mut self) -> &mut usize; - + /// 現在のトークンを取得 fn current_token(&self) -> &Token { self.tokens().get(self.current()).unwrap_or(&Token { @@ -23,7 +23,7 @@ pub trait ParserUtils { column: 0, }) } - + /// 次のトークンを先読み(位置を進めない) fn peek_token(&self) -> &TokenType { if self.current() + 1 < self.tokens().len() { @@ -32,7 +32,7 @@ pub trait ParserUtils { &TokenType::EOF } } - + /// N個先のトークンを先読み #[allow(dead_code)] fn peek_nth_token(&self, n: usize) -> &TokenType { @@ -42,25 +42,26 @@ pub trait ParserUtils { &TokenType::EOF } } - + /// 位置を1つ進める fn advance(&mut self) { if !self.is_at_end() { *self.current_mut() += 1; } } - + /// NEWLINEトークンをスキップ fn skip_newlines(&mut self) { while matches!(self.current_token().token_type, TokenType::NEWLINE) && !self.is_at_end() { self.advance(); } } - + /// 指定されたトークンタイプを消費 (期待通りでなければエラー) fn consume(&mut self, expected: TokenType) -> Result { - if std::mem::discriminant(&self.current_token().token_type) == - std::mem::discriminant(&expected) { + if std::mem::discriminant(&self.current_token().token_type) + == std::mem::discriminant(&expected) + { let token = self.current_token().clone(); self.advance(); Ok(token) @@ -73,47 +74,50 @@ pub trait ParserUtils { }) } } - + /// 現在のトークンが指定されたタイプかチェック fn match_token(&self, token_type: &TokenType) -> bool { - std::mem::discriminant(&self.current_token().token_type) == - std::mem::discriminant(token_type) + std::mem::discriminant(&self.current_token().token_type) + == std::mem::discriminant(token_type) } - + /// 複数のトークンタイプのいずれかにマッチするかチェック #[allow(dead_code)] fn match_any_token(&self, token_types: &[TokenType]) -> bool { let current_discriminant = std::mem::discriminant(&self.current_token().token_type); - token_types.iter().any(|tt| { - std::mem::discriminant(tt) == current_discriminant - }) + token_types + .iter() + .any(|tt| std::mem::discriminant(tt) == current_discriminant) } - + /// 終端に達したかチェック fn is_at_end(&self) -> bool { - self.current() >= self.tokens().len() || - matches!(self.current_token().token_type, TokenType::EOF) + self.current() >= self.tokens().len() + || matches!(self.current_token().token_type, TokenType::EOF) } - + /// 現在のトークンが行の終わり(NEWLINE or EOF)かチェック #[allow(dead_code)] fn is_line_end(&self) -> bool { - matches!(self.current_token().token_type, TokenType::NEWLINE | TokenType::EOF) + matches!( + self.current_token().token_type, + TokenType::NEWLINE | TokenType::EOF + ) } - + /// エラー報告用の現在位置情報を取得 #[allow(dead_code)] fn current_position(&self) -> (usize, usize) { let token = self.current_token(); (token.line, token.column) } - + /// 現在のトークンからSpanを作成 #[allow(dead_code)] fn current_span(&self) -> Span { let token = self.current_token(); Span { - start: 0, // Token doesn't have byte offset, so using 0 + start: 0, // Token doesn't have byte offset, so using 0 end: 0, line: token.line, column: token.column, @@ -125,4 +129,4 @@ pub trait ParserUtils { #[allow(dead_code)] pub fn unknown_span() -> Span { Span::unknown() -} \ No newline at end of file +} diff --git a/src/parser/declarations/box_definition.rs b/src/parser/declarations/box_definition.rs index cbe6c569..df6c13d9 100644 --- a/src/parser/declarations/box_definition.rs +++ b/src/parser/declarations/box_definition.rs @@ -1,22 +1,22 @@ /*! * Box Definition Parser Module - * + * * Box宣言(box, interface box, static box)の解析を担当 * Nyashの中核概念「Everything is Box」を実現する重要モジュール */ -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, Span}; -use crate::parser::{NyashParser, ParseError}; -use crate::parser::common::ParserUtils; use crate::must_advance; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; use std::collections::HashMap; impl NyashParser { /// box宣言をパース: box Name { fields... methods... } pub fn parse_box_declaration(&mut self) -> Result { self.consume(TokenType::BOX)?; - + let name = if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { let name = name.clone(); self.advance(); @@ -29,19 +29,19 @@ impl NyashParser { line, }); }; - + // 🔥 ジェネリクス型パラメータのパース () let type_parameters = if self.match_token(&TokenType::LESS) { self.advance(); // consume '<' let mut params = Vec::new(); - + while !self.match_token(&TokenType::GREATER) && !self.is_at_end() { must_advance!(self, _unused, "generic type parameter parsing"); - + if let TokenType::IDENTIFIER(param) = &self.current_token().token_type { params.push(param.clone()); self.advance(); - + if self.match_token(&TokenType::COMMA) { self.advance(); self.skip_newlines(); @@ -54,18 +54,18 @@ impl NyashParser { }); } } - + self.consume(TokenType::GREATER)?; // consume '>' params } else { Vec::new() }; - + // 🚀 Multi-delegation support: "from Parent1, Parent2, ..." let extends = if self.match_token(&TokenType::FROM) { self.advance(); // consume 'from' let mut parents = Vec::new(); - + // Parse first parent (required) if let TokenType::IDENTIFIER(parent) = &self.current_token().token_type { parents.push(parent.clone()); @@ -77,12 +77,12 @@ impl NyashParser { line: self.current_token().line, }); } - + // Parse additional parents (optional) while self.match_token(&TokenType::COMMA) { self.advance(); // consume ',' self.skip_newlines(); - + if let TokenType::IDENTIFIER(parent) = &self.current_token().token_type { parents.push(parent.clone()); self.advance(); @@ -94,21 +94,22 @@ impl NyashParser { }); } } - + parents } else { Vec::new() }; - + // implementsキーワードのチェック // TODO: TokenType::IMPLEMENTS is not defined in current version - let implements = if false { // self.match_token(&TokenType::IMPLEMENTS) { + let implements = if false { + // self.match_token(&TokenType::IMPLEMENTS) { self.advance(); // consume 'implements' let mut interfaces = Vec::new(); - + loop { must_advance!(self, _unused, "interface implementation parsing"); - + if let TokenType::IDENTIFIER(interface) = &self.current_token().token_type { interfaces.push(interface.clone()); self.advance(); @@ -119,51 +120,51 @@ impl NyashParser { line: self.current_token().line, }); } - + if self.match_token(&TokenType::COMMA) { self.advance(); } else { break; } } - + interfaces } else { Vec::new() }; - + self.consume(TokenType::LBRACE)?; self.skip_newlines(); // ブレース後の改行をスキップ - + let mut fields = Vec::new(); let mut methods = HashMap::new(); let mut public_fields: Vec = Vec::new(); let mut private_fields: Vec = Vec::new(); let mut constructors = HashMap::new(); let mut init_fields = Vec::new(); - let mut weak_fields = Vec::new(); // 🔗 Track weak fields - + let mut weak_fields = Vec::new(); // 🔗 Track weak fields + while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); // ループ開始時に改行をスキップ - + // RBRACEに到達していればループを抜ける if self.match_token(&TokenType::RBRACE) { break; } - + // initブロックの処理(initメソッドではない場合のみ) if self.match_token(&TokenType::INIT) && self.peek_token() != &TokenType::LPAREN { self.advance(); // consume 'init' self.consume(TokenType::LBRACE)?; - + // initブロック内のフィールド定義を読み込み while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); - + if self.match_token(&TokenType::RBRACE) { break; } - + // Check for weak modifier let is_weak = if self.match_token(&TokenType::WEAK) { self.advance(); // consume 'weak' @@ -171,14 +172,14 @@ impl NyashParser { } else { false }; - + if let TokenType::IDENTIFIER(field_name) = &self.current_token().token_type { init_fields.push(field_name.clone()); if is_weak { weak_fields.push(field_name.clone()); // 🔗 Add to weak fields list } self.advance(); - + // カンマがあればスキップ if self.match_token(&TokenType::COMMA) { self.advance(); @@ -186,72 +187,78 @@ impl NyashParser { } else { // 不正なトークンがある場合はエラー return Err(ParseError::UnexpectedToken { - expected: if is_weak { "field name after 'weak'" } else { "field name" }.to_string(), + expected: if is_weak { + "field name after 'weak'" + } else { + "field name" + } + .to_string(), found: self.current_token().token_type.clone(), line: self.current_token().line, }); } } - + self.consume(TokenType::RBRACE)?; continue; } - + // overrideキーワードをチェック let mut is_override = false; if self.match_token(&TokenType::OVERRIDE) { is_override = true; self.advance(); } - + // initトークンをメソッド名として特別処理 if self.match_token(&TokenType::INIT) && self.peek_token() == &TokenType::LPAREN { let field_or_method = "init".to_string(); self.advance(); // consume 'init' - + // コンストラクタとして処理 if self.match_token(&TokenType::LPAREN) { // initは常にコンストラクタ if is_override { return Err(ParseError::UnexpectedToken { - expected: "method definition, not constructor after override keyword".to_string(), + expected: "method definition, not constructor after override keyword" + .to_string(), found: TokenType::INIT, line: self.current_token().line, }); } // コンストラクタの処理 self.advance(); // consume '(' - + let mut params = Vec::new(); while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { must_advance!(self, _unused, "constructor parameter parsing"); - + if let TokenType::IDENTIFIER(param) = &self.current_token().token_type { params.push(param.clone()); self.advance(); } - + if self.match_token(&TokenType::COMMA) { self.advance(); } } - + self.consume(TokenType::RPAREN)?; self.consume(TokenType::LBRACE)?; - + let mut body = Vec::new(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { must_advance!(self, _unused, "constructor body parsing"); - + self.skip_newlines(); if self.match_token(&TokenType::RBRACE) { break; } body.push(self.parse_statement()?); } - + self.consume(TokenType::RBRACE)?; - + let constructor = ASTNode::FunctionDeclaration { name: field_or_method.clone(), params: params.clone(), @@ -260,60 +267,61 @@ impl NyashParser { is_override: false, // コンストラクタは常に非オーバーライド span: Span::unknown(), }; - + // 🔥 init/引数数 形式でキーを作成(インタープリターと一致させる) let constructor_key = format!("{}/{}", field_or_method, params.len()); constructors.insert(constructor_key, constructor); continue; } } - + // packキーワードの処理(ビルトインBox継承用) if self.match_token(&TokenType::PACK) && self.peek_token() == &TokenType::LPAREN { let field_or_method = "pack".to_string(); self.advance(); // consume 'pack' - + // packは常にコンストラクタ if is_override { return Err(ParseError::UnexpectedToken { - expected: "method definition, not constructor after override keyword".to_string(), + expected: "method definition, not constructor after override keyword" + .to_string(), found: TokenType::PACK, line: self.current_token().line, }); } // packコンストラクタの処理 self.advance(); // consume '(' - + let mut params = Vec::new(); while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { must_advance!(self, _unused, "pack parameter parsing"); - + if let TokenType::IDENTIFIER(param) = &self.current_token().token_type { params.push(param.clone()); self.advance(); } - + if self.match_token(&TokenType::COMMA) { self.advance(); } } - + self.consume(TokenType::RPAREN)?; self.consume(TokenType::LBRACE)?; - + let mut body = Vec::new(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { must_advance!(self, _unused, "pack body parsing"); - + self.skip_newlines(); if self.match_token(&TokenType::RBRACE) { break; } body.push(self.parse_statement()?); } - + self.consume(TokenType::RBRACE)?; - + let constructor = ASTNode::FunctionDeclaration { name: field_or_method.clone(), params: params.clone(), @@ -322,59 +330,60 @@ impl NyashParser { is_override: false, // packは常に非オーバーライド span: Span::unknown(), }; - + // 🔥 pack/引数数 形式でキーを作成(インタープリターと一致させる) let constructor_key = format!("{}/{}", field_or_method, params.len()); constructors.insert(constructor_key, constructor); continue; } - + // birthキーワードの処理(生命を与えるコンストラクタ) if self.match_token(&TokenType::BIRTH) && self.peek_token() == &TokenType::LPAREN { let field_or_method = "birth".to_string(); self.advance(); // consume 'birth' - + // birthは常にコンストラクタ if is_override { return Err(ParseError::UnexpectedToken { - expected: "method definition, not constructor after override keyword".to_string(), + expected: "method definition, not constructor after override keyword" + .to_string(), found: TokenType::BIRTH, line: self.current_token().line, }); } // birthコンストラクタの処理 self.advance(); // consume '(' - + let mut params = Vec::new(); while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { must_advance!(self, _unused, "birth parameter parsing"); - + if let TokenType::IDENTIFIER(param) = &self.current_token().token_type { params.push(param.clone()); self.advance(); } - + if self.match_token(&TokenType::COMMA) { self.advance(); } } - + self.consume(TokenType::RPAREN)?; self.consume(TokenType::LBRACE)?; - + let mut body = Vec::new(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { must_advance!(self, _unused, "birth body parsing"); - + self.skip_newlines(); if self.match_token(&TokenType::RBRACE) { break; } body.push(self.parse_statement()?); } - + self.consume(TokenType::RBRACE)?; - + let constructor = ASTNode::FunctionDeclaration { name: field_or_method.clone(), params: params.clone(), @@ -383,13 +392,13 @@ impl NyashParser { is_override: false, // birthは常に非オーバーライド span: Span::unknown(), }; - + // 🔥 birth/引数数 形式でキーを作成(インタープリターと一致させる) let constructor_key = format!("{}/{}", field_or_method, params.len()); constructors.insert(constructor_key, constructor); continue; } - + // 🚨 birth()統一システム: Box名コンストラクタ無効化 // Box名と同じ名前のコンストラクタは禁止(birth()のみ許可) if let TokenType::IDENTIFIER(id) = &self.current_token().token_type { @@ -401,13 +410,13 @@ impl NyashParser { }); } } - + // 通常のフィールド名またはメソッド名を読み取り if let TokenType::IDENTIFIER(field_or_method) = &self.current_token().token_type { let field_or_method = field_or_method.clone(); self.advance(); - // 可視性: + // 可視性: // - public { ... } / private { ... } ブロック // - public name: Type 単行(P0: 型はパースのみ、意味付けは後段) if field_or_method == "public" || field_or_method == "private" { @@ -419,12 +428,18 @@ impl NyashParser { if let TokenType::IDENTIFIER(fname) = &self.current_token().token_type { let fname = fname.clone(); // ブロックに追加 - if field_or_method == "public" { public_fields.push(fname.clone()); } else { private_fields.push(fname.clone()); } + if field_or_method == "public" { + public_fields.push(fname.clone()); + } else { + private_fields.push(fname.clone()); + } // 互換性のため、全体fieldsにも追加 fields.push(fname); self.advance(); // カンマ/改行をスキップ - if self.match_token(&TokenType::COMMA) { self.advance(); } + if self.match_token(&TokenType::COMMA) { + self.advance(); + } self.skip_newlines(); continue; } @@ -440,62 +455,79 @@ impl NyashParser { continue; } else if matches!(self.current_token().token_type, TokenType::IDENTIFIER(_)) { // 単行形式: public name[: Type] - let fname = if let TokenType::IDENTIFIER(n) = &self.current_token().token_type { n.clone() } else { unreachable!() }; + let fname = + if let TokenType::IDENTIFIER(n) = &self.current_token().token_type { + n.clone() + } else { + unreachable!() + }; self.advance(); if self.match_token(&TokenType::COLON) { self.advance(); // consume ':' - // 型名(識別子)を受理して破棄(P0) + // 型名(識別子)を受理して破棄(P0) if let TokenType::IDENTIFIER(_ty) = &self.current_token().token_type { self.advance(); } else { - return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "type name".to_string(), line: self.current_token().line }); + return Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "type name".to_string(), + line: self.current_token().line, + }); } } - if field_or_method == "public" { public_fields.push(fname.clone()); } else { private_fields.push(fname.clone()); } + if field_or_method == "public" { + public_fields.push(fname.clone()); + } else { + private_fields.push(fname.clone()); + } fields.push(fname); self.skip_newlines(); continue; } else { // public/private の後に '{' でも識別子でもない - return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "'{' or field name".to_string(), line: self.current_token().line }); + return Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "'{' or field name".to_string(), + line: self.current_token().line, + }); } } - + // メソッドかフィールドかを判定 if self.match_token(&TokenType::LPAREN) { // メソッド定義 self.advance(); // consume '(' - + let mut params = Vec::new(); while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { must_advance!(self, _unused, "method parameter parsing"); - + if let TokenType::IDENTIFIER(param) = &self.current_token().token_type { params.push(param.clone()); self.advance(); } - + if self.match_token(&TokenType::COMMA) { self.advance(); } } - + self.consume(TokenType::RPAREN)?; self.consume(TokenType::LBRACE)?; - + let mut body = Vec::new(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { must_advance!(self, _unused, "method body parsing"); - + self.skip_newlines(); if self.match_token(&TokenType::RBRACE) { break; } body.push(self.parse_statement()?); } - + self.consume(TokenType::RBRACE)?; - + let method = ASTNode::FunctionDeclaration { name: field_or_method.clone(), params, @@ -504,14 +536,14 @@ impl NyashParser { is_override, span: Span::unknown(), }; - + methods.insert(field_or_method, method); } else { // フィールド定義(P0: 型注釈 name: Type を受理して破棄) let fname = field_or_method; if self.match_token(&TokenType::COLON) { self.advance(); // consume ':' - // 型名(識別子)を許可(P0は保持せず破棄) + // 型名(識別子)を許可(P0は保持せず破棄) if let TokenType::IDENTIFIER(_ty) = &self.current_token().token_type { self.advance(); } @@ -526,14 +558,14 @@ impl NyashParser { }); } } - + self.consume(TokenType::RBRACE)?; - + // 🔥 Override validation for parent in &extends { self.validate_override_methods(&name, parent, &methods)?; } - + Ok(ASTNode::BoxDeclaration { name, fields, @@ -542,7 +574,7 @@ impl NyashParser { methods, constructors, init_fields, - weak_fields, // 🔗 Add weak fields to AST + weak_fields, // 🔗 Add weak fields to AST is_interface: false, extends, implements, @@ -552,12 +584,12 @@ impl NyashParser { span: Span::unknown(), }) } - + /// interface box宣言をパース: interface box Name { methods... } pub fn parse_interface_box_declaration(&mut self) -> Result { self.consume(TokenType::INTERFACE)?; self.consume(TokenType::BOX)?; - + let name = if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { let name = name.clone(); self.advance(); @@ -570,48 +602,48 @@ impl NyashParser { line, }); }; - + self.consume(TokenType::LBRACE)?; self.skip_newlines(); // ブレース後の改行をスキップ - + let mut methods = HashMap::new(); - + while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); // ループ開始時に改行をスキップ if let TokenType::IDENTIFIER(method_name) = &self.current_token().token_type { let method_name = method_name.clone(); self.advance(); - + // インターフェースメソッドはシグネチャのみ if self.match_token(&TokenType::LPAREN) { self.advance(); // consume '(' - + let mut params = Vec::new(); while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { if let TokenType::IDENTIFIER(param) = &self.current_token().token_type { params.push(param.clone()); self.advance(); } - + if self.match_token(&TokenType::COMMA) { self.advance(); } } - + self.consume(TokenType::RPAREN)?; - + // インターフェースメソッドは実装なし(空のbody) let method_decl = ASTNode::FunctionDeclaration { name: method_name.clone(), params, - body: vec![], // 空の実装 - is_static: false, // インターフェースメソッドは通常静的でない + body: vec![], // 空の実装 + is_static: false, // インターフェースメソッドは通常静的でない is_override: false, // デフォルトは非オーバーライド span: Span::unknown(), }; - + methods.insert(method_name, method_decl); - + // メソッド宣言後の改行をスキップ self.skip_newlines(); } else { @@ -631,9 +663,9 @@ impl NyashParser { }); } } - + self.consume(TokenType::RBRACE)?; - + Ok(ASTNode::BoxDeclaration { name, fields: vec![], // インターフェースはフィールドなし @@ -641,14 +673,14 @@ impl NyashParser { private_fields: vec![], methods, constructors: HashMap::new(), // インターフェースにコンストラクタなし - init_fields: vec![], // インターフェースにinitブロックなし - weak_fields: vec![], // 🔗 インターフェースにweak fieldsなし - is_interface: true, // インターフェースフラグ - extends: vec![], // 🚀 Multi-delegation: Changed from None to vec![] + init_fields: vec![], // インターフェースにinitブロックなし + weak_fields: vec![], // 🔗 インターフェースにweak fieldsなし + is_interface: true, // インターフェースフラグ + extends: vec![], // 🚀 Multi-delegation: Changed from None to vec![] implements: vec![], type_parameters: Vec::new(), // 🔥 インターフェースではジェネリクス未対応 - is_static: false, // インターフェースは非static - static_init: None, // インターフェースにstatic initなし + is_static: false, // インターフェースは非static + static_init: None, // インターフェースにstatic initなし span: Span::unknown(), }) } diff --git a/src/parser/declarations/dependency_helpers.rs b/src/parser/declarations/dependency_helpers.rs index 155a79f5..005966d9 100644 --- a/src/parser/declarations/dependency_helpers.rs +++ b/src/parser/declarations/dependency_helpers.rs @@ -1,6 +1,6 @@ /*! * Dependency Analysis Helpers - * + * * Static box依存関係の解析と循環依存検出 */ @@ -10,18 +10,25 @@ use std::collections::{HashMap, HashSet}; impl NyashParser { /// Static初期化ブロック内の文から依存関係を抽出 - pub(super) fn extract_dependencies_from_statements(&self, statements: &[ASTNode]) -> HashSet { + pub(super) fn extract_dependencies_from_statements( + &self, + statements: &[ASTNode], + ) -> HashSet { let mut dependencies = HashSet::new(); - + for stmt in statements { self.extract_dependencies_from_ast(stmt, &mut dependencies); } - + dependencies } - + /// AST内から静的Box参照を再帰的に検出 - pub(super) fn extract_dependencies_from_ast(&self, node: &ASTNode, dependencies: &mut HashSet) { + pub(super) fn extract_dependencies_from_ast( + &self, + node: &ASTNode, + dependencies: &mut HashSet, + ) { match node { ASTNode::FieldAccess { object, .. } => { // Math.PI のような参照を検出 @@ -46,7 +53,12 @@ impl NyashParser { ASTNode::UnaryOp { operand, .. } => { self.extract_dependencies_from_ast(operand, dependencies); } - ASTNode::If { condition, then_body, else_body, .. } => { + ASTNode::If { + condition, + then_body, + else_body, + .. + } => { self.extract_dependencies_from_ast(condition, dependencies); for stmt in then_body { self.extract_dependencies_from_ast(stmt, dependencies); @@ -57,7 +69,9 @@ impl NyashParser { } } } - ASTNode::Loop { condition, body, .. } => { + ASTNode::Loop { + condition, body, .. + } => { self.extract_dependencies_from_ast(condition, dependencies); for stmt in body { self.extract_dependencies_from_ast(stmt, dependencies); @@ -73,26 +87,26 @@ impl NyashParser { } } } - + /// 循環依存検出 pub fn check_circular_dependencies(&self) -> Result<(), ParseError> { // すべてのstatic boxに対して循環検出を実行 let all_boxes: Vec<_> = self.static_box_dependencies.keys().cloned().collect(); - + for box_name in &all_boxes { let mut visited = HashSet::new(); let mut stack = Vec::new(); - + if self.has_cycle_dfs(box_name, &mut visited, &mut stack)? { // 循環を文字列化 let cycle_str = stack.join(" -> "); return Err(ParseError::CircularDependency { cycle: cycle_str }); } } - + Ok(()) } - + /// DFSで循環依存を検出 fn has_cycle_dfs( &self, @@ -105,15 +119,15 @@ impl NyashParser { stack.push(current.to_string()); // 循環を完成させる return Ok(true); } - + // 既に訪問済みで循環がなければスキップ if visited.contains(current) { return Ok(false); } - + visited.insert(current.to_string()); stack.push(current.to_string()); - + // 依存先をチェック if let Some(dependencies) = self.static_box_dependencies.get(current) { for dep in dependencies { @@ -122,23 +136,31 @@ impl NyashParser { } } } - + stack.pop(); Ok(false) } - + /// Override メソッドの検証 - pub(super) fn validate_override_methods(&self, child_name: &str, parent_name: &str, methods: &HashMap) -> Result<(), ParseError> { + pub(super) fn validate_override_methods( + &self, + child_name: &str, + parent_name: &str, + methods: &HashMap, + ) -> Result<(), ParseError> { // 現時点では簡単な検証のみ // TODO: 親クラスのメソッドシグネチャとの比較 for (method_name, method_ast) in methods { if let ASTNode::FunctionDeclaration { is_override, .. } = method_ast { if *is_override { // 将来的にここで親クラスのメソッドが存在するかチェック - eprintln!("🔍 Validating override method '{}' in '{}' from '{}'", method_name, child_name, parent_name); + eprintln!( + "🔍 Validating override method '{}' in '{}' from '{}'", + method_name, child_name, parent_name + ); } } } Ok(()) } -} \ No newline at end of file +} diff --git a/src/parser/declarations/mod.rs b/src/parser/declarations/mod.rs index 37f69885..04992535 100644 --- a/src/parser/declarations/mod.rs +++ b/src/parser/declarations/mod.rs @@ -1,12 +1,12 @@ /*! * Parser Declarations Module - * + * * 宣言(Declaration)の解析を担当するモジュール群 * Box定義、関数定義、use文などの宣言を処理 */ pub mod box_definition; -pub mod static_box; pub mod dependency_helpers; +pub mod static_box; // Re-export commonly used items diff --git a/src/parser/declarations/static_box.rs b/src/parser/declarations/static_box.rs index d70e404a..ca766611 100644 --- a/src/parser/declarations/static_box.rs +++ b/src/parser/declarations/static_box.rs @@ -1,20 +1,20 @@ /*! * Static Box Definition Parser - * + * * static box宣言と関連ヘルパー関数 */ -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, Span}; -use crate::parser::{NyashParser, ParseError}; use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; use std::collections::HashMap; impl NyashParser { /// static box宣言をパース: static box Name { ... } pub fn parse_static_box(&mut self) -> Result { self.consume(TokenType::BOX)?; - + let name = if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { let name = name.clone(); self.advance(); @@ -27,17 +27,17 @@ impl NyashParser { line, }); }; - + // 🔥 ジェネリクス型パラメータのパース () let type_parameters = if self.match_token(&TokenType::LESS) { self.advance(); // consume '<' let mut params = Vec::new(); - + loop { if let TokenType::IDENTIFIER(param_name) = &self.current_token().token_type { params.push(param_name.clone()); self.advance(); - + if self.match_token(&TokenType::COMMA) { self.advance(); // consume ',' } else { @@ -52,24 +52,24 @@ impl NyashParser { }); } } - + self.consume(TokenType::GREATER)?; // consume '>' params } else { Vec::new() }; - + // from句のパース(Multi-delegation)- static boxでもデリゲーション可能 🚀 let extends = if self.match_token(&TokenType::FROM) { self.advance(); // consume 'from' - + let mut parent_list = Vec::new(); - + loop { if let TokenType::IDENTIFIER(parent_name) = &self.current_token().token_type { parent_list.push(parent_name.clone()); self.advance(); - + if self.match_token(&TokenType::COMMA) { self.advance(); // consume ',' } else { @@ -84,23 +84,23 @@ impl NyashParser { }); } } - + parent_list } else { Vec::new() }; - + // interface句のパース(インターフェース実装)- static boxでもinterface実装可能 let implements = if self.match_token(&TokenType::INTERFACE) { self.advance(); // consume 'interface' - + let mut interface_list = Vec::new(); - + loop { if let TokenType::IDENTIFIER(interface_name) = &self.current_token().token_type { interface_list.push(interface_name.clone()); self.advance(); - + if self.match_token(&TokenType::COMMA) { self.advance(); // consume ',' } else { @@ -115,35 +115,35 @@ impl NyashParser { }); } } - + interface_list } else { vec![] }; - + self.consume(TokenType::LBRACE)?; self.skip_newlines(); // ブレース後の改行をスキップ - + let mut fields = Vec::new(); let mut methods = HashMap::new(); let constructors = HashMap::new(); let mut init_fields = Vec::new(); - let mut weak_fields = Vec::new(); // 🔗 Track weak fields for static box + let mut weak_fields = Vec::new(); // 🔗 Track weak fields for static box let mut static_init = None; - + while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); // ループ開始時に改行をスキップ - + // RBRACEに到達していればループを抜ける if self.match_token(&TokenType::RBRACE) { break; } - + // 🔥 static { } ブロックの処理 if self.match_token(&TokenType::STATIC) { self.advance(); // consume 'static' self.consume(TokenType::LBRACE)?; - + let mut static_body = Vec::new(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); @@ -151,25 +151,25 @@ impl NyashParser { static_body.push(self.parse_statement()?); } } - + self.consume(TokenType::RBRACE)?; static_init = Some(static_body); continue; } - + // initブロックの処理 if self.match_token(&TokenType::INIT) { self.advance(); // consume 'init' self.consume(TokenType::LBRACE)?; - + // initブロック内のフィールド定義を読み込み while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); - + if self.match_token(&TokenType::RBRACE) { break; } - + // Check for weak modifier let is_weak = if self.match_token(&TokenType::WEAK) { self.advance(); // consume 'weak' @@ -177,14 +177,14 @@ impl NyashParser { } else { false }; - + if let TokenType::IDENTIFIER(field_name) = &self.current_token().token_type { init_fields.push(field_name.clone()); if is_weak { weak_fields.push(field_name.clone()); // 🔗 Add to weak fields list } self.advance(); - + // カンマがあればスキップ if self.match_token(&TokenType::COMMA) { self.advance(); @@ -192,41 +192,46 @@ impl NyashParser { } else { // 不正なトークンがある場合はエラー return Err(ParseError::UnexpectedToken { - expected: if is_weak { "field name after 'weak'" } else { "field name" }.to_string(), + expected: if is_weak { + "field name after 'weak'" + } else { + "field name" + } + .to_string(), found: self.current_token().token_type.clone(), line: self.current_token().line, }); } } - + self.consume(TokenType::RBRACE)?; continue; } - + if let TokenType::IDENTIFIER(field_or_method) = &self.current_token().token_type { let field_or_method = field_or_method.clone(); self.advance(); - + // メソッド定義か? if self.match_token(&TokenType::LPAREN) { // メソッド定義 self.advance(); // consume '(' - + let mut params = Vec::new(); while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { if let TokenType::IDENTIFIER(param) = &self.current_token().token_type { params.push(param.clone()); self.advance(); } - + if self.match_token(&TokenType::COMMA) { self.advance(); } } - + self.consume(TokenType::RPAREN)?; self.consume(TokenType::LBRACE)?; - + let mut body = Vec::new(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); @@ -234,18 +239,18 @@ impl NyashParser { body.push(self.parse_statement()?); } } - + self.consume(TokenType::RBRACE)?; - + let method = ASTNode::FunctionDeclaration { name: field_or_method.clone(), params, body, - is_static: false, // static box内のメソッドは通常メソッド + is_static: false, // static box内のメソッドは通常メソッド is_override: false, // デフォルトは非オーバーライド span: Span::unknown(), }; - + methods.insert(field_or_method, method); } else { // フィールド定義 @@ -259,17 +264,19 @@ impl NyashParser { }); } } - + self.consume(TokenType::RBRACE)?; - + // 🔥 Static初期化ブロックから依存関係を抽出 if let Some(ref init_stmts) = static_init { let dependencies = self.extract_dependencies_from_statements(init_stmts); - self.static_box_dependencies.insert(name.clone(), dependencies); + self.static_box_dependencies + .insert(name.clone(), dependencies); } else { - self.static_box_dependencies.insert(name.clone(), std::collections::HashSet::new()); + self.static_box_dependencies + .insert(name.clone(), std::collections::HashSet::new()); } - + Ok(ASTNode::BoxDeclaration { name, fields, @@ -278,13 +285,13 @@ impl NyashParser { methods, constructors, init_fields, - weak_fields, // 🔗 Add weak fields to static box construction + weak_fields, // 🔗 Add weak fields to static box construction is_interface: false, extends, implements, type_parameters, - is_static: true, // 🔥 static boxフラグを設定 - static_init, // 🔥 static初期化ブロック + is_static: true, // 🔥 static boxフラグを設定 + static_init, // 🔥 static初期化ブロック span: Span::unknown(), }) } diff --git a/src/parser/entry_sugar.rs b/src/parser/entry_sugar.rs index 4680a0ce..c2a58d1f 100644 --- a/src/parser/entry_sugar.rs +++ b/src/parser/entry_sugar.rs @@ -1,21 +1,22 @@ -use crate::parser::{NyashParser, ParseError}; use crate::parser::sugar_gate; +use crate::parser::{NyashParser, ParseError}; use crate::syntax::sugar_config::{SugarConfig, SugarLevel}; /// Parse code and apply sugar based on a provided level (None/Basic/Full) -pub fn parse_with_sugar_level(code: &str, level: SugarLevel) -> Result { +pub fn parse_with_sugar_level( + code: &str, + level: SugarLevel, +) -> Result { match level { SugarLevel::None => { let ast = NyashParser::parse_from_string(code)?; Ok(ast) } - SugarLevel::Basic | SugarLevel::Full => { - sugar_gate::with_enabled(|| { - let ast = NyashParser::parse_from_string(code)?; - let cfg = SugarConfig { level }; - let ast = crate::parser::sugar::apply_sugar(ast, &cfg); - Ok(ast) - }) - } + SugarLevel::Basic | SugarLevel::Full => sugar_gate::with_enabled(|| { + let ast = NyashParser::parse_from_string(code)?; + let cfg = SugarConfig { level }; + let ast = crate::parser::sugar::apply_sugar(ast, &cfg); + Ok(ast) + }), } } diff --git a/src/parser/expr/bit.rs b/src/parser/expr/bit.rs index 510dadbf..e9c66421 100644 --- a/src/parser/expr/bit.rs +++ b/src/parser/expr/bit.rs @@ -1,7 +1,7 @@ -use crate::parser::{NyashParser, ParseError}; -use crate::parser::common::ParserUtils; -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, BinaryOperator, Span}; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; impl NyashParser { pub(crate) fn expr_parse_bit_or(&mut self) -> Result { @@ -10,7 +10,12 @@ impl NyashParser { let operator = BinaryOperator::BitOr; self.advance(); let right = self.expr_parse_bit_xor()?; - expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() }; + expr = ASTNode::BinaryOp { + operator, + left: Box::new(expr), + right: Box::new(right), + span: Span::unknown(), + }; } Ok(expr) } @@ -21,7 +26,12 @@ impl NyashParser { let operator = BinaryOperator::BitXor; self.advance(); let right = self.expr_parse_bit_and()?; - expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() }; + expr = ASTNode::BinaryOp { + operator, + left: Box::new(expr), + right: Box::new(right), + span: Span::unknown(), + }; } Ok(expr) } @@ -32,9 +42,13 @@ impl NyashParser { let operator = BinaryOperator::BitAnd; self.advance(); let right = self.expr_parse_equality()?; - expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() }; + expr = ASTNode::BinaryOp { + operator, + left: Box::new(expr), + right: Box::new(right), + span: Span::unknown(), + }; } Ok(expr) } } - diff --git a/src/parser/expr/call.rs b/src/parser/expr/call.rs index 48ab8327..bbaae6ff 100644 --- a/src/parser/expr/call.rs +++ b/src/parser/expr/call.rs @@ -1,11 +1,13 @@ -use crate::parser::{NyashParser, ParseError}; -use crate::parser::common::ParserUtils; -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, Span}; use crate::must_advance; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; #[inline] -fn is_sugar_enabled() -> bool { crate::parser::sugar_gate::is_enabled() } +fn is_sugar_enabled() -> bool { + crate::parser::sugar_gate::is_enabled() +} impl NyashParser { pub(crate) fn expr_parse_call(&mut self) -> Result { @@ -70,12 +72,20 @@ impl NyashParser { }); } self.advance(); // consume '?.' - // ident then optional call + // ident then optional call let name = match &self.current_token().token_type { - TokenType::IDENTIFIER(s) => { let v = s.clone(); self.advance(); v } + TokenType::IDENTIFIER(s) => { + let v = s.clone(); + self.advance(); + v + } _ => { let line = self.current_token().line; - return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "identifier after '?.'".to_string(), line }); + return Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "identifier after '?.'".to_string(), + line, + }); } }; let access = if self.match_token(&TokenType::LPAREN) { @@ -85,23 +95,39 @@ impl NyashParser { while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { must_advance!(self, _unused, "safe method call arg parsing"); arguments.push(self.parse_expression()?); - if self.match_token(&TokenType::COMMA) { self.advance(); } + if self.match_token(&TokenType::COMMA) { + self.advance(); + } } self.consume(TokenType::RPAREN)?; - ASTNode::MethodCall { object: Box::new(expr.clone()), method: name, arguments, span: Span::unknown() } + ASTNode::MethodCall { + object: Box::new(expr.clone()), + method: name, + arguments, + span: Span::unknown(), + } } else { // field access - ASTNode::FieldAccess { object: Box::new(expr.clone()), field: name, span: Span::unknown() } + ASTNode::FieldAccess { + object: Box::new(expr.clone()), + field: name, + span: Span::unknown(), + } }; // Wrap with peek: peek expr { null => null, else => access(expr) } expr = ASTNode::PeekExpr { scrutinee: Box::new(expr.clone()), - arms: vec![(crate::ast::LiteralValue::Null, ASTNode::Literal { value: crate::ast::LiteralValue::Null, span: Span::unknown() })], + arms: vec![( + crate::ast::LiteralValue::Null, + ASTNode::Literal { + value: crate::ast::LiteralValue::Null, + span: Span::unknown(), + }, + )], else_expr: Box::new(access), span: Span::unknown(), }; - } else if self.match_token(&TokenType::LPAREN) { // 関数呼び出し: function(args) または 一般式呼び出し: (callee)(args) self.advance(); // consume '(' @@ -109,23 +135,43 @@ impl NyashParser { while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { must_advance!(self, _unused, "function call argument parsing"); arguments.push(self.parse_expression()?); - if self.match_token(&TokenType::COMMA) { self.advance(); } + if self.match_token(&TokenType::COMMA) { + self.advance(); + } } self.consume(TokenType::RPAREN)?; if let ASTNode::Variable { name, .. } = expr.clone() { - expr = ASTNode::FunctionCall { name, arguments, span: Span::unknown() }; + expr = ASTNode::FunctionCall { + name, + arguments, + span: Span::unknown(), + }; } else { - expr = ASTNode::Call { callee: Box::new(expr), arguments, span: Span::unknown() }; + expr = ASTNode::Call { + callee: Box::new(expr), + arguments, + span: Span::unknown(), + }; } } else if self.match_token(&TokenType::QUESTION) { let nt = self.peek_token(); - let is_ender = matches!(nt, - TokenType::NEWLINE | TokenType::EOF | TokenType::RPAREN | TokenType::COMMA | TokenType::RBRACE + let is_ender = matches!( + nt, + TokenType::NEWLINE + | TokenType::EOF + | TokenType::RPAREN + | TokenType::COMMA + | TokenType::RBRACE ); - if !is_ender { break; } + if !is_ender { + break; + } self.advance(); - expr = ASTNode::QMarkPropagate { expression: Box::new(expr), span: Span::unknown() }; + expr = ASTNode::QMarkPropagate { + expression: Box::new(expr), + span: Span::unknown(), + }; } else { break; } diff --git a/src/parser/expr/coalesce.rs b/src/parser/expr/coalesce.rs index c1900cfe..d4571d6b 100644 --- a/src/parser/expr/coalesce.rs +++ b/src/parser/expr/coalesce.rs @@ -1,10 +1,12 @@ -use crate::parser::{NyashParser, ParseError}; -use crate::parser::common::ParserUtils; -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, Span}; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; #[inline] -fn is_sugar_enabled() -> bool { crate::parser::sugar_gate::is_enabled() } +fn is_sugar_enabled() -> bool { + crate::parser::sugar_gate::is_enabled() +} impl NyashParser { pub(crate) fn expr_parse_coalesce(&mut self) -> Result { diff --git a/src/parser/expr/compare.rs b/src/parser/expr/compare.rs index 3401e0ff..00ed333d 100644 --- a/src/parser/expr/compare.rs +++ b/src/parser/expr/compare.rs @@ -1,7 +1,7 @@ -use crate::parser::{NyashParser, ParseError}; -use crate::parser::common::ParserUtils; -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, BinaryOperator, Span}; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; impl NyashParser { pub(crate) fn expr_parse_equality(&mut self) -> Result { @@ -15,11 +15,25 @@ impl NyashParser { self.advance(); let right = self.expr_parse_comparison()?; if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { - let name = match operator { BinaryOperator::Equal=>"eq", BinaryOperator::NotEqual=>"ne", _=>"cmp" }; + let name = match operator { + BinaryOperator::Equal => "eq", + BinaryOperator::NotEqual => "ne", + _ => "cmp", + }; let ok = crate::grammar::engine::get().syntax_is_allowed_binop(name); - if !ok { eprintln!("[GRAMMAR-DIFF][Parser] binop '{}' not allowed by syntax rules", name); } + if !ok { + eprintln!( + "[GRAMMAR-DIFF][Parser] binop '{}' not allowed by syntax rules", + name + ); + } } - expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() }; + expr = ASTNode::BinaryOp { + operator, + left: Box::new(expr), + right: Box::new(right), + span: Span::unknown(), + }; } Ok(expr) } @@ -40,9 +54,13 @@ impl NyashParser { }; self.advance(); let right = self.expr_parse_range()?; - expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() }; + expr = ASTNode::BinaryOp { + operator, + left: Box::new(expr), + right: Box::new(right), + span: Span::unknown(), + }; } Ok(expr) } } - diff --git a/src/parser/expr/factor.rs b/src/parser/expr/factor.rs index 1a050b4b..7568c65e 100644 --- a/src/parser/expr/factor.rs +++ b/src/parser/expr/factor.rs @@ -1,7 +1,7 @@ -use crate::parser::{NyashParser, ParseError}; -use crate::parser::common::ParserUtils; -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, BinaryOperator, Span}; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; impl NyashParser { pub(crate) fn expr_parse_factor(&mut self) -> Result { @@ -19,13 +19,26 @@ impl NyashParser { self.advance(); let right = self.parse_unary()?; if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { - let name = match operator { BinaryOperator::Multiply=>"mul", BinaryOperator::Divide=>"div", _=>"mod" }; + let name = match operator { + BinaryOperator::Multiply => "mul", + BinaryOperator::Divide => "div", + _ => "mod", + }; let ok = crate::grammar::engine::get().syntax_is_allowed_binop(name); - if !ok { eprintln!("[GRAMMAR-DIFF][Parser] binop '{}' not allowed by syntax rules", name); } + if !ok { + eprintln!( + "[GRAMMAR-DIFF][Parser] binop '{}' not allowed by syntax rules", + name + ); + } } - expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() }; + expr = ASTNode::BinaryOp { + operator, + left: Box::new(expr), + right: Box::new(right), + span: Span::unknown(), + }; } Ok(expr) } } - diff --git a/src/parser/expr/logic.rs b/src/parser/expr/logic.rs index 5643161d..dc5329d5 100644 --- a/src/parser/expr/logic.rs +++ b/src/parser/expr/logic.rs @@ -1,7 +1,7 @@ -use crate::parser::{NyashParser, ParseError}; -use crate::parser::common::ParserUtils; -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, BinaryOperator, Span}; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; impl NyashParser { pub(crate) fn expr_parse_or(&mut self) -> Result { @@ -12,9 +12,16 @@ impl NyashParser { let right = self.expr_parse_and()?; if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { let ok = crate::grammar::engine::get().syntax_is_allowed_binop("or"); - if !ok { eprintln!("[GRAMMAR-DIFF][Parser] binop 'or' not allowed by syntax rules"); } + if !ok { + eprintln!("[GRAMMAR-DIFF][Parser] binop 'or' not allowed by syntax rules"); + } } - expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() }; + expr = ASTNode::BinaryOp { + operator, + left: Box::new(expr), + right: Box::new(right), + span: Span::unknown(), + }; } Ok(expr) } @@ -27,9 +34,16 @@ impl NyashParser { let right = self.expr_parse_equality()?; if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { let ok = crate::grammar::engine::get().syntax_is_allowed_binop("and"); - if !ok { eprintln!("[GRAMMAR-DIFF][Parser] binop 'and' not allowed by syntax rules"); } + if !ok { + eprintln!("[GRAMMAR-DIFF][Parser] binop 'and' not allowed by syntax rules"); + } } - expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() }; + expr = ASTNode::BinaryOp { + operator, + left: Box::new(expr), + right: Box::new(right), + span: Span::unknown(), + }; } Ok(expr) } diff --git a/src/parser/expr/mod.rs b/src/parser/expr/mod.rs index 383324ad..dc38bad4 100644 --- a/src/parser/expr/mod.rs +++ b/src/parser/expr/mod.rs @@ -1,11 +1,11 @@ -pub(crate) mod ternary; -pub(crate) mod coalesce; -pub(crate) mod logic; pub(crate) mod bit; -pub(crate) mod compare; -pub(crate) mod range; -pub(crate) mod term; -pub(crate) mod shift; -pub(crate) mod factor; pub(crate) mod call; +pub(crate) mod coalesce; +pub(crate) mod compare; +pub(crate) mod factor; +pub(crate) mod logic; pub(crate) mod primary; +pub(crate) mod range; +pub(crate) mod shift; +pub(crate) mod term; +pub(crate) mod ternary; diff --git a/src/parser/expr/primary.rs b/src/parser/expr/primary.rs index d8005b9e..984e16b8 100644 --- a/src/parser/expr/primary.rs +++ b/src/parser/expr/primary.rs @@ -1,7 +1,7 @@ -use crate::parser::{NyashParser, ParseError}; +use crate::ast::{ASTNode, LiteralValue, Span}; use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; use crate::tokenizer::TokenType; -use crate::ast::{ASTNode, Span, LiteralValue}; impl NyashParser { pub(crate) fn expr_parse_primary(&mut self) -> Result { @@ -23,10 +23,15 @@ impl NyashParser { crate::must_advance!(self, _unused, "array literal element parsing"); let el = self.parse_expression()?; elems.push(el); - if self.match_token(&TokenType::COMMA) { self.advance(); } + if self.match_token(&TokenType::COMMA) { + self.advance(); + } } self.consume(TokenType::RBRACK)?; - Ok(ASTNode::ArrayLiteral { elements: elems, span: Span::unknown() }) + Ok(ASTNode::ArrayLiteral { + elements: elems, + span: Span::unknown(), + }) } TokenType::LBRACE => { let sugar_on = crate::parser::sugar_gate::is_enabled() @@ -38,39 +43,111 @@ impl NyashParser { self.advance(); let mut entries: Vec<(String, ASTNode)> = Vec::new(); let sugar_level = std::env::var("NYASH_SYNTAX_SUGAR_LEVEL").ok(); - let ident_key_on = std::env::var("NYASH_ENABLE_MAP_IDENT_KEY").ok().as_deref() == Some("1") || sugar_level.as_deref() == Some("full"); + let ident_key_on = std::env::var("NYASH_ENABLE_MAP_IDENT_KEY").ok().as_deref() + == Some("1") + || sugar_level.as_deref() == Some("full"); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { let key = match &self.current_token().token_type { - TokenType::STRING(s) => { let v = s.clone(); self.advance(); v } - TokenType::IDENTIFIER(id) if ident_key_on => { let v = id.clone(); self.advance(); v } + TokenType::STRING(s) => { + let v = s.clone(); + self.advance(); + v + } + TokenType::IDENTIFIER(id) if ident_key_on => { + let v = id.clone(); + self.advance(); + v + } _ => { let line = self.current_token().line; - return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: if ident_key_on { "string or identifier key in map literal".to_string() } else { "string key in map literal".to_string() }, line }); + return Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: if ident_key_on { + "string or identifier key in map literal".to_string() + } else { + "string key in map literal".to_string() + }, + line, + }); } }; self.consume(TokenType::COLON)?; let value_expr = self.parse_expression()?; entries.push((key, value_expr)); - if self.match_token(&TokenType::COMMA) { self.advance(); } + if self.match_token(&TokenType::COMMA) { + self.advance(); + } } self.consume(TokenType::RBRACE)?; - Ok(ASTNode::MapLiteral { entries, span: Span::unknown() }) + Ok(ASTNode::MapLiteral { + entries, + span: Span::unknown(), + }) + } + TokenType::INCLUDE => self.parse_include(), + TokenType::STRING(s) => { + let value = s.clone(); + self.advance(); + Ok(ASTNode::Literal { + value: LiteralValue::String(value), + span: Span::unknown(), + }) + } + TokenType::NUMBER(n) => { + let value = *n; + self.advance(); + Ok(ASTNode::Literal { + value: LiteralValue::Integer(value), + span: Span::unknown(), + }) + } + TokenType::FLOAT(f) => { + let value = *f; + self.advance(); + Ok(ASTNode::Literal { + value: LiteralValue::Float(value), + span: Span::unknown(), + }) + } + TokenType::TRUE => { + self.advance(); + Ok(ASTNode::Literal { + value: LiteralValue::Bool(true), + span: Span::unknown(), + }) + } + TokenType::FALSE => { + self.advance(); + Ok(ASTNode::Literal { + value: LiteralValue::Bool(false), + span: Span::unknown(), + }) + } + TokenType::NULL => { + self.advance(); + Ok(ASTNode::Literal { + value: LiteralValue::Null, + span: Span::unknown(), + }) } - TokenType::INCLUDE => { self.parse_include() } - TokenType::STRING(s) => { let value = s.clone(); self.advance(); Ok(ASTNode::Literal { value: LiteralValue::String(value), span: Span::unknown() }) } - TokenType::NUMBER(n) => { let value = *n; self.advance(); Ok(ASTNode::Literal { value: LiteralValue::Integer(value), span: Span::unknown() }) } - TokenType::FLOAT(f) => { let value = *f; self.advance(); Ok(ASTNode::Literal { value: LiteralValue::Float(value), span: Span::unknown() }) } - TokenType::TRUE => { self.advance(); Ok(ASTNode::Literal { value: LiteralValue::Bool(true), span: Span::unknown() }) } - TokenType::FALSE => { self.advance(); Ok(ASTNode::Literal { value: LiteralValue::Bool(false), span: Span::unknown() }) } - TokenType::NULL => { self.advance(); Ok(ASTNode::Literal { value: LiteralValue::Null, span: Span::unknown() }) } TokenType::THIS => { if std::env::var("NYASH_DEPRECATE_THIS").ok().as_deref() == Some("1") { - eprintln!("[deprecate:this] 'this' is deprecated; use 'me' instead (line {})", self.current_token().line); + eprintln!( + "[deprecate:this] 'this' is deprecated; use 'me' instead (line {})", + self.current_token().line + ); } self.advance(); - Ok(ASTNode::Me { span: Span::unknown() }) + Ok(ASTNode::Me { + span: Span::unknown(), + }) + } + TokenType::ME => { + self.advance(); + Ok(ASTNode::Me { + span: Span::unknown(), + }) } - TokenType::ME => { self.advance(); Ok(ASTNode::Me { span: Span::unknown() }) } TokenType::NEW => { self.advance(); if let TokenType::IDENTIFIER(class_name) = &self.current_token().token_type { @@ -80,10 +157,23 @@ impl NyashParser { if self.match_token(&TokenType::LESS) { self.advance(); loop { - if let TokenType::IDENTIFIER(tn) = &self.current_token().token_type { type_arguments.push(tn.clone()); self.advance(); } - else { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "type argument".to_string(), line }); } - if self.match_token(&TokenType::COMMA) { self.advance(); continue; } - self.consume(TokenType::GREATER)?; break; + if let TokenType::IDENTIFIER(tn) = &self.current_token().token_type { + type_arguments.push(tn.clone()); + self.advance(); + } else { + let line = self.current_token().line; + return Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "type argument".to_string(), + line, + }); + } + if self.match_token(&TokenType::COMMA) { + self.advance(); + continue; + } + self.consume(TokenType::GREATER)?; + break; } } self.consume(TokenType::LPAREN)?; @@ -91,49 +181,108 @@ impl NyashParser { while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { crate::must_advance!(self, _unused, "new expression argument parsing"); arguments.push(self.parse_expression()?); - if self.match_token(&TokenType::COMMA) { self.advance(); } + if self.match_token(&TokenType::COMMA) { + self.advance(); + } } self.consume(TokenType::RPAREN)?; - Ok(ASTNode::New { class, arguments, type_arguments, span: Span::unknown() }) + Ok(ASTNode::New { + class, + arguments, + type_arguments, + span: Span::unknown(), + }) } else { let line = self.current_token().line; - Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "class name".to_string(), line }) + Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "class name".to_string(), + line, + }) } } - TokenType::FROM => { self.parse_from_call() } + TokenType::FROM => self.parse_from_call(), TokenType::IDENTIFIER(name) => { let parent = name.clone(); self.advance(); if self.match_token(&TokenType::DoubleColon) { self.advance(); let method = match &self.current_token().token_type { - TokenType::IDENTIFIER(m) => { let s=m.clone(); self.advance(); s } - TokenType::INIT => { self.advance(); "init".to_string() } - TokenType::PACK => { self.advance(); "pack".to_string() } - TokenType::BIRTH => { self.advance(); "birth".to_string() } - _ => { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "method name".to_string(), line }); } + TokenType::IDENTIFIER(m) => { + let s = m.clone(); + self.advance(); + s + } + TokenType::INIT => { + self.advance(); + "init".to_string() + } + TokenType::PACK => { + self.advance(); + "pack".to_string() + } + TokenType::BIRTH => { + self.advance(); + "birth".to_string() + } + _ => { + let line = self.current_token().line; + return Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "method name".to_string(), + line, + }); + } }; self.consume(TokenType::LPAREN)?; let mut arguments = Vec::new(); while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { crate::must_advance!(self, _unused, "Parent::method call argument parsing"); arguments.push(self.parse_expression()?); - if self.match_token(&TokenType::COMMA) { self.advance(); } + if self.match_token(&TokenType::COMMA) { + self.advance(); + } } self.consume(TokenType::RPAREN)?; - Ok(ASTNode::FromCall { parent, method, arguments, span: Span::unknown() }) + Ok(ASTNode::FromCall { + parent, + method, + arguments, + span: Span::unknown(), + }) } else { - Ok(ASTNode::Variable { name: parent, span: Span::unknown() }) + Ok(ASTNode::Variable { + name: parent, + span: Span::unknown(), + }) } } - TokenType::LPAREN => { self.advance(); let expr = self.parse_expression()?; self.consume(TokenType::RPAREN)?; Ok(expr) } + TokenType::LPAREN => { + self.advance(); + let expr = self.parse_expression()?; + self.consume(TokenType::RPAREN)?; + Ok(expr) + } TokenType::FN => { self.advance(); let mut params: Vec = Vec::new(); - if self.match_token(&TokenType::LPAREN) { self.advance(); + if self.match_token(&TokenType::LPAREN) { + self.advance(); while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { - if let TokenType::IDENTIFIER(p) = &self.current_token().token_type { params.push(p.clone()); self.advance(); if self.match_token(&TokenType::COMMA) { self.advance(); } } - else { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "parameter name".to_string(), line }); } + if let TokenType::IDENTIFIER(p) = &self.current_token().token_type { + params.push(p.clone()); + self.advance(); + if self.match_token(&TokenType::COMMA) { + self.advance(); + } + } else { + let line = self.current_token().line; + return Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "parameter name".to_string(), + line, + }); + } } self.consume(TokenType::RPAREN)?; } @@ -141,12 +290,21 @@ impl NyashParser { let mut body: Vec = Vec::new(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); - if !self.match_token(&TokenType::RBRACE) { body.push(self.parse_statement()?); } + if !self.match_token(&TokenType::RBRACE) { + body.push(self.parse_statement()?); + } } self.consume(TokenType::RBRACE)?; - Ok(ASTNode::Lambda { params, body, span: Span::unknown() }) + Ok(ASTNode::Lambda { + params, + body, + span: Span::unknown(), + }) + } + _ => { + let line = self.current_token().line; + Err(ParseError::InvalidExpression { line }) } - _ => { let line = self.current_token().line; Err(ParseError::InvalidExpression { line }) } } } } diff --git a/src/parser/expr/range.rs b/src/parser/expr/range.rs index 699b7b90..724f6962 100644 --- a/src/parser/expr/range.rs +++ b/src/parser/expr/range.rs @@ -1,10 +1,12 @@ -use crate::parser::{NyashParser, ParseError}; -use crate::parser::common::ParserUtils; -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, Span}; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; #[inline] -fn is_sugar_enabled() -> bool { crate::parser::sugar_gate::is_enabled() } +fn is_sugar_enabled() -> bool { + crate::parser::sugar_gate::is_enabled() +} impl NyashParser { pub(crate) fn expr_parse_range(&mut self) -> Result { @@ -20,9 +22,12 @@ impl NyashParser { } self.advance(); let rhs = self.expr_parse_term()?; - expr = ASTNode::FunctionCall { name: "Range".to_string(), arguments: vec![expr, rhs], span: Span::unknown() }; + expr = ASTNode::FunctionCall { + name: "Range".to_string(), + arguments: vec![expr, rhs], + span: Span::unknown(), + }; } Ok(expr) } } - diff --git a/src/parser/expr/shift.rs b/src/parser/expr/shift.rs index 09ffb71a..8eca70e0 100644 --- a/src/parser/expr/shift.rs +++ b/src/parser/expr/shift.rs @@ -1,7 +1,7 @@ -use crate::parser::{NyashParser, ParseError}; -use crate::parser::common::ParserUtils; -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, BinaryOperator, Span}; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; impl NyashParser { pub(crate) fn expr_parse_shift(&mut self) -> Result { @@ -10,13 +10,23 @@ impl NyashParser { if self.match_token(&TokenType::ShiftLeft) { self.advance(); let rhs = self.expr_parse_factor()?; - expr = ASTNode::BinaryOp { operator: BinaryOperator::Shl, left: Box::new(expr), right: Box::new(rhs), span: Span::unknown() }; + expr = ASTNode::BinaryOp { + operator: BinaryOperator::Shl, + left: Box::new(expr), + right: Box::new(rhs), + span: Span::unknown(), + }; continue; } if self.match_token(&TokenType::ShiftRight) { self.advance(); let rhs = self.expr_parse_factor()?; - expr = ASTNode::BinaryOp { operator: BinaryOperator::Shr, left: Box::new(expr), right: Box::new(rhs), span: Span::unknown() }; + expr = ASTNode::BinaryOp { + operator: BinaryOperator::Shr, + left: Box::new(expr), + right: Box::new(rhs), + span: Span::unknown(), + }; continue; } break; @@ -24,4 +34,3 @@ impl NyashParser { Ok(expr) } } - diff --git a/src/parser/expr/term.rs b/src/parser/expr/term.rs index 98a48fac..14ee2ee7 100644 --- a/src/parser/expr/term.rs +++ b/src/parser/expr/term.rs @@ -1,7 +1,7 @@ -use crate::parser::{NyashParser, ParseError}; -use crate::parser::common::ParserUtils; -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, BinaryOperator, Span}; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; impl NyashParser { pub(crate) fn expr_parse_term(&mut self) -> Result { @@ -15,13 +15,26 @@ impl NyashParser { self.advance(); let right = self.expr_parse_shift()?; if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { - let name = match operator { BinaryOperator::Add=>"add", BinaryOperator::Subtract=>"sub", _=>"term" }; + let name = match operator { + BinaryOperator::Add => "add", + BinaryOperator::Subtract => "sub", + _ => "term", + }; let ok = crate::grammar::engine::get().syntax_is_allowed_binop(name); - if !ok { eprintln!("[GRAMMAR-DIFF][Parser] binop '{}' not allowed by syntax rules", name); } + if !ok { + eprintln!( + "[GRAMMAR-DIFF][Parser] binop '{}' not allowed by syntax rules", + name + ); + } } - expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() }; + expr = ASTNode::BinaryOp { + operator, + left: Box::new(expr), + right: Box::new(right), + span: Span::unknown(), + }; } Ok(expr) } } - diff --git a/src/parser/expr/ternary.rs b/src/parser/expr/ternary.rs index 669f59f3..dd7f0c3c 100644 --- a/src/parser/expr/ternary.rs +++ b/src/parser/expr/ternary.rs @@ -1,10 +1,12 @@ -use crate::parser::{NyashParser, ParseError}; -use crate::parser::common::ParserUtils; -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, Span}; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; #[inline] -fn is_sugar_enabled() -> bool { crate::parser::sugar_gate::is_enabled() } +fn is_sugar_enabled() -> bool { + crate::parser::sugar_gate::is_enabled() +} impl NyashParser { pub(crate) fn expr_parse_ternary(&mut self) -> Result { diff --git a/src/parser/expressions.rs b/src/parser/expressions.rs index 132dbfd2..c31aba20 100644 --- a/src/parser/expressions.rs +++ b/src/parser/expressions.rs @@ -1,20 +1,22 @@ /*! * Nyash Parser - Expression Parsing Module - * + * * 式(Expression)の解析を担当するモジュール * 演算子の優先順位に従った再帰下降パーサー実装 */ -use crate::tokenizer::TokenType; -use crate::ast::{ASTNode, BinaryOperator, LiteralValue, UnaryOperator, Span}; -use super::{NyashParser, ParseError}; use super::common::ParserUtils; +use super::{NyashParser, ParseError}; +use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span, UnaryOperator}; +use crate::tokenizer::TokenType; // Debug macros are now imported from the parent module via #[macro_export] use crate::must_advance; #[inline] -fn is_sugar_enabled() -> bool { crate::parser::sugar_gate::is_enabled() } +fn is_sugar_enabled() -> bool { + crate::parser::sugar_gate::is_enabled() +} impl NyashParser { /// 式をパース (演算子優先順位あり) @@ -44,29 +46,60 @@ impl NyashParser { // 変換: rhs の形に応じて lhs を先頭引数として追加 expr = match rhs { - ASTNode::FunctionCall { name, mut arguments, span } => { + ASTNode::FunctionCall { + name, + mut arguments, + span, + } => { let mut new_args = Vec::with_capacity(arguments.len() + 1); new_args.push(expr); new_args.append(&mut arguments); - ASTNode::FunctionCall { name, arguments: new_args, span } + ASTNode::FunctionCall { + name, + arguments: new_args, + span, + } } - ASTNode::MethodCall { object, method, mut arguments, span } => { + ASTNode::MethodCall { + object, + method, + mut arguments, + span, + } => { let mut new_args = Vec::with_capacity(arguments.len() + 1); new_args.push(expr); new_args.append(&mut arguments); - ASTNode::MethodCall { object, method, arguments: new_args, span } + ASTNode::MethodCall { + object, + method, + arguments: new_args, + span, + } } - ASTNode::Variable { name, .. } => { - ASTNode::FunctionCall { name, arguments: vec![expr], span: Span::unknown() } - } - ASTNode::FieldAccess { object, field, .. } => { - ASTNode::MethodCall { object, method: field, arguments: vec![expr], span: Span::unknown() } - } - ASTNode::Call { callee, mut arguments, span } => { + ASTNode::Variable { name, .. } => ASTNode::FunctionCall { + name, + arguments: vec![expr], + span: Span::unknown(), + }, + ASTNode::FieldAccess { object, field, .. } => ASTNode::MethodCall { + object, + method: field, + arguments: vec![expr], + span: Span::unknown(), + }, + ASTNode::Call { + callee, + mut arguments, + span, + } => { let mut new_args = Vec::with_capacity(arguments.len() + 1); new_args.push(expr); new_args.append(&mut arguments); - ASTNode::Call { callee, arguments: new_args, span } + ASTNode::Call { + callee, + arguments: new_args, + span, + } } other => { // 許容外: 関数/メソッド/変数/フィールド以外には適用不可 @@ -85,44 +118,70 @@ impl NyashParser { /// 三項演算子: cond ? then : else /// Grammar (Phase 12.7): TernaryExpr = NullsafeExpr ( "?" Expr ":" Expr )? /// 実装: coalesce の上に差し込み、`cond ? a : b` を If式に変換する。 - fn parse_ternary(&mut self) -> Result { self.expr_parse_ternary() } + fn parse_ternary(&mut self) -> Result { + self.expr_parse_ternary() + } /// デフォルト値(??): x ?? y => peek x { null => y, else => x } - fn parse_coalesce(&mut self) -> Result { self.expr_parse_coalesce() } - + fn parse_coalesce(&mut self) -> Result { + self.expr_parse_coalesce() + } + /// OR演算子をパース: || - fn parse_or(&mut self) -> Result { self.expr_parse_or() } - + fn parse_or(&mut self) -> Result { + self.expr_parse_or() + } + /// AND演算子をパース: && - fn parse_and(&mut self) -> Result { self.expr_parse_and() } + fn parse_and(&mut self) -> Result { + self.expr_parse_and() + } /// ビットOR: | - pub(crate) fn parse_bit_or(&mut self) -> Result { self.expr_parse_bit_or() } + pub(crate) fn parse_bit_or(&mut self) -> Result { + self.expr_parse_bit_or() + } /// ビットXOR: ^ - fn parse_bit_xor(&mut self) -> Result { self.expr_parse_bit_xor() } + fn parse_bit_xor(&mut self) -> Result { + self.expr_parse_bit_xor() + } /// ビットAND: & - fn parse_bit_and(&mut self) -> Result { self.expr_parse_bit_and() } - + fn parse_bit_and(&mut self) -> Result { + self.expr_parse_bit_and() + } + /// 等値演算子をパース: == != - pub(crate) fn parse_equality(&mut self) -> Result { self.expr_parse_equality() } - + pub(crate) fn parse_equality(&mut self) -> Result { + self.expr_parse_equality() + } + /// 比較演算子をパース: < <= > >= - fn parse_comparison(&mut self) -> Result { self.expr_parse_comparison() } + fn parse_comparison(&mut self) -> Result { + self.expr_parse_comparison() + } /// 範囲演算子: a .. b => Range(a,b) - fn parse_range(&mut self) -> Result { self.expr_parse_range() } - + fn parse_range(&mut self) -> Result { + self.expr_parse_range() + } + /// 項をパース: + - - fn parse_term(&mut self) -> Result { self.expr_parse_term() } - + fn parse_term(&mut self) -> Result { + self.expr_parse_term() + } + /// シフトをパース: << >> - fn parse_shift(&mut self) -> Result { self.expr_parse_shift() } - + fn parse_shift(&mut self) -> Result { + self.expr_parse_shift() + } + /// 因子をパース: * / - fn parse_factor(&mut self) -> Result { self.expr_parse_factor() } - + fn parse_factor(&mut self) -> Result { + self.expr_parse_factor() + } + /// 単項演算子をパース pub(crate) fn parse_unary(&mut self) -> Result { // peek式の先読み @@ -138,7 +197,7 @@ impl NyashParser { span: Span::unknown(), }); } - + if self.match_token(&TokenType::NOT) { self.advance(); // consume 'not' let operand = self.parse_unary()?; // 再帰的に単項演算をパース @@ -148,7 +207,7 @@ impl NyashParser { span: Span::unknown(), }); } - + if self.match_token(&TokenType::AWAIT) { self.advance(); // consume 'await' let expression = self.parse_unary()?; // 再帰的にパース @@ -157,7 +216,7 @@ impl NyashParser { span: Span::unknown(), }); } - + self.parse_call() } @@ -177,7 +236,9 @@ impl NyashParser { self.advance(); self.skip_newlines(); } - if self.match_token(&TokenType::RBRACE) { break; } + if self.match_token(&TokenType::RBRACE) { + break; + } // else or literal let is_else = matches!(self.current_token().token_type, TokenType::ELSE); @@ -196,7 +257,10 @@ impl NyashParser { } } self.consume(TokenType::RBRACE)?; - ASTNode::Program { statements: stmts, span: Span::unknown() } + ASTNode::Program { + statements: stmts, + span: Span::unknown(), + } } else { self.parse_expression()? }; @@ -216,7 +280,10 @@ impl NyashParser { } } self.consume(TokenType::RBRACE)?; - ASTNode::Program { statements: stmts, span: Span::unknown() } + ASTNode::Program { + statements: stmts, + span: Span::unknown(), + } } else { self.parse_expression()? }; @@ -224,8 +291,12 @@ impl NyashParser { } // 区切り(カンマや改行を許可) - if self.match_token(&TokenType::COMMA) { self.advance(); } - if self.match_token(&TokenType::NEWLINE) { self.advance(); } + if self.match_token(&TokenType::COMMA) { + self.advance(); + } + if self.match_token(&TokenType::NEWLINE) { + self.advance(); + } } self.consume(TokenType::RBRACE)?; @@ -245,29 +316,58 @@ impl NyashParser { fn parse_literal_only(&mut self) -> Result { match &self.current_token().token_type { - TokenType::STRING(s) => { let v = crate::ast::LiteralValue::String(s.clone()); self.advance(); Ok(v) } - TokenType::NUMBER(n) => { let v = crate::ast::LiteralValue::Integer(*n); self.advance(); Ok(v) } - TokenType::FLOAT(f) => { let v = crate::ast::LiteralValue::Float(*f); self.advance(); Ok(v) } - TokenType::TRUE => { self.advance(); Ok(crate::ast::LiteralValue::Bool(true)) } - TokenType::FALSE => { self.advance(); Ok(crate::ast::LiteralValue::Bool(false)) } - TokenType::NULL => { self.advance(); Ok(crate::ast::LiteralValue::Null) } + TokenType::STRING(s) => { + let v = crate::ast::LiteralValue::String(s.clone()); + self.advance(); + Ok(v) + } + TokenType::NUMBER(n) => { + let v = crate::ast::LiteralValue::Integer(*n); + self.advance(); + Ok(v) + } + TokenType::FLOAT(f) => { + let v = crate::ast::LiteralValue::Float(*f); + self.advance(); + Ok(v) + } + TokenType::TRUE => { + self.advance(); + Ok(crate::ast::LiteralValue::Bool(true)) + } + TokenType::FALSE => { + self.advance(); + Ok(crate::ast::LiteralValue::Bool(false)) + } + TokenType::NULL => { + self.advance(); + Ok(crate::ast::LiteralValue::Null) + } _ => { let line = self.current_token().line; - Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "literal".to_string(), line }) + Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "literal".to_string(), + line, + }) } } } - + /// 関数・メソッド呼び出しをパース - fn parse_call(&mut self) -> Result { self.expr_parse_call() } - + fn parse_call(&mut self) -> Result { + self.expr_parse_call() + } + /// 基本式をパース: リテラル、変数、括弧、this、new、配列リテラル(糖衣) - fn parse_primary(&mut self) -> Result { self.expr_parse_primary() } - + fn parse_primary(&mut self) -> Result { + self.expr_parse_primary() + } + /// from構文をパース: from Parent.method(arguments) pub(super) fn parse_from_call(&mut self) -> Result { self.advance(); // consume 'from' - + // Parent名を取得 let parent = if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { let name = name.clone(); @@ -281,12 +381,12 @@ impl NyashParser { line, }); }; - + // DOT とmethod名は任意(pack透明化対応) let method = if self.match_token(&TokenType::DOT) { // DOTがある場合: from Parent.method() 形式 self.advance(); // consume DOT - + // method名を取得 (IDENTIFIERまたはINITを受け入れ) match &self.current_token().token_type { TokenType::IDENTIFIER(name) => { @@ -320,28 +420,31 @@ impl NyashParser { // Phase 8.9: 明示的birth()構文を強制 let line = self.current_token().line; return Err(ParseError::TransparencySystemRemoved { - suggestion: format!("Use 'from {}.birth()' instead of 'from {}()'", parent, parent), + suggestion: format!( + "Use 'from {}.birth()' instead of 'from {}()'", + parent, parent + ), line, }); }; - + // 引数リストをパース self.consume(TokenType::LPAREN)?; let mut arguments = Vec::new(); - + while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { must_advance!(self, _unused, "from call argument parsing"); - + arguments.push(self.parse_expression()?); - + if self.match_token(&TokenType::COMMA) { self.advance(); // カンマの後の trailing comma をチェック } } - + self.consume(TokenType::RPAREN)?; - + Ok(ASTNode::FromCall { parent, method, diff --git a/src/parser/items/functions.rs b/src/parser/items/functions.rs index b73a86b8..42737945 100644 --- a/src/parser/items/functions.rs +++ b/src/parser/items/functions.rs @@ -2,17 +2,17 @@ * Function declaration parsing */ -use crate::parser::{NyashParser, ParseError}; -use crate::parser::common::ParserUtils; -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, Span}; use crate::must_advance; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; impl NyashParser { /// function宣言をパース: function name(params) { body } pub fn parse_function_declaration(&mut self) -> Result { self.consume(TokenType::FUNCTION)?; - + // 関数名を取得 let name = if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { let name = name.clone(); @@ -26,18 +26,18 @@ impl NyashParser { line, }); }; - + // パラメータリストをパース self.consume(TokenType::LPAREN)?; let mut params = Vec::new(); - + while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { must_advance!(self, _unused, "function declaration parameter parsing"); - + if let TokenType::IDENTIFIER(param) = &self.current_token().token_type { params.push(param.clone()); self.advance(); - + if self.match_token(&TokenType::COMMA) { self.advance(); } @@ -50,13 +50,13 @@ impl NyashParser { }); } } - + self.consume(TokenType::RPAREN)?; - + // 関数本体をパース self.consume(TokenType::LBRACE)?; self.skip_newlines(); - + let mut body = Vec::new(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); @@ -64,16 +64,16 @@ impl NyashParser { body.push(self.parse_statement()?); } } - + self.consume(TokenType::RBRACE)?; - + Ok(ASTNode::FunctionDeclaration { name, params, body, - is_static: false, // 通常の関数は静的でない + is_static: false, // 通常の関数は静的でない is_override: false, // デフォルトは非オーバーライド span: Span::unknown(), }) } -} \ No newline at end of file +} diff --git a/src/parser/items/global_vars.rs b/src/parser/items/global_vars.rs index 09b192c7..65e3a9ac 100644 --- a/src/parser/items/global_vars.rs +++ b/src/parser/items/global_vars.rs @@ -2,16 +2,16 @@ * Global variable parsing */ -use crate::parser::{NyashParser, ParseError}; -use crate::parser::common::ParserUtils; -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, Span}; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; impl NyashParser { /// グローバル変数をパース: global name = value pub fn parse_global_var(&mut self) -> Result { self.consume(TokenType::GLOBAL)?; - + let name = if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { let name = name.clone(); self.advance(); @@ -24,10 +24,14 @@ impl NyashParser { line, }); }; - + self.consume(TokenType::ASSIGN)?; let value = Box::new(self.parse_expression()?); - - Ok(ASTNode::GlobalVar { name, value, span: Span::unknown() }) + + Ok(ASTNode::GlobalVar { + name, + value, + span: Span::unknown(), + }) } -} \ No newline at end of file +} diff --git a/src/parser/items/mod.rs b/src/parser/items/mod.rs index 263d8a4d..a9d930c8 100644 --- a/src/parser/items/mod.rs +++ b/src/parser/items/mod.rs @@ -1,14 +1,14 @@ /*! * Parser Items Module - * + * * Top-level item declarations: * - Global variables * - Function declarations * - Static declarations (functions and boxes) */ -pub mod global_vars; pub mod functions; +pub mod global_vars; pub mod static_items; // Re-export for convenience diff --git a/src/parser/items/static_items.rs b/src/parser/items/static_items.rs index 1f919f1a..50302e78 100644 --- a/src/parser/items/static_items.rs +++ b/src/parser/items/static_items.rs @@ -3,17 +3,17 @@ * Handles both static functions and static boxes */ -use crate::parser::{NyashParser, ParseError}; -use crate::parser::common::ParserUtils; -use crate::tokenizer::TokenType; use crate::ast::{ASTNode, Span}; use crate::must_advance; +use crate::parser::common::ParserUtils; +use crate::parser::{NyashParser, ParseError}; +use crate::tokenizer::TokenType; impl NyashParser { /// 静的宣言をパース - 🔥 static function / static box 記法 pub fn parse_static_declaration(&mut self) -> Result { self.consume(TokenType::STATIC)?; - + // 次のトークンで分岐: function か box か match &self.current_token().token_type { TokenType::FUNCTION => self.parse_static_function(), @@ -28,20 +28,20 @@ impl NyashParser { } } } - + /// 静的関数宣言をパース - static function Name() { ... } fn parse_static_function(&mut self) -> Result { self.consume(TokenType::FUNCTION)?; - + // 関数名を取得(Box名.関数名の形式をサポート) let name = if let TokenType::IDENTIFIER(first_part) = &self.current_token().token_type { let mut full_name = first_part.clone(); self.advance(); - + // ドット記法をチェック(例:Math.min) if self.match_token(&TokenType::DOT) { self.advance(); // DOTを消費 - + if let TokenType::IDENTIFIER(method_name) = &self.current_token().token_type { full_name = format!("{}.{}", full_name, method_name); self.advance(); @@ -54,7 +54,7 @@ impl NyashParser { }); } } - + full_name } else { let line = self.current_token().line; @@ -64,18 +64,18 @@ impl NyashParser { line, }); }; - + // パラメータリストをパース self.consume(TokenType::LPAREN)?; let mut params = Vec::new(); - + while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() { must_advance!(self, _unused, "static function parameter parsing"); - + if let TokenType::IDENTIFIER(param) = &self.current_token().token_type { params.push(param.clone()); self.advance(); - + if self.match_token(&TokenType::COMMA) { self.advance(); } @@ -88,13 +88,13 @@ impl NyashParser { }); } } - + self.consume(TokenType::RPAREN)?; - + // 関数本体をパース self.consume(TokenType::LBRACE)?; self.skip_newlines(); - + let mut body = Vec::new(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); @@ -102,16 +102,16 @@ impl NyashParser { body.push(self.parse_statement()?); } } - + self.consume(TokenType::RBRACE)?; - + Ok(ASTNode::FunctionDeclaration { name, params, body, - is_static: true, // 🔥 静的関数フラグを設定 + is_static: true, // 🔥 静的関数フラグを設定 is_override: false, // デフォルトは非オーバーライド span: Span::unknown(), }) } -} \ No newline at end of file +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 0b73f451..7adeebe5 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,16 +1,16 @@ /*! * Nyash Parser - Rust Implementation - * + * * Python版nyashc_v4.pyのNyashParserをRustで完全再実装 * Token列をAST (Abstract Syntax Tree) に変換 - * + * * モジュール構造: * - common.rs: 共通ユーティリティとトレイト (ParserUtils) * - expressions.rs: 式パーサー (parse_expression, parse_or, parse_and等) * - statements.rs: 文パーサー (parse_statement, parse_if, parse_loop等) * - declarations/: Box宣言パーサー (box_definition, static_box, dependency_helpers) * - items/: トップレベル宣言 (global_vars, functions, static_items) - * + * * 2025-08-16: 大規模リファクタリング完了 * - 1530行 → 227行 (85%削減) * - 機能ごとにモジュール分離で保守性向上 @@ -18,24 +18,26 @@ // サブモジュール宣言 mod common; -mod expressions; -mod expr; -mod statements; mod declarations; -mod items; -pub mod sugar; // Phase 12.7-B: desugar pass (basic) pub mod entry_sugar; // helper to parse with sugar level +mod expr; +mod expressions; +mod items; +mod statements; +pub mod sugar; // Phase 12.7-B: desugar pass (basic) pub mod sugar_gate; // thread-local gate for sugar parsing (tests/docs) -// mod errors; + // mod errors; use common::ParserUtils; -use crate::tokenizer::{Token, TokenType, TokenizeError}; use crate::ast::{ASTNode, Span}; +use crate::tokenizer::{Token, TokenType, TokenizeError}; use thiserror::Error; #[inline] -fn is_sugar_enabled() -> bool { crate::parser::sugar_gate::is_enabled() } +fn is_sugar_enabled() -> bool { + crate::parser::sugar_gate::is_enabled() +} // ===== 🔥 Debug Macros ===== @@ -49,9 +51,17 @@ macro_rules! must_advance { if let Some(ref mut limit) = $parser.debug_fuel { if *limit == 0 { eprintln!("🚨 PARSER INFINITE LOOP DETECTED at {}", $location); - eprintln!("🔍 Current token: {:?} at line {}", $parser.current_token().token_type, $parser.current_token().line); - eprintln!("🔍 Parser position: {}/{}", $parser.current, $parser.tokens.len()); - return Err($crate::parser::ParseError::InfiniteLoop { + eprintln!( + "🔍 Current token: {:?} at line {}", + $parser.current_token().token_type, + $parser.current_token().line + ); + eprintln!( + "🔍 Parser position: {}/{}", + $parser.current, + $parser.tokens.len() + ); + return Err($crate::parser::ParseError::InfiniteLoop { location: $location.to_string(), token: $parser.current_token().token_type.clone(), line: $parser.current_token().line, @@ -77,32 +87,42 @@ macro_rules! debug_fuel { #[derive(Error, Debug)] pub enum ParseError { #[error("Unexpected token {found:?}, expected {expected} at line {line}")] - UnexpectedToken { found: TokenType, expected: String, line: usize }, - + UnexpectedToken { + found: TokenType, + expected: String, + line: usize, + }, + #[error("Unexpected end of file")] UnexpectedEOF, - + #[error("Invalid expression at line {line}")] InvalidExpression { line: usize }, - + #[error("Invalid statement at line {line}")] InvalidStatement { line: usize }, - + #[error("Circular dependency detected between static boxes: {cycle}")] CircularDependency { cycle: String }, - + #[error("🚨 Infinite loop detected in parser at {location} - token: {token:?} at line {line}")] - InfiniteLoop { location: String, token: TokenType, line: usize }, - + InfiniteLoop { + location: String, + token: TokenType, + line: usize, + }, + #[error("🔥 Transparency system removed: {suggestion} at line {line}")] TransparencySystemRemoved { suggestion: String, line: usize }, - - #[error("Unsupported namespace '{name}' at line {line}. Only 'nyashstd' is supported in Phase 0.")] + + #[error( + "Unsupported namespace '{name}' at line {line}. Only 'nyashstd' is supported in Phase 0." + )] UnsupportedNamespace { name: String, line: usize }, - + #[error("Expected identifier at line {line}")] ExpectedIdentifier { line: usize }, - + #[error("Tokenize error: {0}")] TokenizeError(#[from] TokenizeError), } @@ -112,7 +132,8 @@ pub struct NyashParser { pub(super) tokens: Vec, pub(super) current: usize, /// 🔥 Static box依存関係追跡(循環依存検出用) - pub(super) static_box_dependencies: std::collections::HashMap>, + pub(super) static_box_dependencies: + std::collections::HashMap>, /// 🔥 デバッグ燃料:無限ループ検出用制限値 (None = 無制限) pub(super) debug_fuel: Option, } @@ -122,11 +143,11 @@ impl ParserUtils for NyashParser { fn tokens(&self) -> &Vec { &self.tokens } - + fn current(&self) -> usize { self.current } - + fn current_mut(&mut self) -> &mut usize { &mut self.current } @@ -142,97 +163,99 @@ impl NyashParser { debug_fuel: Some(100_000), // デフォルト値 } } - + /// 文字列からパース (トークナイズ + パース) pub fn parse_from_string(input: impl Into) -> Result { Self::parse_from_string_with_fuel(input, Some(100_000)) } - + /// 文字列からパース (デバッグ燃料指定版) /// fuel: Some(n) = n回まで、None = 無制限 - pub fn parse_from_string_with_fuel(input: impl Into, fuel: Option) -> Result { + pub fn parse_from_string_with_fuel( + input: impl Into, + fuel: Option, + ) -> Result { let mut tokenizer = crate::tokenizer::NyashTokenizer::new(input); let tokens = tokenizer.tokenize()?; - + let mut parser = Self::new(tokens); parser.debug_fuel = fuel; let result = parser.parse(); result } - + /// パース実行 - Program ASTを返す pub fn parse(&mut self) -> Result { self.parse_program() } - + // ===== パース関数群 ===== - + /// プログラム全体をパース fn parse_program(&mut self) -> Result { let mut statements = Vec::new(); let mut _statement_count = 0; - + while !self.is_at_end() { - // EOF tokenはスキップ if matches!(self.current_token().token_type, TokenType::EOF) { break; } - + // NEWLINE tokenはスキップ(文の区切りとして使用) if matches!(self.current_token().token_type, TokenType::NEWLINE) { self.advance(); continue; } - + let statement = self.parse_statement()?; statements.push(statement); _statement_count += 1; } - - + // 🔥 すべてのstatic box解析後に循環依存検出 self.check_circular_dependencies()?; - - Ok(ASTNode::Program { statements, span: Span::unknown() }) + + Ok(ASTNode::Program { + statements, + span: Span::unknown(), + }) } // Statement parsing methods are now in statements.rs module - + /// 代入文または関数呼び出しをパース fn parse_assignment_or_function_call(&mut self) -> Result { - // まず左辺を式としてパース let expr = self.parse_expression()?; - + // 次のトークンが = または 複合代入演算子 なら代入文 if self.match_token(&TokenType::ASSIGN) { self.advance(); // consume '=' let value = Box::new(self.parse_expression()?); - + // 左辺が代入可能な形式かチェック match &expr { - ASTNode::Variable { .. } | - ASTNode::FieldAccess { .. } => { - Ok(ASTNode::Assignment { - target: Box::new(expr), - value, - span: Span::unknown(), - }) - } + ASTNode::Variable { .. } | ASTNode::FieldAccess { .. } => Ok(ASTNode::Assignment { + target: Box::new(expr), + value, + span: Span::unknown(), + }), _ => { let line = self.current_token().line; Err(ParseError::InvalidStatement { line }) } } - } else if self.match_token(&TokenType::PlusAssign) || - self.match_token(&TokenType::MinusAssign) || - self.match_token(&TokenType::MulAssign) || - self.match_token(&TokenType::DivAssign) { + } else if self.match_token(&TokenType::PlusAssign) + || self.match_token(&TokenType::MinusAssign) + || self.match_token(&TokenType::MulAssign) + || self.match_token(&TokenType::DivAssign) + { if !is_sugar_enabled() { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), - expected: "enable NYASH_SYNTAX_SUGAR_LEVEL=basic|full for '+=' and friends".to_string(), + expected: "enable NYASH_SYNTAX_SUGAR_LEVEL=basic|full for '+=' and friends" + .to_string(), line, }); } @@ -250,8 +273,17 @@ impl NyashParser { match &expr { ASTNode::Variable { .. } | ASTNode::FieldAccess { .. } => { let left_clone = expr.clone(); - let value = ASTNode::BinaryOp { operator: op, left: Box::new(left_clone), right: Box::new(rhs), span: Span::unknown() }; - Ok(ASTNode::Assignment { target: Box::new(expr), value: Box::new(value), span: Span::unknown() }) + let value = ASTNode::BinaryOp { + operator: op, + left: Box::new(left_clone), + right: Box::new(rhs), + span: Span::unknown(), + }; + Ok(ASTNode::Assignment { + target: Box::new(expr), + value: Box::new(value), + span: Span::unknown(), + }) } _ => { let line = self.current_token().line; @@ -263,10 +295,10 @@ impl NyashParser { Ok(expr) } } - + // Expression parsing methods are now in expressions.rs module // Utility methods are now in common.rs module via ParserUtils trait // Item parsing methods are now in items.rs module - + // ===== 🔥 Static Box循環依存検出 ===== } diff --git a/src/parser/statements.rs b/src/parser/statements.rs index dc1c3e08..3e46bb0d 100644 --- a/src/parser/statements.rs +++ b/src/parser/statements.rs @@ -1,14 +1,14 @@ /*! * Nyash Parser - Statement Parsing Module - * + * * 文(Statement)の解析を担当するモジュール * if, loop, break, return, print等の制御構文を処理 */ -use crate::tokenizer::TokenType; -use crate::ast::{ASTNode, CatchClause, Span}; -use super::{NyashParser, ParseError}; use super::common::ParserUtils; +use super::{NyashParser, ParseError}; +use crate::ast::{ASTNode, CatchClause, Span}; +use crate::tokenizer::TokenType; impl NyashParser { /// 文をパース @@ -16,67 +16,31 @@ impl NyashParser { // For grammar diff: capture starting token to classify statement keyword let start_tok = self.current_token().token_type.clone(); let result = match &start_tok { - TokenType::BOX => { - self.parse_box_declaration() - }, - TokenType::IMPORT => { - self.parse_import() - }, - TokenType::INTERFACE => { - self.parse_interface_box_declaration() - }, - TokenType::GLOBAL => { - self.parse_global_var() - }, - TokenType::FUNCTION => { - self.parse_function_declaration() - }, + TokenType::BOX => self.parse_box_declaration(), + TokenType::IMPORT => self.parse_import(), + TokenType::INTERFACE => self.parse_interface_box_declaration(), + TokenType::GLOBAL => self.parse_global_var(), + TokenType::FUNCTION => self.parse_function_declaration(), TokenType::STATIC => { - self.parse_static_declaration() // 🔥 静的宣言 (function/box) - }, - TokenType::IF => { - self.parse_if() - }, - TokenType::LOOP => { - self.parse_loop() - }, - TokenType::BREAK => { - self.parse_break() - }, - TokenType::CONTINUE => { - self.parse_continue() - }, - TokenType::RETURN => { - self.parse_return() - }, - TokenType::PRINT => { - self.parse_print() - }, - TokenType::NOWAIT => { - self.parse_nowait() - }, - TokenType::INCLUDE => { - self.parse_include() - }, - TokenType::LOCAL => { - self.parse_local() - }, - TokenType::OUTBOX => { - self.parse_outbox() - }, - TokenType::TRY => { - self.parse_try_catch() - }, - TokenType::THROW => { - self.parse_throw() - }, - TokenType::USING => { - self.parse_using() - }, + self.parse_static_declaration() // 🔥 静的宣言 (function/box) + } + TokenType::IF => self.parse_if(), + TokenType::LOOP => self.parse_loop(), + TokenType::BREAK => self.parse_break(), + TokenType::CONTINUE => self.parse_continue(), + TokenType::RETURN => self.parse_return(), + TokenType::PRINT => self.parse_print(), + TokenType::NOWAIT => self.parse_nowait(), + TokenType::INCLUDE => self.parse_include(), + TokenType::LOCAL => self.parse_local(), + TokenType::OUTBOX => self.parse_outbox(), + TokenType::TRY => self.parse_try_catch(), + TokenType::THROW => self.parse_throw(), + TokenType::USING => self.parse_using(), TokenType::FROM => { // 🔥 from構文: from Parent.method(args) または from Parent.constructor(args) self.parse_from_call_statement() - }, + } TokenType::IDENTIFIER(_name) => { // function宣言 または 代入文 または 関数呼び出し self.parse_assignment_or_function_call() @@ -91,7 +55,7 @@ impl NyashParser { Ok(self.parse_expression()?) } }; - + // Non-invasive syntax rule check if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { let kw = match start_tok { @@ -116,7 +80,12 @@ impl NyashParser { }; if let Some(k) = kw { let ok = crate::grammar::engine::get().syntax_is_allowed_statement(k); - if !ok { eprintln!("[GRAMMAR-DIFF][Parser] statement '{}' not allowed by syntax rules", k); } + if !ok { + eprintln!( + "[GRAMMAR-DIFF][Parser] statement '{}' not allowed by syntax rules", + k + ); + } } } result @@ -130,7 +99,11 @@ impl NyashParser { self.advance(); v } else { - return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "string literal".to_string(), line: self.current_token().line }); + return Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "string literal".to_string(), + line: self.current_token().line, + }); }; // Optional: 'as' Alias (treat 'as' as identifier literal) let mut alias: Option = None; @@ -141,20 +114,28 @@ impl NyashParser { alias = Some(name.clone()); self.advance(); } else { - return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "alias name".to_string(), line: self.current_token().line }); + return Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "alias name".to_string(), + line: self.current_token().line, + }); } } } - Ok(ASTNode::ImportStatement { path, alias, span: Span::unknown() }) + Ok(ASTNode::ImportStatement { + path, + alias, + span: Span::unknown(), + }) } - + /// if文をパース: if (condition) { body } else if ... else { body } pub(super) fn parse_if(&mut self) -> Result { self.advance(); // consume 'if' - + // 条件部分を取得 let condition = Box::new(self.parse_expression()?); - + // then部分を取得 self.consume(TokenType::LBRACE)?; let mut then_body = Vec::new(); @@ -165,11 +146,11 @@ impl NyashParser { } } self.consume(TokenType::RBRACE)?; - + // else if/else部分を処理 let else_body = if self.match_token(&TokenType::ELSE) { self.advance(); // consume 'else' - + if self.match_token(&TokenType::IF) { // else if を ネストしたifとして処理 let nested_if = self.parse_if()?; @@ -190,7 +171,7 @@ impl NyashParser { } else { None }; - + Ok(ASTNode::If { condition, then_body, @@ -198,11 +179,11 @@ impl NyashParser { span: Span::unknown(), }) } - + /// loop文をパース pub(super) fn parse_loop(&mut self) -> Result { self.advance(); // consume 'loop' - + // 条件部分を取得(省略可: `loop { ... }` は無条件ループとして扱う) let condition = if self.match_token(&TokenType::LPAREN) { self.advance(); // consume '(' @@ -211,9 +192,12 @@ impl NyashParser { cond } else { // default: true - Box::new(ASTNode::Literal { value: crate::ast::LiteralValue::Bool(true), span: Span::unknown() }) + Box::new(ASTNode::Literal { + value: crate::ast::LiteralValue::Bool(true), + span: Span::unknown(), + }) }; - + // body部分を取得 self.consume(TokenType::LBRACE)?; let mut body = Vec::new(); @@ -224,30 +208,34 @@ impl NyashParser { } } self.consume(TokenType::RBRACE)?; - + Ok(ASTNode::Loop { condition, body, span: Span::unknown(), }) } - + /// break文をパース pub(super) fn parse_break(&mut self) -> Result { self.advance(); // consume 'break' - Ok(ASTNode::Break { span: Span::unknown() }) + Ok(ASTNode::Break { + span: Span::unknown(), + }) } /// continue文をパース pub(super) fn parse_continue(&mut self) -> Result { self.advance(); // consume 'continue' - Ok(ASTNode::Continue { span: Span::unknown() }) + Ok(ASTNode::Continue { + span: Span::unknown(), + }) } - + /// return文をパース pub(super) fn parse_return(&mut self) -> Result { self.advance(); // consume 'return' - // 許容: 改行をスキップしてから式有無を判定 + // 許容: 改行をスキップしてから式有無を判定 self.skip_newlines(); // returnの後に式があるかチェック(RBRACE/EOFなら値なし) let value = if self.is_at_end() || self.match_token(&TokenType::RBRACE) { @@ -255,24 +243,30 @@ impl NyashParser { } else { Some(Box::new(self.parse_expression()?)) }; - - Ok(ASTNode::Return { value, span: Span::unknown() }) + + Ok(ASTNode::Return { + value, + span: Span::unknown(), + }) } - + /// print文をパース pub(super) fn parse_print(&mut self) -> Result { self.advance(); // consume 'print' self.consume(TokenType::LPAREN)?; let value = Box::new(self.parse_expression()?); self.consume(TokenType::RPAREN)?; - - Ok(ASTNode::Print { expression: value, span: Span::unknown() }) + + Ok(ASTNode::Print { + expression: value, + span: Span::unknown(), + }) } - + /// nowait文をパース: nowait variable = expression pub(super) fn parse_nowait(&mut self) -> Result { self.advance(); // consume 'nowait' - + // 変数名を取得 let variable = if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { let name = name.clone(); @@ -286,21 +280,21 @@ impl NyashParser { line, }); }; - + self.consume(TokenType::ASSIGN)?; let expression = Box::new(self.parse_expression()?); - + Ok(ASTNode::Nowait { variable, expression, span: Span::unknown(), }) } - + /// include文をパース pub(super) fn parse_include(&mut self) -> Result { self.advance(); // consume 'include' - + let path = if let TokenType::STRING(path) = &self.current_token().token_type { let path = path.clone(); self.advance(); @@ -313,27 +307,30 @@ impl NyashParser { line, }); }; - - Ok(ASTNode::Include { filename: path, span: Span::unknown() }) + + Ok(ASTNode::Include { + filename: path, + span: Span::unknown(), + }) } - + /// local変数宣言をパース: local var1, var2, var3 または local x = 10 pub(super) fn parse_local(&mut self) -> Result { self.advance(); // consume 'local' - + let mut names = Vec::new(); let mut initial_values = Vec::new(); - + // 最初の変数名を取得 if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { names.push(name.clone()); self.advance(); - + // = があれば初期値を設定 if self.match_token(&TokenType::ASSIGN) { self.advance(); // consume '=' initial_values.push(Some(Box::new(self.parse_expression()?))); - + // 初期化付きlocalは単一変数のみ(カンマ区切り不可) Ok(ASTNode::Local { variables: names, @@ -343,11 +340,11 @@ impl NyashParser { } else { // 初期化なしの場合はカンマ区切りで複数変数可能 initial_values.push(None); - + // カンマ区切りで追加の変数名を取得 while self.match_token(&TokenType::COMMA) { self.advance(); // consume ',' - + if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { names.push(name.clone()); initial_values.push(None); @@ -361,7 +358,7 @@ impl NyashParser { }); } } - + Ok(ASTNode::Local { variables: names, initial_values, @@ -377,22 +374,22 @@ impl NyashParser { }) } } - + /// outbox変数宣言をパース: outbox var1, var2, var3 pub(super) fn parse_outbox(&mut self) -> Result { self.advance(); // consume 'outbox' - + let mut names = Vec::new(); - + // 最初の変数名を取得 if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { names.push(name.clone()); self.advance(); - + // カンマ区切りで追加の変数名を取得 while self.match_token(&TokenType::COMMA) { self.advance(); // consume ',' - + if let TokenType::IDENTIFIER(name) = &self.current_token().token_type { names.push(name.clone()); self.advance(); @@ -405,7 +402,7 @@ impl NyashParser { }); } } - + let num_vars = names.len(); Ok(ASTNode::Outbox { variables: names, @@ -421,12 +418,12 @@ impl NyashParser { }) } } - + /// try-catch文をパース pub(super) fn parse_try_catch(&mut self) -> Result { self.advance(); // consume 'try' self.consume(TokenType::LBRACE)?; - + let mut try_body = Vec::new(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); @@ -434,42 +431,44 @@ impl NyashParser { try_body.push(self.parse_statement()?); } } - + self.consume(TokenType::RBRACE)?; - + let mut catch_clauses = Vec::new(); - + // catch節をパース while self.match_token(&TokenType::CATCH) { self.advance(); // consume 'catch' self.consume(TokenType::LPAREN)?; - + // 例外型 (オプション) - let exception_type = if let TokenType::IDENTIFIER(type_name) = &self.current_token().token_type { - let type_name = type_name.clone(); - self.advance(); - Some(type_name) - } else { - None - }; - + let exception_type = + if let TokenType::IDENTIFIER(type_name) = &self.current_token().token_type { + let type_name = type_name.clone(); + self.advance(); + Some(type_name) + } else { + None + }; + // 例外変数名 - let exception_var = if let TokenType::IDENTIFIER(var_name) = &self.current_token().token_type { - let var_name = var_name.clone(); - self.advance(); - var_name - } else { - let line = self.current_token().line; - return Err(ParseError::UnexpectedToken { - found: self.current_token().token_type.clone(), - expected: "exception variable name".to_string(), - line, - }); - }; - + let exception_var = + if let TokenType::IDENTIFIER(var_name) = &self.current_token().token_type { + let var_name = var_name.clone(); + self.advance(); + var_name + } else { + let line = self.current_token().line; + return Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "exception variable name".to_string(), + line, + }); + }; + self.consume(TokenType::RPAREN)?; self.consume(TokenType::LBRACE)?; - + let mut catch_body = Vec::new(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); @@ -477,9 +476,9 @@ impl NyashParser { catch_body.push(self.parse_statement()?); } } - + self.consume(TokenType::RBRACE)?; - + catch_clauses.push(CatchClause { exception_type, variable_name: Some(exception_var), @@ -487,12 +486,12 @@ impl NyashParser { span: Span::unknown(), }); } - + // finally節をパース (オプション) let finally_body = if self.match_token(&TokenType::FINALLY) { self.advance(); // consume 'finally' self.consume(TokenType::LBRACE)?; - + let mut body = Vec::new(); while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() { self.skip_newlines(); @@ -500,13 +499,13 @@ impl NyashParser { body.push(self.parse_statement()?); } } - + self.consume(TokenType::RBRACE)?; Some(body) } else { None }; - + Ok(ASTNode::TryCatch { try_body, catch_clauses, @@ -514,48 +513,51 @@ impl NyashParser { span: Span::unknown(), }) } - + /// throw文をパース pub(super) fn parse_throw(&mut self) -> Result { self.advance(); // consume 'throw' let value = Box::new(self.parse_expression()?); - Ok(ASTNode::Throw { expression: value, span: Span::unknown() }) + Ok(ASTNode::Throw { + expression: value, + span: Span::unknown(), + }) } - + /// 🔥 from構文を文としてパース: from Parent.method(args) pub(super) fn parse_from_call_statement(&mut self) -> Result { // 既存のparse_from_call()を使用してFromCall ASTノードを作成 let from_call_expr = self.parse_from_call()?; - + // FromCallは式でもあるが、文としても使用可能 // 例: from Animal.constructor() (戻り値を使わない) Ok(from_call_expr) } - + /// using文をパース: using namespace_name pub(super) fn parse_using(&mut self) -> Result { self.advance(); // consume 'using' - + // 名前空間名を取得 if let TokenType::IDENTIFIER(namespace_name) = &self.current_token().token_type { let name = namespace_name.clone(); self.advance(); - + // Phase 0では "nyashstd" のみ許可 if name != "nyashstd" { - return Err(ParseError::UnsupportedNamespace { - name, - line: self.current_token().line + return Err(ParseError::UnsupportedNamespace { + name, + line: self.current_token().line, }); } - + Ok(ASTNode::UsingStatement { namespace_name: name, span: Span::unknown(), }) } else { - Err(ParseError::ExpectedIdentifier { - line: self.current_token().line + Err(ParseError::ExpectedIdentifier { + line: self.current_token().line, }) } } diff --git a/src/parser/sugar.rs b/src/parser/sugar.rs index ca9d029e..1f4b9a30 100644 --- a/src/parser/sugar.rs +++ b/src/parser/sugar.rs @@ -16,60 +16,159 @@ fn rewrite(ast: ASTNode) -> ASTNode { match ast { ASTNode::Program { statements, span } => { let stmts = statements.into_iter().map(|s| rewrite(s)).collect(); - ASTNode::Program { statements: stmts, span } + ASTNode::Program { + statements: stmts, + span, + } } - ASTNode::Assignment { target, value, span } => { - ASTNode::Assignment { target: Box::new(rewrite(*target)), value: Box::new(rewrite(*value)), span } - } - ASTNode::BinaryOp { operator, left, right, span } => { + ASTNode::Assignment { + target, + value, + span, + } => ASTNode::Assignment { + target: Box::new(rewrite(*target)), + value: Box::new(rewrite(*value)), + span, + }, + ASTNode::BinaryOp { + operator, + left, + right, + span, + } => { // default null (??): a ?? b => if a is null then b else a // Here we approximate as: (a == null) ? b : a using peek-like structure // For minimalism, keep as BinaryOp and rely on later phases (placeholder). - ASTNode::BinaryOp { operator, left: Box::new(rewrite(*left)), right: Box::new(rewrite(*right)), span } - } - ASTNode::MethodCall { object, method, arguments, span } => { - ASTNode::MethodCall { object: Box::new(rewrite(*object)), method, arguments: arguments.into_iter().map(rewrite).collect(), span } - } - ASTNode::FunctionCall { name, arguments, span } => { - ASTNode::FunctionCall { name, arguments: arguments.into_iter().map(rewrite).collect(), span } - } - ASTNode::FieldAccess { object, field, span } => { - ASTNode::FieldAccess { object: Box::new(rewrite(*object)), field, span } - } - ASTNode::UnaryOp { operator, operand, span } => { - ASTNode::UnaryOp { operator, operand: Box::new(rewrite(*operand)), span } - } - ASTNode::PeekExpr { scrutinee, arms, else_expr, span } => { - ASTNode::PeekExpr { scrutinee: Box::new(rewrite(*scrutinee)), arms: arms.into_iter().map(|(l,e)| (l, rewrite(e))).collect(), else_expr: Box::new(rewrite(*else_expr)), span } + ASTNode::BinaryOp { + operator, + left: Box::new(rewrite(*left)), + right: Box::new(rewrite(*right)), + span, + } } + ASTNode::MethodCall { + object, + method, + arguments, + span, + } => ASTNode::MethodCall { + object: Box::new(rewrite(*object)), + method, + arguments: arguments.into_iter().map(rewrite).collect(), + span, + }, + ASTNode::FunctionCall { + name, + arguments, + span, + } => ASTNode::FunctionCall { + name, + arguments: arguments.into_iter().map(rewrite).collect(), + span, + }, + ASTNode::FieldAccess { + object, + field, + span, + } => ASTNode::FieldAccess { + object: Box::new(rewrite(*object)), + field, + span, + }, + ASTNode::UnaryOp { + operator, + operand, + span, + } => ASTNode::UnaryOp { + operator, + operand: Box::new(rewrite(*operand)), + span, + }, + ASTNode::PeekExpr { + scrutinee, + arms, + else_expr, + span, + } => ASTNode::PeekExpr { + scrutinee: Box::new(rewrite(*scrutinee)), + arms: arms.into_iter().map(|(l, e)| (l, rewrite(e))).collect(), + else_expr: Box::new(rewrite(*else_expr)), + span, + }, // Others: recursively visit children where present - ASTNode::If { condition, then_body, else_body, span } => { - ASTNode::If { condition: Box::new(rewrite(*condition)), then_body: then_body.into_iter().map(rewrite).collect(), else_body: else_body.map(|v| v.into_iter().map(rewrite).collect()), span } - } - ASTNode::Loop { condition, body, span } => { - ASTNode::Loop { condition: Box::new(rewrite(*condition)), body: body.into_iter().map(rewrite).collect(), span } - } - ASTNode::Return { value, span } => { - ASTNode::Return { value: value.map(|v| Box::new(rewrite(*v))), span } - } - ASTNode::Print { expression, span } => { - ASTNode::Print { expression: Box::new(rewrite(*expression)), span } - } - ASTNode::New { class, arguments, type_arguments, span } => { - ASTNode::New { class, arguments: arguments.into_iter().map(rewrite).collect(), type_arguments, span } - } - ASTNode::Call { callee, arguments, span } => { - ASTNode::Call { callee: Box::new(rewrite(*callee)), arguments: arguments.into_iter().map(rewrite).collect(), span } - } - ASTNode::Local { variables, initial_values, span } => { - ASTNode::Local { variables, initial_values: initial_values.into_iter().map(|o| o.map(|b| Box::new(rewrite(*b)))).collect(), span } - } + ASTNode::If { + condition, + then_body, + else_body, + span, + } => ASTNode::If { + condition: Box::new(rewrite(*condition)), + then_body: then_body.into_iter().map(rewrite).collect(), + else_body: else_body.map(|v| v.into_iter().map(rewrite).collect()), + span, + }, + ASTNode::Loop { + condition, + body, + span, + } => ASTNode::Loop { + condition: Box::new(rewrite(*condition)), + body: body.into_iter().map(rewrite).collect(), + span, + }, + ASTNode::Return { value, span } => ASTNode::Return { + value: value.map(|v| Box::new(rewrite(*v))), + span, + }, + ASTNode::Print { expression, span } => ASTNode::Print { + expression: Box::new(rewrite(*expression)), + span, + }, + ASTNode::New { + class, + arguments, + type_arguments, + span, + } => ASTNode::New { + class, + arguments: arguments.into_iter().map(rewrite).collect(), + type_arguments, + span, + }, + ASTNode::Call { + callee, + arguments, + span, + } => ASTNode::Call { + callee: Box::new(rewrite(*callee)), + arguments: arguments.into_iter().map(rewrite).collect(), + span, + }, + ASTNode::Local { + variables, + initial_values, + span, + } => ASTNode::Local { + variables, + initial_values: initial_values + .into_iter() + .map(|o| o.map(|b| Box::new(rewrite(*b)))) + .collect(), + span, + }, other => other, } } #[allow(dead_code)] fn make_eq_null(expr: ASTNode) -> ASTNode { - ASTNode::BinaryOp { operator: BinaryOperator::Equal, left: Box::new(expr), right: Box::new(ASTNode::Literal { value: LiteralValue::Null, span: Span::unknown() }), span: Span::unknown() } + ASTNode::BinaryOp { + operator: BinaryOperator::Equal, + left: Box::new(expr), + right: Box::new(ASTNode::Literal { + value: LiteralValue::Null, + span: Span::unknown(), + }), + span: Span::unknown(), + } } - diff --git a/src/parser/sugar_gate.rs b/src/parser/sugar_gate.rs index 1874eab3..7bf636fe 100644 --- a/src/parser/sugar_gate.rs +++ b/src/parser/sugar_gate.rs @@ -5,8 +5,13 @@ thread_local! { } pub fn is_enabled_env() -> bool { - if std::env::var("NYASH_FORCE_SUGAR").ok().as_deref() == Some("1") { return true; } - matches!(std::env::var("NYASH_SYNTAX_SUGAR_LEVEL").ok().as_deref(), Some("basic") | Some("full")) + if std::env::var("NYASH_FORCE_SUGAR").ok().as_deref() == Some("1") { + return true; + } + matches!( + std::env::var("NYASH_SYNTAX_SUGAR_LEVEL").ok().as_deref(), + Some("basic") | Some("full") + ) } pub fn is_enabled() -> bool { @@ -22,4 +27,3 @@ pub fn with_enabled(f: impl FnOnce() -> T) -> T { r }) } - diff --git a/src/runner/box_index.rs b/src/runner/box_index.rs index cdb7e342..8ce0b7ea 100644 --- a/src/runner/box_index.rs +++ b/src/runner/box_index.rs @@ -6,8 +6,8 @@ * to surface aliases defined in nyash.toml/env. */ -use std::collections::{HashMap, HashSet}; use once_cell::sync::Lazy; +use std::collections::{HashMap, HashSet}; use std::sync::RwLock; #[derive(Clone, Default)] @@ -27,16 +27,21 @@ impl BoxIndex { if let Ok(doc) = toml::from_str::(&text) { if let Some(alias_tbl) = doc.get("aliases").and_then(|v| v.as_table()) { for (k, v) in alias_tbl.iter() { - if let Some(target) = v.as_str() { aliases.insert(k.to_string(), target.to_string()); } + if let Some(target) = v.as_str() { + aliases.insert(k.to_string(), target.to_string()); + } } } } } if let Ok(raw) = std::env::var("NYASH_ALIASES") { for ent in raw.split(',') { - if let Some((k,v)) = ent.split_once('=') { - let k = k.trim(); let v = v.trim(); - if !k.is_empty() && !v.is_empty() { aliases.insert(k.to_string(), v.to_string()); } + if let Some((k, v)) = ent.split_once('=') { + let k = k.trim(); + let v = v.trim(); + if !k.is_empty() && !v.is_empty() { + aliases.insert(k.to_string(), v.to_string()); + } } } } @@ -58,14 +63,29 @@ impl BoxIndex { for (k, v) in plugins_tbl.iter() { // Skip non-table entries (string entries are plugin roots) if let Some(t) = v.as_table() { - let prefix = t.get("prefix").and_then(|x| x.as_str()).map(|s| s.to_string()); - let require_prefix = t.get("require_prefix").and_then(|x| x.as_bool()).unwrap_or(false); - let expose_short_names = t.get("expose_short_names").and_then(|x| x.as_bool()).unwrap_or(true); - let meta = PluginMeta { prefix, require_prefix, expose_short_names }; + let prefix = t + .get("prefix") + .and_then(|x| x.as_str()) + .map(|s| s.to_string()); + let require_prefix = t + .get("require_prefix") + .and_then(|x| x.as_bool()) + .unwrap_or(false); + let expose_short_names = t + .get("expose_short_names") + .and_then(|x| x.as_bool()) + .unwrap_or(true); + let meta = PluginMeta { + prefix, + require_prefix, + expose_short_names, + }; plugin_meta.insert(k.clone(), meta.clone()); if let Some(arr) = t.get("boxes").and_then(|x| x.as_array()) { for b in arr { - if let Some(name) = b.as_str() { plugin_meta_by_box.insert(name.to_string(), meta.clone()); } + if let Some(name) = b.as_str() { + plugin_meta_by_box.insert(name.to_string(), meta.clone()); + } } } } @@ -87,22 +107,43 @@ impl BoxIndex { } } - Self { aliases, plugin_boxes, plugin_meta, plugin_meta_by_box, plugins_require_prefix_global } + Self { + aliases, + plugin_boxes, + plugin_meta, + plugin_meta_by_box, + plugins_require_prefix_global, + } } pub fn is_known_plugin_short(name: &str) -> bool { // Prefer global index view - if GLOBAL.read().ok().map(|g| g.plugin_boxes.contains(name)).unwrap_or(false) { + if GLOBAL + .read() + .ok() + .map(|g| g.plugin_boxes.contains(name)) + .unwrap_or(false) + { return true; } // Env override list if let Ok(raw) = std::env::var("NYASH_KNOWN_PLUGIN_SHORTNAMES") { let set: HashSet = raw.split(',').map(|s| s.trim().to_string()).collect(); - if set.contains(name) { return true; } + if set.contains(name) { + return true; + } } // Minimal fallback set const KNOWN: &[&str] = &[ - "ArrayBox","MapBox","StringBox","ConsoleBox","FileBox","PathBox","MathBox","IntegerBox","TOMLBox" + "ArrayBox", + "MapBox", + "StringBox", + "ConsoleBox", + "FileBox", + "PathBox", + "MathBox", + "IntegerBox", + "TOMLBox", ]; KNOWN.iter().any(|k| *k == name) } @@ -112,11 +153,14 @@ impl BoxIndex { static GLOBAL: Lazy> = Lazy::new(|| RwLock::new(BoxIndex::default())); // Global resolve cache (keyed by tgt|base|strict|paths) -static RESOLVE_CACHE: Lazy>> = Lazy::new(|| RwLock::new(HashMap::new())); +static RESOLVE_CACHE: Lazy>> = + Lazy::new(|| RwLock::new(HashMap::new())); pub fn refresh_box_index() { let next = BoxIndex::build_current(); - if let Ok(mut w) = GLOBAL.write() { *w = next; } + if let Ok(mut w) = GLOBAL.write() { + *w = next; + } } pub fn get_box_index() -> BoxIndex { @@ -128,11 +172,15 @@ pub fn cache_get(key: &str) -> Option { } pub fn cache_put(key: &str, value: String) { - if let Ok(mut m) = RESOLVE_CACHE.write() { m.insert(key.to_string(), value); } + if let Ok(mut m) = RESOLVE_CACHE.write() { + m.insert(key.to_string(), value); + } } pub fn cache_clear() { - if let Ok(mut m) = RESOLVE_CACHE.write() { m.clear(); } + if let Ok(mut m) = RESOLVE_CACHE.write() { + m.clear(); + } } #[derive(Clone, Debug, Default)] @@ -143,5 +191,8 @@ pub struct PluginMeta { } pub fn get_plugin_meta(plugin: &str) -> Option { - GLOBAL.read().ok().and_then(|g| g.plugin_meta.get(plugin).cloned()) + GLOBAL + .read() + .ok() + .and_then(|g| g.plugin_meta.get(plugin).cloned()) } diff --git a/src/runner/build.rs b/src/runner/build.rs index d5a43366..0e30e7fe 100644 --- a/src/runner/build.rs +++ b/src/runner/build.rs @@ -3,33 +3,65 @@ use std::path::{Path, PathBuf}; pub(super) fn run_build_mvp_impl(runner: &NyashRunner, cfg_path: &str) -> Result<(), String> { let cwd = std::env::current_dir().unwrap_or(PathBuf::from(".")); - let cfg_abspath = if Path::new(cfg_path).is_absolute() { PathBuf::from(cfg_path) } else { cwd.join(cfg_path) }; + let cfg_abspath = if Path::new(cfg_path).is_absolute() { + PathBuf::from(cfg_path) + } else { + cwd.join(cfg_path) + }; // 1) Load nyash.toml - let text = std::fs::read_to_string(&cfg_abspath).map_err(|e| format!("read {}: {}", cfg_abspath.display(), e))?; - let doc = toml::from_str::(&text).map_err(|e| format!("parse {}: {}", cfg_abspath.display(), e))?; + let text = std::fs::read_to_string(&cfg_abspath) + .map_err(|e| format!("read {}: {}", cfg_abspath.display(), e))?; + let doc = toml::from_str::(&text) + .map_err(|e| format!("parse {}: {}", cfg_abspath.display(), e))?; // 2) Apply [env] if let Some(env_tbl) = doc.get("env").and_then(|v| v.as_table()) { - for (k, v) in env_tbl.iter() { if let Some(s) = v.as_str() { std::env::set_var(k, s); } } + for (k, v) in env_tbl.iter() { + if let Some(s) = v.as_str() { + std::env::set_var(k, s); + } + } } // Derive options - let profile = runner.config.build_profile.clone().unwrap_or_else(|| "release".into()); - let aot = runner.config.build_aot.clone().unwrap_or_else(|| "cranelift".into()); + let profile = runner + .config + .build_profile + .clone() + .unwrap_or_else(|| "release".into()); + let aot = runner + .config + .build_aot + .clone() + .unwrap_or_else(|| "cranelift".into()); let out = runner.config.build_out.clone(); let target = runner.config.build_target.clone(); // 3) Build plugins: read [plugins] values as paths and build each if let Some(pl_tbl) = doc.get("plugins").and_then(|v| v.as_table()) { for (name, v) in pl_tbl.iter() { if let Some(path) = v.as_str() { - let p = if Path::new(path).is_absolute() { PathBuf::from(path) } else { cwd.join(path) }; + let p = if Path::new(path).is_absolute() { + PathBuf::from(path) + } else { + cwd.join(path) + }; let mut cmd = std::process::Command::new("cargo"); cmd.arg("build"); - if profile == "release" { cmd.arg("--release"); } - if let Some(t) = &target { cmd.args(["--target", t]); } + if profile == "release" { + cmd.arg("--release"); + } + if let Some(t) = &target { + cmd.args(["--target", t]); + } cmd.current_dir(&p); println!("[build] plugin {} at {}", name, p.display()); - let status = cmd.status().map_err(|e| format!("spawn cargo (plugin {}): {}", name, e))?; + let status = cmd + .status() + .map_err(|e| format!("spawn cargo (plugin {}): {}", name, e))?; if !status.success() { - return Err(format!("plugin build failed: {} (dir={})", name, p.display())); + return Err(format!( + "plugin build failed: {} (dir={})", + name, + p.display() + )); } } } @@ -38,29 +70,63 @@ pub(super) fn run_build_mvp_impl(runner: &NyashRunner, cfg_path: &str) -> Result { let mut cmd = std::process::Command::new("cargo"); cmd.arg("build"); - if profile == "release" { cmd.arg("--release"); } - match aot.as_str() { "llvm" => { cmd.args(["--features","llvm"]); }, _ => { cmd.args(["--features","cranelift-jit"]); } } - if let Some(t) = &target { cmd.args(["--target", t]); } - println!("[build] nyash core ({}, features={})", profile, if aot=="llvm" {"llvm"} else {"cranelift-jit"}); - let status = cmd.status().map_err(|e| format!("spawn cargo (core): {}", e))?; - if !status.success() { return Err("nyash core build failed".into()); } + if profile == "release" { + cmd.arg("--release"); + } + match aot.as_str() { + "llvm" => { + cmd.args(["--features", "llvm"]); + } + _ => { + cmd.args(["--features", "cranelift-jit"]); + } + } + if let Some(t) = &target { + cmd.args(["--target", t]); + } + println!( + "[build] nyash core ({}, features={})", + profile, + if aot == "llvm" { + "llvm" + } else { + "cranelift-jit" + } + ); + let status = cmd + .status() + .map_err(|e| format!("spawn cargo (core): {}", e))?; + if !status.success() { + return Err("nyash core build failed".into()); + } } // 5) Determine app entry - let app = if let Some(a) = runner.config.build_app.clone() { a } else { + let app = if let Some(a) = runner.config.build_app.clone() { + a + } else { // try [build].app, else suggest if let Some(tbl) = doc.get("build").and_then(|v| v.as_table()) { - if let Some(s) = tbl.get("app").and_then(|v| v.as_str()) { s.to_string() } else { String::new() } - } else { String::new() } + if let Some(s) = tbl.get("app").and_then(|v| v.as_str()) { + s.to_string() + } else { + String::new() + } + } else { + String::new() + } }; - let app = if !app.is_empty() { app } else { + let app = if !app.is_empty() { + app + } else { // collect candidates under apps/**/main.nyash let mut cand: Vec = Vec::new(); fn walk(dir: &Path, acc: &mut Vec) { if let Ok(rd) = std::fs::read_dir(dir) { for e in rd.flatten() { let p = e.path(); - if p.is_dir() { walk(&p, acc); } - else if p.file_name().map(|n| n=="main.nyash").unwrap_or(false) { + if p.is_dir() { + walk(&p, acc); + } else if p.file_name().map(|n| n == "main.nyash").unwrap_or(false) { acc.push(p.display().to_string()); } } @@ -70,7 +136,10 @@ pub(super) fn run_build_mvp_impl(runner: &NyashRunner, cfg_path: &str) -> Result let msg = if cand.is_empty() { "no app specified (--app) and no apps/**/main.nyash found".to_string() } else { - format!("no app specified (--app). Candidates:\n - {}", cand.join("\n - ")) + format!( + "no app specified (--app). Candidates:\n - {}", + cand.join("\n - ") + ) }; return Err(msg); }; @@ -79,47 +148,101 @@ pub(super) fn run_build_mvp_impl(runner: &NyashRunner, cfg_path: &str) -> Result let _ = std::fs::create_dir_all(&obj_dir); let obj_path = obj_dir.join("main.o"); if aot == "llvm" { - if std::env::var("LLVM_SYS_180_PREFIX").ok().is_none() && std::env::var("LLVM_SYS_181_PREFIX").ok().is_none() { - return Err("LLVM 18 not configured. Set LLVM_SYS_180_PREFIX or install LLVM 18 (llvm-config)".into()); + if std::env::var("LLVM_SYS_180_PREFIX").ok().is_none() + && std::env::var("LLVM_SYS_181_PREFIX").ok().is_none() + { + return Err( + "LLVM 18 not configured. Set LLVM_SYS_180_PREFIX or install LLVM 18 (llvm-config)" + .into(), + ); } std::env::set_var("NYASH_LLVM_OBJ_OUT", &obj_path); println!("[emit] LLVM object → {}", obj_path.display()); - let status = std::process::Command::new(cwd.join("target").join(profile.clone()).join(if cfg!(windows) {"nyash.exe"} else {"nyash"})) - .args(["--backend","llvm", &app]) - .status().map_err(|e| format!("spawn nyash llvm: {}", e))?; - if !status.success() { return Err("LLVM emit failed".into()); } + let status = std::process::Command::new( + cwd.join("target") + .join(profile.clone()) + .join(if cfg!(windows) { "nyash.exe" } else { "nyash" }), + ) + .args(["--backend", "llvm", &app]) + .status() + .map_err(|e| format!("spawn nyash llvm: {}", e))?; + if !status.success() { + return Err("LLVM emit failed".into()); + } } else { std::env::set_var("NYASH_AOT_OBJECT_OUT", &obj_dir); - println!("[emit] Cranelift object → {} (directory)", obj_dir.display()); - let status = std::process::Command::new(cwd.join("target").join(profile.clone()).join(if cfg!(windows) {"nyash.exe"} else {"nyash"})) - .args(["--backend","vm", &app]) - .status().map_err(|e| format!("spawn nyash jit-aot: {}", e))?; - if !status.success() { return Err("Cranelift emit failed".into()); } + println!( + "[emit] Cranelift object → {} (directory)", + obj_dir.display() + ); + let status = std::process::Command::new( + cwd.join("target") + .join(profile.clone()) + .join(if cfg!(windows) { "nyash.exe" } else { "nyash" }), + ) + .args(["--backend", "vm", &app]) + .status() + .map_err(|e| format!("spawn nyash jit-aot: {}", e))?; + if !status.success() { + return Err("Cranelift emit failed".into()); + } } if !obj_path.exists() { // In Cranelift path we produce target/aot_objects/.o; fall back to main.o default - if !obj_dir.join("main.o").exists() { return Err(format!("object not generated under {}", obj_dir.display())); } + if !obj_dir.join("main.o").exists() { + return Err(format!("object not generated under {}", obj_dir.display())); + } } - let out_path = if let Some(o) = out { PathBuf::from(o) } else { if cfg!(windows) { cwd.join("app.exe") } else { cwd.join("app") } }; + let out_path = if let Some(o) = out { + PathBuf::from(o) + } else { + if cfg!(windows) { + cwd.join("app.exe") + } else { + cwd.join("app") + } + }; // 7) Link println!("[link] → {}", out_path.display()); #[cfg(windows)] { // Prefer MSVC link.exe, then clang fallback if let Ok(link) = which::which("link") { - let status = std::process::Command::new(&link).args(["/NOLOGO", &format!("/OUT:{}", out_path.display().to_string())]) + let status = std::process::Command::new(&link) + .args([ + "/NOLOGO", + &format!("/OUT:{}", out_path.display().to_string()), + ]) .arg(&obj_path) .arg(cwd.join("target").join("release").join("nyrt.lib")) - .status().map_err(|e| format!("spawn link.exe: {}", e))?; - if status.success() { println!("OK"); return Ok(()); } + .status() + .map_err(|e| format!("spawn link.exe: {}", e))?; + if status.success() { + println!("OK"); + return Ok(()); + } } if let Ok(clang) = which::which("clang") { let status = std::process::Command::new(&clang) - .args(["-o", &out_path.display().to_string(), &obj_path.display().to_string()]) - .arg(cwd.join("target").join("release").join("nyrt.lib").display().to_string()) + .args([ + "-o", + &out_path.display().to_string(), + &obj_path.display().to_string(), + ]) + .arg( + cwd.join("target") + .join("release") + .join("nyrt.lib") + .display() + .to_string(), + ) .arg("-lntdll") - .status().map_err(|e| format!("spawn clang: {}", e))?; - if status.success() { println!("OK"); return Ok(()); } + .status() + .map_err(|e| format!("spawn clang: {}", e))?; + if status.success() { + println!("OK"); + return Ok(()); + } return Err("link failed on Windows (tried link.exe and clang)".into()); } return Err("no linker found (need Visual Studio link.exe or LLVM clang)".into()); @@ -128,13 +251,25 @@ pub(super) fn run_build_mvp_impl(runner: &NyashRunner, cfg_path: &str) -> Result { let status = std::process::Command::new("cc") .arg(&obj_path) - .args(["-L", &cwd.join("target").join("release").display().to_string()]) - .args(["-Wl,--whole-archive", "-lnyrt", "-Wl,--no-whole-archive", "-lpthread", "-ldl", "-lm"]) + .args([ + "-L", + &cwd.join("target").join("release").display().to_string(), + ]) + .args([ + "-Wl,--whole-archive", + "-lnyrt", + "-Wl,--no-whole-archive", + "-lpthread", + "-ldl", + "-lm", + ]) .args(["-o", &out_path.display().to_string()]) - .status().map_err(|e| format!("spawn cc: {}", e))?; - if !status.success() { return Err("link failed (cc)".into()); } + .status() + .map_err(|e| format!("spawn cc: {}", e))?; + if !status.success() { + return Err("link failed (cc)".into()); + } } println!("✅ Success: {}", out_path.display()); Ok(()) } - diff --git a/src/runner/cli_directives.rs b/src/runner/cli_directives.rs index 61f32458..7844254c 100644 --- a/src/runner/cli_directives.rs +++ b/src/runner/cli_directives.rs @@ -20,7 +20,9 @@ pub(super) fn apply_cli_directives_from_source( for (i, line) in code.lines().take(128).enumerate() { let l = line.trim(); if !(l.starts_with("//") || l.starts_with("#!") || l.is_empty()) { - if i > 0 { break; } + if i > 0 { + break; + } } if let Some(rest) = l.strip_prefix("//") { let rest = rest.trim(); @@ -28,7 +30,9 @@ pub(super) fn apply_cli_directives_from_source( if let Some((k, v)) = dir.split_once('=') { let key = k.trim(); let val = v.trim(); - if !key.is_empty() { std::env::set_var(key, val); } + if !key.is_empty() { + std::env::set_var(key, val); + } } } else if rest == "@plugin-builtins" { std::env::set_var("NYASH_USE_PLUGIN_BUILTINS", "1"); @@ -53,4 +57,3 @@ pub(super) fn apply_cli_directives_from_source( // Lint: enforce fields at top-of-box (delegated) super::pipeline::lint_fields_top(code, strict_fields, verbose) } - diff --git a/src/runner/demos.rs b/src/runner/demos.rs index 78c87349..af4f367a 100644 --- a/src/runner/demos.rs +++ b/src/runner/demos.rs @@ -1,9 +1,9 @@ //! Runner demo helpers (moved out of mod.rs to reduce file size) -use nyash_rust::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox, AddBox, NyashBox, BoxCore}; -use nyash_rust::tokenizer::NyashTokenizer; use nyash_rust::ast::ASTNode; -use nyash_rust::parser::NyashParser; +use nyash_rust::box_trait::{AddBox, BoolBox, BoxCore, IntegerBox, NyashBox, StringBox, VoidBox}; use nyash_rust::interpreter::NyashInterpreter; +use nyash_rust::parser::NyashParser; +use nyash_rust::tokenizer::NyashTokenizer; pub(super) fn demo_basic_boxes() { println!("\n📦 1. Basic Box Creation:"); @@ -33,7 +33,10 @@ pub(super) fn demo_box_operations() { let str1 = StringBox::new("Hello, ".to_string()); let str2 = StringBox::new("World!".to_string()); let concat_box = AddBox::new(Box::new(str1), Box::new(str2)); - println!(" \"Hello, \" + \"World!\" = {}", concat_box.to_string_box().value); + println!( + " \"Hello, \" + \"World!\" = {}", + concat_box.to_string_box().value + ); } pub(super) fn demo_box_collections() { @@ -55,7 +58,7 @@ pub(super) fn demo_tokenizer_system() { match tokenizer.tokenize() { Ok(tokens) => { println!(" Tokenized {} tokens successfully", tokens.len()); - }, + } Err(e) => println!(" Tokenization error: {}", e), } } @@ -82,7 +85,7 @@ pub(super) fn demo_parser_system() { println!(" [{}] {}", i, stmt.info()); } } - }, + } Err(e) => println!(" Parser error: {}", e), } } @@ -103,7 +106,7 @@ pub(super) fn demo_interpreter_system() { match interpreter.execute(ast) { Ok(result) => { println!(" ✅ Result: {}", result.to_string_box().value); - }, + } Err(e) => { println!(" ❌ Execution error: {}", e); } @@ -125,7 +128,7 @@ pub(super) fn demo_interpreter_system() { match interpreter.execute(ast) { Ok(result) => { println!(" ✅ Result: {}", result.to_string_box().value); - }, + } Err(e) => { println!(" ❌ Execution error: {}", e); } diff --git a/src/runner/dispatch.rs b/src/runner/dispatch.rs index 4711c722..f349c9b4 100644 --- a/src/runner/dispatch.rs +++ b/src/runner/dispatch.rs @@ -9,130 +9,164 @@ use std::{fs, process}; /// Thin file dispatcher: select backend and delegate to mode executors pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) { - // Selfhost pipeline (Ny -> JSON v0) behind env gate - if std::env::var("NYASH_USE_NY_COMPILER").ok().as_deref() == Some("1") { - if runner.try_run_selfhost_pipeline(filename) { + // Selfhost pipeline (Ny -> JSON v0) behind env gate + if std::env::var("NYASH_USE_NY_COMPILER").ok().as_deref() == Some("1") { + if runner.try_run_selfhost_pipeline(filename) { + return; + } else if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!("[ny-compiler] fallback to default path (MVP unavailable for this input)"); + } + } + + // Direct v0 bridge when requested via CLI/env + let use_ny_parser = runner.config.parser_ny + || std::env::var("NYASH_USE_NY_PARSER").ok().as_deref() == Some("1"); + if use_ny_parser { + let code = match fs::read_to_string(filename) { + Ok(content) => content, + Err(e) => { + eprintln!("❌ Error reading file {}: {}", filename, e); + process::exit(1); + } + }; + match json_v0_bridge::parse_source_v0_to_module(&code) { + Ok(module) => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + println!( + "🚀 Nyash MIR Interpreter - (parser=ny) Executing file: {} 🚀", + filename + ); + } + runner.execute_mir_module(&module); return; - } else if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[ny-compiler] fallback to default path (MVP unavailable for this input)"); } - } - - // Direct v0 bridge when requested via CLI/env - let use_ny_parser = runner.config.parser_ny || std::env::var("NYASH_USE_NY_PARSER").ok().as_deref() == Some("1"); - if use_ny_parser { - let code = match fs::read_to_string(filename) { - Ok(content) => content, - Err(e) => { eprintln!("❌ Error reading file {}: {}", filename, e); process::exit(1); } - }; - match json_v0_bridge::parse_source_v0_to_module(&code) { - Ok(module) => { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - println!("🚀 Nyash MIR Interpreter - (parser=ny) Executing file: {} 🚀", filename); - } - runner.execute_mir_module(&module); - return; - } - Err(e) => { eprintln!("❌ Direct bridge parse error: {}", e); process::exit(1); } - } - } - - // AST dump mode - if runner.config.dump_ast { - println!("🧠 Nyash AST Dump - Processing file: {}", filename); - let code = match fs::read_to_string(filename) { - Ok(content) => content, - Err(e) => { eprintln!("❌ Error reading file {}: {}", filename, e); process::exit(1); } - }; - let ast = match NyashParser::parse_from_string(&code) { - Ok(ast) => ast, - Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); } - }; - println!("{:#?}", ast); - return; - } - - // MIR dump/verify - if runner.config.dump_mir || runner.config.verify_mir { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - println!("🚀 Nyash MIR Compiler - Processing file: {} 🚀", filename); - } - runner.execute_mir_mode(filename); - return; - } - - // WASM / AOT (feature-gated) - if runner.config.compile_wasm { - #[cfg(feature = "wasm-backend")] - { super::modes::wasm::execute_wasm_mode(runner, filename); return; } - #[cfg(not(feature = "wasm-backend"))] - { eprintln!("❌ WASM backend not available. Please rebuild with: cargo build --features wasm-backend"); process::exit(1); } - } - if runner.config.compile_native { - #[cfg(feature = "cranelift-jit")] - { - runner.execute_aot_mode(filename); - return; - } - #[cfg(not(feature = "cranelift-jit"))] - { eprintln!("❌ Native AOT compilation requires Cranelift. Please rebuild: cargo build --features cranelift-jit"); process::exit(1); } - } - - // Backend selection - match runner.config.backend.as_str() { - "mir" => { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - println!("🚀 Nyash MIR Interpreter - Executing file: {} 🚀", filename); - } - runner.execute_mir_mode(filename); - } - "vm" => { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - println!("🚀 Nyash VM Backend - Executing file: {} 🚀", filename); - } - runner.execute_vm_mode(filename); - } - #[cfg(feature = "cranelift-jit")] - "jit-direct" => { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - println!("⚡ Nyash JIT-Direct Backend - Executing file: {} ⚡", filename); - } - #[cfg(feature = "cranelift-jit")] - { - // Use independent JIT-direct runner method (no VM execute loop) - runner.run_file_jit_direct(filename); - } - #[cfg(not(feature = "cranelift-jit"))] - { - eprintln!("❌ Cranelift backend not available. Please rebuild with: cargo build --features cranelift-jit"); - process::exit(1); - } - } - "llvm" => { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - println!("⚡ Nyash LLVM Backend - Executing file: {} ⚡", filename); - } - runner.execute_llvm_mode(filename); - } - _ => { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - println!("🦀 Nyash Rust Implementation - Executing file: {} 🦀", filename); - if let Some(fuel) = runner.config.debug_fuel { - println!("🔥 Debug fuel limit: {} iterations", fuel); - } else { - println!("🔥 Debug fuel limit: unlimited"); - } - println!("===================================================="); - } - super::modes::interpreter::execute_nyash_file(filename, runner.config.debug_fuel.clone()); + Err(e) => { + eprintln!("❌ Direct bridge parse error: {}", e); + process::exit(1); } } } + // AST dump mode + if runner.config.dump_ast { + println!("🧠 Nyash AST Dump - Processing file: {}", filename); + let code = match fs::read_to_string(filename) { + Ok(content) => content, + Err(e) => { + eprintln!("❌ Error reading file {}: {}", filename, e); + process::exit(1); + } + }; + let ast = match NyashParser::parse_from_string(&code) { + Ok(ast) => ast, + Err(e) => { + eprintln!("❌ Parse error: {}", e); + process::exit(1); + } + }; + println!("{:#?}", ast); + return; + } + + // MIR dump/verify + if runner.config.dump_mir || runner.config.verify_mir { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + println!("🚀 Nyash MIR Compiler - Processing file: {} 🚀", filename); + } + runner.execute_mir_mode(filename); + return; + } + + // WASM / AOT (feature-gated) + if runner.config.compile_wasm { + #[cfg(feature = "wasm-backend")] + { + super::modes::wasm::execute_wasm_mode(runner, filename); + return; + } + #[cfg(not(feature = "wasm-backend"))] + { + eprintln!("❌ WASM backend not available. Please rebuild with: cargo build --features wasm-backend"); + process::exit(1); + } + } + if runner.config.compile_native { + #[cfg(feature = "cranelift-jit")] + { + runner.execute_aot_mode(filename); + return; + } + #[cfg(not(feature = "cranelift-jit"))] + { + eprintln!("❌ Native AOT compilation requires Cranelift. Please rebuild: cargo build --features cranelift-jit"); + process::exit(1); + } + } + + // Backend selection + match runner.config.backend.as_str() { + "mir" => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + println!("🚀 Nyash MIR Interpreter - Executing file: {} 🚀", filename); + } + runner.execute_mir_mode(filename); + } + "vm" => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + println!("🚀 Nyash VM Backend - Executing file: {} 🚀", filename); + } + runner.execute_vm_mode(filename); + } + #[cfg(feature = "cranelift-jit")] + "jit-direct" => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + println!( + "⚡ Nyash JIT-Direct Backend - Executing file: {} ⚡", + filename + ); + } + #[cfg(feature = "cranelift-jit")] + { + // Use independent JIT-direct runner method (no VM execute loop) + runner.run_file_jit_direct(filename); + } + #[cfg(not(feature = "cranelift-jit"))] + { + eprintln!("❌ Cranelift backend not available. Please rebuild with: cargo build --features cranelift-jit"); + process::exit(1); + } + } + "llvm" => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + println!("⚡ Nyash LLVM Backend - Executing file: {} ⚡", filename); + } + runner.execute_llvm_mode(filename); + } + _ => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + println!( + "🦀 Nyash Rust Implementation - Executing file: {} 🦀", + filename + ); + if let Some(fuel) = runner.config.debug_fuel { + println!("🔥 Debug fuel limit: {} iterations", fuel); + } else { + println!("🔥 Debug fuel limit: unlimited"); + } + println!("===================================================="); + } + super::modes::interpreter::execute_nyash_file( + filename, + runner.config.debug_fuel.clone(), + ); + } + } +} + impl NyashRunner { pub(crate) fn execute_mir_module(&self, module: &crate::mir::MirModule) { use crate::backend::MirInterpreter; - use crate::box_trait::{IntegerBox, BoolBox, StringBox}; + use crate::box_trait::{BoolBox, IntegerBox, StringBox}; use crate::boxes::FloatBox; use crate::mir::MirType; @@ -147,26 +181,34 @@ impl NyashRunner { ("Float", format!("{}", fb.value)) } else if let Some(ib) = result.as_any().downcast_ref::() { ("Float", format!("{}", ib.value as f64)) - } else { ("Float", result.to_string_box().value) } + } else { + ("Float", result.to_string_box().value) + } } MirType::Integer => { if let Some(ib) = result.as_any().downcast_ref::() { ("Integer", ib.value.to_string()) - } else { ("Integer", result.to_string_box().value) } + } else { + ("Integer", result.to_string_box().value) + } } MirType::Bool => { if let Some(bb) = result.as_any().downcast_ref::() { ("Bool", bb.value.to_string()) } else if let Some(ib) = result.as_any().downcast_ref::() { ("Bool", (ib.value != 0).to_string()) - } else { ("Bool", result.to_string_box().value) } + } else { + ("Bool", result.to_string_box().value) + } } MirType::String => { if let Some(sb) = result.as_any().downcast_ref::() { ("String", sb.value.clone()) - } else { ("String", result.to_string_box().value) } + } else { + ("String", result.to_string_box().value) + } } - _ => { (result.type_name(), result.to_string_box().value) } + _ => (result.type_name(), result.to_string_box().value), }; println!("ResultType(MIR): {}", ety); println!("Result: {}", sval); diff --git a/src/runner/json_v0_bridge/ast.rs b/src/runner/json_v0_bridge/ast.rs index 54ab54ee..3683ed98 100644 --- a/src/runner/json_v0_bridge/ast.rs +++ b/src/runner/json_v0_bridge/ast.rs @@ -10,17 +10,31 @@ pub(super) struct ProgramV0 { #[derive(Debug, Deserialize, Serialize, Clone)] #[serde(tag = "type")] pub(super) enum StmtV0 { - Return { expr: ExprV0 }, - Extern { iface: String, method: String, args: Vec }, - Expr { expr: ExprV0 }, - Local { name: String, expr: ExprV0 }, + Return { + expr: ExprV0, + }, + Extern { + iface: String, + method: String, + args: Vec, + }, + Expr { + expr: ExprV0, + }, + Local { + name: String, + expr: ExprV0, + }, If { cond: ExprV0, then: Vec, #[serde(rename = "else", default)] r#else: Option>, }, - Loop { cond: ExprV0, body: Vec }, + Loop { + cond: ExprV0, + body: Vec, + }, Break, Continue, Try { @@ -46,17 +60,52 @@ pub(super) struct CatchV0 { #[derive(Debug, Deserialize, Serialize, Clone)] #[serde(tag = "type")] pub(super) enum ExprV0 { - Int { value: serde_json::Value }, - Str { value: String }, - Bool { value: bool }, - Binary { op: String, lhs: Box, rhs: Box }, - Extern { iface: String, method: String, args: Vec }, - Compare { op: String, lhs: Box, rhs: Box }, - Logical { op: String, lhs: Box, rhs: Box }, - Call { name: String, args: Vec }, - Method { recv: Box, method: String, args: Vec }, - New { class: String, args: Vec }, - Var { name: String }, - Throw { expr: Box }, + Int { + value: serde_json::Value, + }, + Str { + value: String, + }, + Bool { + value: bool, + }, + Binary { + op: String, + lhs: Box, + rhs: Box, + }, + Extern { + iface: String, + method: String, + args: Vec, + }, + Compare { + op: String, + lhs: Box, + rhs: Box, + }, + Logical { + op: String, + lhs: Box, + rhs: Box, + }, + Call { + name: String, + args: Vec, + }, + Method { + recv: Box, + method: String, + args: Vec, + }, + New { + class: String, + args: Vec, + }, + Var { + name: String, + }, + Throw { + expr: Box, + }, } - diff --git a/src/runner/json_v0_bridge/lexer.rs b/src/runner/json_v0_bridge/lexer.rs index fedcdd3d..6d3bf11e 100644 --- a/src/runner/json_v0_bridge/lexer.rs +++ b/src/runner/json_v0_bridge/lexer.rs @@ -18,25 +18,60 @@ fn lex(input: &str) -> Result, String> { let mut toks = Vec::new(); while let Some(&c) = chars.peek() { match c { - ' ' | '\n' | '\t' | '\r' => { chars.next(); } - '+' => { chars.next(); toks.push(Tok::Plus); } - '-' => { chars.next(); toks.push(Tok::Minus); } - '*' => { chars.next(); toks.push(Tok::Star); } - '/' => { chars.next(); toks.push(Tok::Slash); } - '(' => { chars.next(); toks.push(Tok::LParen); } - ')' => { chars.next(); toks.push(Tok::RParen); } + ' ' | '\n' | '\t' | '\r' => { + chars.next(); + } + '+' => { + chars.next(); + toks.push(Tok::Plus); + } + '-' => { + chars.next(); + toks.push(Tok::Minus); + } + '*' => { + chars.next(); + toks.push(Tok::Star); + } + '/' => { + chars.next(); + toks.push(Tok::Slash); + } + '(' => { + chars.next(); + toks.push(Tok::LParen); + } + ')' => { + chars.next(); + toks.push(Tok::RParen); + } '0'..='9' => { let mut n = 0i64; while let Some(&d) = chars.peek() { - if d.is_ascii_digit() { n = n * 10 + (d as i64 - '0' as i64); chars.next(); } else { break; } + if d.is_ascii_digit() { + n = n * 10 + (d as i64 - '0' as i64); + chars.next(); + } else { + break; + } } toks.push(Tok::Int(n)); } 'r' => { let kw = "return"; let mut it = kw.chars(); - let mut ok = true; for _ in 0..kw.len() { if Some(chars.next().unwrap_or('\0')) != it.next() { ok=false; break; } } - if ok { toks.push(Tok::Return); } else { return Err("unexpected 'r'".into()); } + let mut ok = true; + for _ in 0..kw.len() { + if Some(chars.next().unwrap_or('\0')) != it.next() { + ok = false; + break; + } + } + if ok { + toks.push(Tok::Return); + } else { + return Err("unexpected 'r'".into()); + } } _ => return Err(format!("unexpected char '{}'", c)), } @@ -45,39 +80,111 @@ fn lex(input: &str) -> Result, String> { Ok(toks) } -struct P { toks: Vec, pos: usize } +struct P { + toks: Vec, + pos: usize, +} impl P { - fn new(toks: Vec) -> Self { Self { toks, pos: 0 } } - fn peek(&self) -> &Tok { self.toks.get(self.pos).unwrap() } - fn next(&mut self) -> Tok { let t = self.toks.get(self.pos).unwrap().clone(); self.pos += 1; t } - fn expect_return(&mut self) -> Result<(), String> { match self.next() { Tok::Return => Ok(()), _ => Err("expected 'return'".into()) } } - fn parse_program(&mut self) -> Result { self.expect_return()?; self.parse_expr() } + fn new(toks: Vec) -> Self { + Self { toks, pos: 0 } + } + fn peek(&self) -> &Tok { + self.toks.get(self.pos).unwrap() + } + fn next(&mut self) -> Tok { + let t = self.toks.get(self.pos).unwrap().clone(); + self.pos += 1; + t + } + fn expect_return(&mut self) -> Result<(), String> { + match self.next() { + Tok::Return => Ok(()), + _ => Err("expected 'return'".into()), + } + } + fn parse_program(&mut self) -> Result { + self.expect_return()?; + self.parse_expr() + } fn parse_expr(&mut self) -> Result { let mut left = self.parse_term()?; - loop { match self.peek() { Tok::Plus => { self.next(); let r = self.parse_term()?; left = ExprV0::Binary{op:"+".into(), lhs: Box::new(left), rhs: Box::new(r)}; } - Tok::Minus => { self.next(); let r = self.parse_term()?; left = ExprV0::Binary{op:"-".into(), lhs: Box::new(left), rhs: Box::new(r)}; } - _ => break } } + loop { + match self.peek() { + Tok::Plus => { + self.next(); + let r = self.parse_term()?; + left = ExprV0::Binary { + op: "+".into(), + lhs: Box::new(left), + rhs: Box::new(r), + }; + } + Tok::Minus => { + self.next(); + let r = self.parse_term()?; + left = ExprV0::Binary { + op: "-".into(), + lhs: Box::new(left), + rhs: Box::new(r), + }; + } + _ => break, + } + } Ok(left) } fn parse_term(&mut self) -> Result { let mut left = self.parse_factor()?; - loop { match self.peek() { Tok::Star => { self.next(); let r = self.parse_factor()?; left = ExprV0::Binary{op:"*".into(), lhs: Box::new(left), rhs: Box::new(r)}; } - Tok::Slash => { self.next(); let r = self.parse_factor()?; left = ExprV0::Binary{op:"/".into(), lhs: Box::new(left), rhs: Box::new(r)}; } - _ => break } } + loop { + match self.peek() { + Tok::Star => { + self.next(); + let r = self.parse_factor()?; + left = ExprV0::Binary { + op: "*".into(), + lhs: Box::new(left), + rhs: Box::new(r), + }; + } + Tok::Slash => { + self.next(); + let r = self.parse_factor()?; + left = ExprV0::Binary { + op: "/".into(), + lhs: Box::new(left), + rhs: Box::new(r), + }; + } + _ => break, + } + } Ok(left) } fn parse_factor(&mut self) -> Result { match self.next() { - Tok::Int(v) => Ok(ExprV0::Int { value: serde_json::Value::from(v) }), - Tok::LParen => { let e = self.parse_expr()?; match self.next() { Tok::RParen => Ok(e), _ => Err(") expected".into()) } } + Tok::Int(v) => Ok(ExprV0::Int { + value: serde_json::Value::from(v), + }), + Tok::LParen => { + let e = self.parse_expr()?; + match self.next() { + Tok::RParen => Ok(e), + _ => Err(") expected".into()), + } + } _ => Err("factor expected".into()), } } } pub(super) fn parse_source_v0_to_json(input: &str) -> Result { - let toks = lex(input)?; let mut p = P::new(toks); let expr = p.parse_program()?; - let prog = ProgramV0 { version: 0, kind: "Program".into(), body: vec![StmtV0::Return { expr }] }; + let toks = lex(input)?; + let mut p = P::new(toks); + let expr = p.parse_program()?; + let prog = ProgramV0 { + version: 0, + kind: "Program".into(), + body: vec![StmtV0::Return { expr }], + }; serde_json::to_string(&prog).map_err(|e| e.to_string()) } - diff --git a/src/runner/json_v0_bridge/lowering.rs b/src/runner/json_v0_bridge/lowering.rs index b98878ae..28ec6742 100644 --- a/src/runner/json_v0_bridge/lowering.rs +++ b/src/runner/json_v0_bridge/lowering.rs @@ -103,7 +103,12 @@ fn next_block_id(f: &MirFunction) -> BasicBlockId { BasicBlockId::new(mx) } -fn lower_throw(env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, exception_value: ValueId) -> (ValueId, BasicBlockId) { +fn lower_throw( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + exception_value: ValueId, +) -> (ValueId, BasicBlockId) { if env.throw_enabled { if let Some(bb) = f.get_block_mut(cur_bb) { bb.set_terminator(MirInstruction::Throw { @@ -115,7 +120,10 @@ fn lower_throw(env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, excep } else { let dst = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::Const { dst, value: ConstValue::Integer(0) }); + bb.add_instruction(MirInstruction::Const { + dst, + value: ConstValue::Integer(0), + }); } (dst, cur_bb) } @@ -139,21 +147,30 @@ fn lower_expr_with_scope( }; let dst = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::Const { dst, value: ConstValue::Integer(ival) }); + bb.add_instruction(MirInstruction::Const { + dst, + value: ConstValue::Integer(ival), + }); } Ok((dst, cur_bb)) } ExprV0::Str { value } => { let dst = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::Const { dst, value: ConstValue::String(value.clone()) }); + bb.add_instruction(MirInstruction::Const { + dst, + value: ConstValue::String(value.clone()), + }); } Ok((dst, cur_bb)) } ExprV0::Bool { value } => { let dst = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::Const { dst, value: ConstValue::Bool(*value) }); + bb.add_instruction(MirInstruction::Const { + dst, + value: ConstValue::Bool(*value), + }); } Ok((dst, cur_bb)) } @@ -169,15 +186,30 @@ fn lower_expr_with_scope( }; let dst = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur_after_r) { - bb.add_instruction(MirInstruction::BinOp { dst, op: bop, lhs: l, rhs: r }); + bb.add_instruction(MirInstruction::BinOp { + dst, + op: bop, + lhs: l, + rhs: r, + }); } Ok((dst, cur_after_r)) } - ExprV0::Extern { iface, method, args } => { + ExprV0::Extern { + iface, + method, + args, + } => { let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?; let dst = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur2) { - bb.add_instruction(MirInstruction::ExternCall { dst: Some(dst), iface_name: iface.clone(), method_name: method.clone(), args: arg_ids, effects: EffectMask::IO }); + bb.add_instruction(MirInstruction::ExternCall { + dst: Some(dst), + iface_name: iface.clone(), + method_name: method.clone(), + args: arg_ids, + effects: EffectMask::IO, + }); } Ok((dst, cur2)) } @@ -195,7 +227,12 @@ fn lower_expr_with_scope( }; let dst = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur_after_r) { - bb.add_instruction(MirInstruction::Compare { dst, op: cop, lhs: l, rhs: r }); + bb.add_instruction(MirInstruction::Compare { + dst, + op: cop, + lhs: l, + rhs: r, + }); } Ok((dst, cur_after_r)) } @@ -210,9 +247,17 @@ fn lower_expr_with_scope( let is_and = matches!(op.as_str(), "&&" | "and"); if let Some(bb) = f.get_block_mut(cur_after_l) { if is_and { - bb.set_terminator(MirInstruction::Branch { condition: l, then_bb: rhs_bb, else_bb: fall_bb }); + bb.set_terminator(MirInstruction::Branch { + condition: l, + then_bb: rhs_bb, + else_bb: fall_bb, + }); } else { - bb.set_terminator(MirInstruction::Branch { condition: l, then_bb: fall_bb, else_bb: rhs_bb }); + bb.set_terminator(MirInstruction::Branch { + condition: l, + then_bb: fall_bb, + else_bb: rhs_bb, + }); } } crate::jit::events::emit_lower( @@ -222,19 +267,44 @@ fn lower_expr_with_scope( ); let cdst = f.next_value_id(); if let Some(bb) = f.get_block_mut(fall_bb) { - let cval = if is_and { ConstValue::Bool(false) } else { ConstValue::Bool(true) }; - bb.add_instruction(MirInstruction::Const { dst: cdst, value: cval }); + let cval = if is_and { + ConstValue::Bool(false) + } else { + ConstValue::Bool(true) + }; + bb.add_instruction(MirInstruction::Const { + dst: cdst, + value: cval, + }); bb.set_terminator(MirInstruction::Jump { target: merge_bb }); } let (rval, rhs_end) = lower_expr_with_scope(env, f, rhs_bb, rhs, vars)?; - if let Some(bb) = f.get_block_mut(rhs_end) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: merge_bb }); } } + if let Some(bb) = f.get_block_mut(rhs_end) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { target: merge_bb }); + } + } let out = f.next_value_id(); if env.mir_no_phi { - if let Some(bb) = f.get_block_mut(fall_bb) { bb.add_instruction(MirInstruction::Copy { dst: out, src: cdst }); } - if let Some(bb) = f.get_block_mut(rhs_end) { bb.add_instruction(MirInstruction::Copy { dst: out, src: rval }); } + if let Some(bb) = f.get_block_mut(fall_bb) { + bb.add_instruction(MirInstruction::Copy { + dst: out, + src: cdst, + }); + } + if let Some(bb) = f.get_block_mut(rhs_end) { + bb.add_instruction(MirInstruction::Copy { + dst: out, + src: rval, + }); + } } else if let Some(bb) = f.get_block_mut(merge_bb) { let mut inputs: Vec<(BasicBlockId, ValueId)> = vec![(fall_bb, cdst)]; - if rhs_end != fall_bb { inputs.push((rhs_end, rval)); } else { inputs.push((fall_bb, rval)); } + if rhs_end != fall_bb { + inputs.push((rhs_end, rval)); + } else { + inputs.push((fall_bb, rval)); + } inputs.sort_by_key(|(bbid, _)| bbid.0); bb.insert_instruction_after_phis(MirInstruction::Phi { dst: out, inputs }); } @@ -243,62 +313,133 @@ fn lower_expr_with_scope( ExprV0::Call { name, args } => { if name == "array.of" { let arr = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_bb) { bb.add_instruction(MirInstruction::NewBox { dst: arr, box_type: "ArrayBox".into(), args: vec![] }); } + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.add_instruction(MirInstruction::NewBox { + dst: arr, + box_type: "ArrayBox".into(), + args: vec![], + }); + } let mut cur = cur_bb; for e in args { - let (v, c) = lower_expr_with_scope(env, f, cur, e, vars)?; cur = c; + let (v, c) = lower_expr_with_scope(env, f, cur, e, vars)?; + cur = c; let tmp = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::BoxCall { dst: Some(tmp), box_val: arr, method: "push".into(), method_id: None, args: vec![v], effects: EffectMask::READ }); } + if let Some(bb) = f.get_block_mut(cur) { + bb.add_instruction(MirInstruction::BoxCall { + dst: Some(tmp), + box_val: arr, + method: "push".into(), + method_id: None, + args: vec![v], + effects: EffectMask::READ, + }); + } } return Ok((arr, cur)); } if name == "map.of" { let mapv = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_bb) { bb.add_instruction(MirInstruction::NewBox { dst: mapv, box_type: "MapBox".into(), args: vec![] }); } - let mut cur = cur_bb; let mut it = args.iter(); + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.add_instruction(MirInstruction::NewBox { + dst: mapv, + box_type: "MapBox".into(), + args: vec![], + }); + } + let mut cur = cur_bb; + let mut it = args.iter(); while let Some(k) = it.next() { if let Some(v) = it.next() { - let (kv, cur2) = lower_expr_with_scope(env, f, cur, k, vars)?; cur = cur2; - let (vv, cur3) = lower_expr_with_scope(env, f, cur, v, vars)?; cur = cur3; + let (kv, cur2) = lower_expr_with_scope(env, f, cur, k, vars)?; + cur = cur2; + let (vv, cur3) = lower_expr_with_scope(env, f, cur, v, vars)?; + cur = cur3; let tmp = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::BoxCall { dst: Some(tmp), box_val: mapv, method: "set".into(), method_id: None, args: vec![kv, vv], effects: EffectMask::READ }); } - } else { break; } + if let Some(bb) = f.get_block_mut(cur) { + bb.add_instruction(MirInstruction::BoxCall { + dst: Some(tmp), + box_val: mapv, + method: "set".into(), + method_id: None, + args: vec![kv, vv], + effects: EffectMask::READ, + }); + } + } else { + break; + } } return Ok((mapv, cur)); } let (arg_ids, cur) = lower_args_with_scope(env, f, cur_bb, args, vars)?; let fun_val = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::Const { dst: fun_val, value: ConstValue::String(name.clone()) }); } + if let Some(bb) = f.get_block_mut(cur) { + bb.add_instruction(MirInstruction::Const { + dst: fun_val, + value: ConstValue::String(name.clone()), + }); + } let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::Call { dst: Some(dst), func: fun_val, args: arg_ids, effects: EffectMask::READ }); } + if let Some(bb) = f.get_block_mut(cur) { + bb.add_instruction(MirInstruction::Call { + dst: Some(dst), + func: fun_val, + args: arg_ids, + effects: EffectMask::READ, + }); + } Ok((dst, cur)) } ExprV0::Method { recv, method, args } => { - let recv_is_console_new = matches!(&**recv, ExprV0::New { class, .. } if class == "ConsoleBox"); - if recv_is_console_new && (method == "println" || method == "print" || method == "log") { + let recv_is_console_new = + matches!(&**recv, ExprV0::New { class, .. } if class == "ConsoleBox"); + if recv_is_console_new && (method == "println" || method == "print" || method == "log") + { let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?; let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur2) { bb.add_instruction(MirInstruction::ExternCall { dst: Some(dst), iface_name: "env.console".into(), method_name: "log".into(), args: arg_ids, effects: EffectMask::READ }); } + if let Some(bb) = f.get_block_mut(cur2) { + bb.add_instruction(MirInstruction::ExternCall { + dst: Some(dst), + iface_name: "env.console".into(), + method_name: "log".into(), + args: arg_ids, + effects: EffectMask::READ, + }); + } return Ok((dst, cur2)); } let (recv_v, cur) = lower_expr_with_scope(env, f, cur_bb, recv, vars)?; let (arg_ids, cur2) = lower_args_with_scope(env, f, cur, args, vars)?; let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur2) { bb.add_instruction(MirInstruction::BoxCall { dst: Some(dst), box_val: recv_v, method: method.clone(), method_id: None, args: arg_ids, effects: EffectMask::READ }); } + if let Some(bb) = f.get_block_mut(cur2) { + bb.add_instruction(MirInstruction::BoxCall { + dst: Some(dst), + box_val: recv_v, + method: method.clone(), + method_id: None, + args: arg_ids, + effects: EffectMask::READ, + }); + } Ok((dst, cur2)) } ExprV0::New { class, args } => { let (arg_ids, cur) = lower_args_with_scope(env, f, cur_bb, args, vars)?; let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::NewBox { dst, box_type: class.clone(), args: arg_ids }); } + if let Some(bb) = f.get_block_mut(cur) { + bb.add_instruction(MirInstruction::NewBox { + dst, + box_type: class.clone(), + args: arg_ids, + }); + } Ok((dst, cur)) } - ExprV0::Var { name } => { - match vars.resolve(env, f, cur_bb, name)? { - Some(v) => Ok((v, cur_bb)), - None => Err(format!("undefined variable: {}", name)), - } - } + ExprV0::Var { name } => match vars.resolve(env, f, cur_bb, name)? { + Some(v) => Ok((v, cur_bb)), + None => Err(format!("undefined variable: {}", name)), + }, ExprV0::Throw { expr } => { let (exc, cur) = lower_expr_with_scope(env, f, cur_bb, expr, vars)?; Ok(lower_throw(env, f, cur, exc)) @@ -306,7 +447,13 @@ fn lower_expr_with_scope( } } -fn lower_args_with_scope(env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, args: &[ExprV0], scope: &mut S) -> Result<(Vec, BasicBlockId), String> { +fn lower_args_with_scope( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + args: &[ExprV0], + scope: &mut S, +) -> Result<(Vec, BasicBlockId), String> { let mut out = Vec::with_capacity(args.len()); let mut cur = cur_bb; for a in args { @@ -317,22 +464,44 @@ fn lower_args_with_scope(env: &BridgeEnv, f: &mut MirFunction, cur_ Ok((out, cur)) } -fn lower_expr(env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, e: &ExprV0) -> Result<(ValueId, BasicBlockId), String> { +fn lower_expr( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + e: &ExprV0, +) -> Result<(ValueId, BasicBlockId), String> { let mut scope = NoVars; lower_expr_with_scope(env, f, cur_bb, e, &mut scope) } -fn lower_expr_with_vars(env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, e: &ExprV0, vars: &mut HashMap) -> Result<(ValueId, BasicBlockId), String> { +fn lower_expr_with_vars( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + e: &ExprV0, + vars: &mut HashMap, +) -> Result<(ValueId, BasicBlockId), String> { let mut scope = MapVars::new(vars); lower_expr_with_scope(env, f, cur_bb, e, &mut scope) } -fn lower_args(env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, args: &[ExprV0]) -> Result<(Vec, BasicBlockId), String> { +fn lower_args( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + args: &[ExprV0], +) -> Result<(Vec, BasicBlockId), String> { let mut scope = NoVars; lower_args_with_scope(env, f, cur_bb, args, &mut scope) } -fn lower_args_with_vars(env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, args: &[ExprV0], vars: &mut HashMap) -> Result<(Vec, BasicBlockId), String> { +fn lower_args_with_vars( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + args: &[ExprV0], + vars: &mut HashMap, +) -> Result<(Vec, BasicBlockId), String> { let mut scope = MapVars::new(vars); lower_args_with_scope(env, f, cur_bb, args, &mut scope) } @@ -348,12 +517,26 @@ fn lower_stmt_with_vars( match s { StmtV0::Return { expr } => { let (v, cur) = lower_expr_with_vars(env, f, cur_bb, expr, vars)?; - if let Some(bb) = f.get_block_mut(cur) { bb.set_terminator(MirInstruction::Return { value: Some(v) }); } + if let Some(bb) = f.get_block_mut(cur) { + bb.set_terminator(MirInstruction::Return { value: Some(v) }); + } Ok(cur) } - StmtV0::Extern { iface, method, args } => { + StmtV0::Extern { + iface, + method, + args, + } => { let (arg_ids, cur) = lower_args_with_vars(env, f, cur_bb, args, vars)?; - if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::ExternCall { dst: None, iface_name: iface.clone(), method_name: method.clone(), args: arg_ids, effects: EffectMask::IO }); } + if let Some(bb) = f.get_block_mut(cur) { + bb.add_instruction(MirInstruction::ExternCall { + dst: None, + iface_name: iface.clone(), + method_name: method.clone(), + args: arg_ids, + effects: EffectMask::IO, + }); + } Ok(cur) } StmtV0::Expr { expr } => { @@ -367,7 +550,11 @@ fn lower_stmt_with_vars( } StmtV0::Break => { if let Some(ctx) = loop_stack.last().copied() { - if let Some(bb) = f.get_block_mut(cur_bb) { bb.set_terminator(MirInstruction::Jump { target: ctx.exit_bb }); } + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.set_terminator(MirInstruction::Jump { + target: ctx.exit_bb, + }); + } crate::jit::events::emit_lower( serde_json::json!({ "id": "loop_break","exit_bb": ctx.exit_bb.0,"decision": "lower" }), "loop", @@ -378,7 +565,11 @@ fn lower_stmt_with_vars( } StmtV0::Continue => { if let Some(ctx) = loop_stack.last().copied() { - if let Some(bb) = f.get_block_mut(cur_bb) { bb.set_terminator(MirInstruction::Jump { target: ctx.cond_bb }); } + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.set_terminator(MirInstruction::Jump { + target: ctx.cond_bb, + }); + } crate::jit::events::emit_lower( serde_json::json!({ "id": "loop_continue","cond_bb": ctx.cond_bb.0,"decision": "lower" }), "loop", @@ -387,40 +578,89 @@ fn lower_stmt_with_vars( } Ok(cur_bb) } - StmtV0::Try { try_body, catches, finally } => { + StmtV0::Try { + try_body, + catches, + finally, + } => { let try_enabled = std::env::var("NYASH_BRIDGE_TRY_ENABLE").ok().as_deref() == Some("1"); if !try_enabled || catches.is_empty() || catches.len() > 1 { let mut tmp_vars = vars.clone(); - let mut next_bb = lower_stmt_list_with_vars(f, cur_bb, try_body, &mut tmp_vars, loop_stack, env)?; + let mut next_bb = + lower_stmt_list_with_vars(f, cur_bb, try_body, &mut tmp_vars, loop_stack, env)?; if !finally.is_empty() { - next_bb = lower_stmt_list_with_vars(f, next_bb, finally, &mut tmp_vars, loop_stack, env)?; + next_bb = lower_stmt_list_with_vars( + f, + next_bb, + finally, + &mut tmp_vars, + loop_stack, + env, + )?; } *vars = tmp_vars; return Ok(next_bb); } let base_vars = vars.clone(); - let try_bb = next_block_id(f); f.add_block(BasicBlock::new(try_bb)); + let try_bb = next_block_id(f); + f.add_block(BasicBlock::new(try_bb)); let catch_clause = &catches[0]; - let catch_bb = next_block_id(f); f.add_block(BasicBlock::new(catch_bb)); - let finally_bb = if !finally.is_empty() { let id = next_block_id(f); f.add_block(BasicBlock::new(id)); Some(id) } else { None }; - let exit_bb = next_block_id(f); f.add_block(BasicBlock::new(exit_bb)); + let catch_bb = next_block_id(f); + f.add_block(BasicBlock::new(catch_bb)); + let finally_bb = if !finally.is_empty() { + let id = next_block_id(f); + f.add_block(BasicBlock::new(id)); + Some(id) + } else { + None + }; + let exit_bb = next_block_id(f); + f.add_block(BasicBlock::new(exit_bb)); let handler_target = finally_bb.unwrap_or(exit_bb); let exception_value = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::Catch { exception_type: catch_clause.type_hint.clone(), exception_value, handler_bb: catch_bb }); + bb.add_instruction(MirInstruction::Catch { + exception_type: catch_clause.type_hint.clone(), + exception_value, + handler_bb: catch_bb, + }); bb.set_terminator(MirInstruction::Jump { target: try_bb }); } let mut try_vars = vars.clone(); - let try_end = lower_stmt_list_with_vars(f, try_bb, try_body, &mut try_vars, loop_stack, env)?; - if let Some(bb) = f.get_block_mut(try_end) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: handler_target }); } } + let try_end = + lower_stmt_list_with_vars(f, try_bb, try_body, &mut try_vars, loop_stack, env)?; + if let Some(bb) = f.get_block_mut(try_end) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { + target: handler_target, + }); + } + } let try_branch_vars = try_vars.clone(); let mut catch_vars = base_vars.clone(); - if let Some(param) = &catch_clause.param { catch_vars.insert(param.clone(), exception_value); } - let catch_end = lower_stmt_list_with_vars(f, catch_bb, &catch_clause.body, &mut catch_vars, loop_stack, env)?; - if let Some(param) = &catch_clause.param { catch_vars.remove(param); } - if let Some(bb) = f.get_block_mut(catch_end) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: handler_target }); } } + if let Some(param) = &catch_clause.param { + catch_vars.insert(param.clone(), exception_value); + } + let catch_end = lower_stmt_list_with_vars( + f, + catch_bb, + &catch_clause.body, + &mut catch_vars, + loop_stack, + env, + )?; + if let Some(param) = &catch_clause.param { + catch_vars.remove(param); + } + if let Some(bb) = f.get_block_mut(catch_end) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { + target: handler_target, + }); + } + } let catch_branch_vars = catch_vars.clone(); use std::collections::HashSet; @@ -428,78 +668,220 @@ fn lower_stmt_with_vars( if let Some(finally_block) = finally_bb { let names: HashSet = { let mut set: HashSet = base_vars.keys().cloned().collect(); - for (_, map) in &branch_vars { set.extend(map.keys().cloned()); } + for (_, map) in &branch_vars { + set.extend(map.keys().cloned()); + } set }; let mut merged_vars = base_vars.clone(); let mut phi_entries: Vec<(ValueId, Vec<(BasicBlockId, ValueId)>)> = Vec::new(); for name in names { let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new(); - for (bbid, map) in &branch_vars { if let Some(&val) = map.get(&name) { inputs.push((*bbid, val)); } } - if inputs.is_empty() { if let Some(&base_val) = base_vars.get(&name) { merged_vars.insert(name.clone(), base_val); } continue; } + for (bbid, map) in &branch_vars { + if let Some(&val) = map.get(&name) { + inputs.push((*bbid, val)); + } + } + if inputs.is_empty() { + if let Some(&base_val) = base_vars.get(&name) { + merged_vars.insert(name.clone(), base_val); + } + continue; + } let unique: HashSet = inputs.iter().map(|(_, v)| *v).collect(); - if unique.len() == 1 { merged_vars.insert(name.clone(), inputs[0].1); continue; } - let dst = f.next_value_id(); inputs.sort_by_key(|(bbid, _)| bbid.0); phi_entries.push((dst, inputs)); merged_vars.insert(name.clone(), dst); + if unique.len() == 1 { + merged_vars.insert(name.clone(), inputs[0].1); + continue; + } + let dst = f.next_value_id(); + inputs.sort_by_key(|(bbid, _)| bbid.0); + phi_entries.push((dst, inputs)); + merged_vars.insert(name.clone(), dst); + } + if let Some(bb) = f.get_block_mut(finally_block) { + for (dst, inputs) in phi_entries { + bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs }); + } } - if let Some(bb) = f.get_block_mut(finally_block) { for (dst, inputs) in phi_entries { bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs }); } } let mut finally_vars = merged_vars.clone(); - let final_end = lower_stmt_list_with_vars(f, finally_block, finally, &mut finally_vars, loop_stack, env)?; - if let Some(bb) = f.get_block_mut(final_end) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: exit_bb }); } } - *vars = finally_vars; Ok(exit_bb) + let final_end = lower_stmt_list_with_vars( + f, + finally_block, + finally, + &mut finally_vars, + loop_stack, + env, + )?; + if let Some(bb) = f.get_block_mut(final_end) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { target: exit_bb }); + } + } + *vars = finally_vars; + Ok(exit_bb) } else { let names: HashSet = { let mut set: HashSet = base_vars.keys().cloned().collect(); - for (_, map) in &branch_vars { set.extend(map.keys().cloned()); } + for (_, map) in &branch_vars { + set.extend(map.keys().cloned()); + } set }; let mut merged_vars = base_vars.clone(); let mut phi_entries: Vec<(ValueId, Vec<(BasicBlockId, ValueId)>)> = Vec::new(); for name in names { let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new(); - for (bbid, map) in &branch_vars { if let Some(&val) = map.get(&name) { inputs.push((*bbid, val)); } } - if inputs.is_empty() { if let Some(&base_val) = base_vars.get(&name) { merged_vars.insert(name.clone(), base_val); } continue; } + for (bbid, map) in &branch_vars { + if let Some(&val) = map.get(&name) { + inputs.push((*bbid, val)); + } + } + if inputs.is_empty() { + if let Some(&base_val) = base_vars.get(&name) { + merged_vars.insert(name.clone(), base_val); + } + continue; + } let unique: HashSet = inputs.iter().map(|(_, v)| *v).collect(); - if unique.len() == 1 { merged_vars.insert(name.clone(), inputs[0].1); continue; } - let dst = f.next_value_id(); inputs.sort_by_key(|(bbid, _)| bbid.0); phi_entries.push((dst, inputs)); merged_vars.insert(name.clone(), dst); + if unique.len() == 1 { + merged_vars.insert(name.clone(), inputs[0].1); + continue; + } + let dst = f.next_value_id(); + inputs.sort_by_key(|(bbid, _)| bbid.0); + phi_entries.push((dst, inputs)); + merged_vars.insert(name.clone(), dst); } - if let Some(bb) = f.get_block_mut(exit_bb) { for (dst, inputs) in phi_entries { bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs }); } } - *vars = merged_vars; Ok(exit_bb) + if let Some(bb) = f.get_block_mut(exit_bb) { + for (dst, inputs) in phi_entries { + bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs }); + } + } + *vars = merged_vars; + Ok(exit_bb) } } StmtV0::If { cond, then, r#else } => { let (cval, cur) = lower_expr_with_vars(env, f, cur_bb, cond, vars)?; - let then_bb = next_block_id(f); let else_bb = BasicBlockId::new(then_bb.0 + 1); let merge_bb = BasicBlockId::new(then_bb.0 + 2); - f.add_block(BasicBlock::new(then_bb)); f.add_block(BasicBlock::new(else_bb)); f.add_block(BasicBlock::new(merge_bb)); - if let Some(bb) = f.get_block_mut(cur) { bb.set_terminator(MirInstruction::Branch { condition: cval, then_bb, else_bb }); } - let base_vars = vars.clone(); let mut then_vars = base_vars.clone(); - let tend = lower_stmt_list_with_vars(f, then_bb, then, &mut then_vars, loop_stack, env)?; - if let Some(bb) = f.get_block_mut(tend) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: merge_bb }); } } + let then_bb = next_block_id(f); + let else_bb = BasicBlockId::new(then_bb.0 + 1); + let merge_bb = BasicBlockId::new(then_bb.0 + 2); + f.add_block(BasicBlock::new(then_bb)); + f.add_block(BasicBlock::new(else_bb)); + f.add_block(BasicBlock::new(merge_bb)); + if let Some(bb) = f.get_block_mut(cur) { + bb.set_terminator(MirInstruction::Branch { + condition: cval, + then_bb, + else_bb, + }); + } + let base_vars = vars.clone(); + let mut then_vars = base_vars.clone(); + let tend = + lower_stmt_list_with_vars(f, then_bb, then, &mut then_vars, loop_stack, env)?; + if let Some(bb) = f.get_block_mut(tend) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { target: merge_bb }); + } + } let (else_end_pred, else_vars) = if let Some(elses) = r#else { let mut ev = base_vars.clone(); let eend = lower_stmt_list_with_vars(f, else_bb, elses, &mut ev, loop_stack, env)?; - if let Some(bb) = f.get_block_mut(eend) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: merge_bb }); } } + if let Some(bb) = f.get_block_mut(eend) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { target: merge_bb }); + } + } (eend, ev) } else { - if let Some(bb) = f.get_block_mut(else_bb) { bb.set_terminator(MirInstruction::Jump { target: merge_bb }); } + if let Some(bb) = f.get_block_mut(else_bb) { + bb.set_terminator(MirInstruction::Jump { target: merge_bb }); + } (else_bb, base_vars.clone()) }; use std::collections::HashSet; let no_phi = env.mir_no_phi; - let mut names: HashSet = base_vars.keys().cloned().collect(); for k in then_vars.keys() { names.insert(k.clone()); } for k in else_vars.keys() { names.insert(k.clone()); } + let mut names: HashSet = base_vars.keys().cloned().collect(); + for k in then_vars.keys() { + names.insert(k.clone()); + } + for k in else_vars.keys() { + names.insert(k.clone()); + } for name in names { - let tv = then_vars.get(&name).copied(); let ev = else_vars.get(&name).copied(); let exists_base = base_vars.contains_key(&name); + let tv = then_vars.get(&name).copied(); + let ev = else_vars.get(&name).copied(); + let exists_base = base_vars.contains_key(&name); match (tv, ev, exists_base) { (Some(tval), Some(eval), _) => { - let merged = if tval == eval { tval } else { let dst = f.next_value_id(); if no_phi { if let Some(bb) = f.get_block_mut(tend) { bb.add_instruction(MirInstruction::Copy { dst, src: tval }); } if let Some(bb) = f.get_block_mut(else_end_pred) { bb.add_instruction(MirInstruction::Copy { dst, src: eval }); } } else if let Some(bb) = f.get_block_mut(merge_bb) { bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(tend, tval), (else_end_pred, eval)] }); } dst }; vars.insert(name, merged); + let merged = if tval == eval { + tval + } else { + let dst = f.next_value_id(); + if no_phi { + if let Some(bb) = f.get_block_mut(tend) { + bb.add_instruction(MirInstruction::Copy { dst, src: tval }); + } + if let Some(bb) = f.get_block_mut(else_end_pred) { + bb.add_instruction(MirInstruction::Copy { dst, src: eval }); + } + } else if let Some(bb) = f.get_block_mut(merge_bb) { + bb.insert_instruction_after_phis(MirInstruction::Phi { + dst, + inputs: vec![(tend, tval), (else_end_pred, eval)], + }); + } + dst + }; + vars.insert(name, merged); } (Some(tval), None, true) => { if let Some(&bval) = base_vars.get(&name) { - let merged = if tval == bval { tval } else { let dst = f.next_value_id(); if no_phi { if let Some(bb) = f.get_block_mut(tend) { bb.add_instruction(MirInstruction::Copy { dst, src: tval }); } if let Some(bb) = f.get_block_mut(else_end_pred) { bb.add_instruction(MirInstruction::Copy { dst, src: bval }); } } else if let Some(bb) = f.get_block_mut(merge_bb) { bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(tend, tval), (else_end_pred, bval)] }); } dst }; vars.insert(name, merged); + let merged = if tval == bval { + tval + } else { + let dst = f.next_value_id(); + if no_phi { + if let Some(bb) = f.get_block_mut(tend) { + bb.add_instruction(MirInstruction::Copy { dst, src: tval }); + } + if let Some(bb) = f.get_block_mut(else_end_pred) { + bb.add_instruction(MirInstruction::Copy { dst, src: bval }); + } + } else if let Some(bb) = f.get_block_mut(merge_bb) { + bb.insert_instruction_after_phis(MirInstruction::Phi { + dst, + inputs: vec![(tend, tval), (else_end_pred, bval)], + }); + } + dst + }; + vars.insert(name, merged); } } (None, Some(eval), true) => { if let Some(&bval) = base_vars.get(&name) { - let merged = if eval == bval { eval } else { let dst = f.next_value_id(); if no_phi { if let Some(bb) = f.get_block_mut(tend) { bb.add_instruction(MirInstruction::Copy { dst, src: bval }); } if let Some(bb) = f.get_block_mut(else_end_pred) { bb.add_instruction(MirInstruction::Copy { dst, src: eval }); } } else if let Some(bb) = f.get_block_mut(merge_bb) { bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(tend, bval), (else_end_pred, eval)] }); } dst }; vars.insert(name, merged); + let merged = if eval == bval { + eval + } else { + let dst = f.next_value_id(); + if no_phi { + if let Some(bb) = f.get_block_mut(tend) { + bb.add_instruction(MirInstruction::Copy { dst, src: bval }); + } + if let Some(bb) = f.get_block_mut(else_end_pred) { + bb.add_instruction(MirInstruction::Copy { dst, src: eval }); + } + } else if let Some(bb) = f.get_block_mut(merge_bb) { + bb.insert_instruction_after_phis(MirInstruction::Phi { + dst, + inputs: vec![(tend, bval), (else_end_pred, eval)], + }); + } + dst + }; + vars.insert(name, merged); } } _ => {} @@ -508,37 +890,90 @@ fn lower_stmt_with_vars( Ok(merge_bb) } StmtV0::Loop { cond, body } => { - let cond_bb = next_block_id(f); let body_bb = BasicBlockId::new(cond_bb.0 + 1); let exit_bb = BasicBlockId::new(cond_bb.0 + 2); - f.add_block(BasicBlock::new(cond_bb)); f.add_block(BasicBlock::new(body_bb)); f.add_block(BasicBlock::new(exit_bb)); - if let Some(bb) = f.get_block_mut(cur_bb) { if !bb.is_terminated() { bb.add_instruction(MirInstruction::Jump { target: cond_bb }); } } + let cond_bb = next_block_id(f); + let body_bb = BasicBlockId::new(cond_bb.0 + 1); + let exit_bb = BasicBlockId::new(cond_bb.0 + 2); + f.add_block(BasicBlock::new(cond_bb)); + f.add_block(BasicBlock::new(body_bb)); + f.add_block(BasicBlock::new(exit_bb)); + if let Some(bb) = f.get_block_mut(cur_bb) { + if !bb.is_terminated() { + bb.add_instruction(MirInstruction::Jump { target: cond_bb }); + } + } let no_phi = env.mir_no_phi; - let base_vars = vars.clone(); let orig_names: Vec = base_vars.keys().cloned().collect(); + let base_vars = vars.clone(); + let orig_names: Vec = base_vars.keys().cloned().collect(); let mut phi_map: HashMap = HashMap::new(); for name in &orig_names { if let Some(&bval) = base_vars.get(name) { let dst = f.next_value_id(); if no_phi { - if let Some(bb) = f.get_block_mut(cur_bb) { bb.add_instruction(MirInstruction::Copy { dst, src: bval }); } - } else if let Some(bb) = f.get_block_mut(cond_bb) { bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(cur_bb, bval)] }); } + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.add_instruction(MirInstruction::Copy { dst, src: bval }); + } + } else if let Some(bb) = f.get_block_mut(cond_bb) { + bb.insert_instruction_after_phis(MirInstruction::Phi { + dst, + inputs: vec![(cur_bb, bval)], + }); + } phi_map.insert(name.clone(), dst); } } - for (name, &phi) in &phi_map { vars.insert(name.clone(), phi); } + for (name, &phi) in &phi_map { + vars.insert(name.clone(), phi); + } let (cval, _cend) = lower_expr_with_vars(env, f, cond_bb, cond, vars)?; - if let Some(bb) = f.get_block_mut(cond_bb) { bb.set_terminator(MirInstruction::Branch { condition: cval, then_bb: body_bb, else_bb: exit_bb }); } - let mut body_vars = vars.clone(); loop_stack.push(LoopContext { cond_bb, exit_bb }); - let bend_res = lower_stmt_list_with_vars(f, body_bb, body, &mut body_vars, loop_stack, env); loop_stack.pop(); + if let Some(bb) = f.get_block_mut(cond_bb) { + bb.set_terminator(MirInstruction::Branch { + condition: cval, + then_bb: body_bb, + else_bb: exit_bb, + }); + } + let mut body_vars = vars.clone(); + loop_stack.push(LoopContext { cond_bb, exit_bb }); + let bend_res = + lower_stmt_list_with_vars(f, body_bb, body, &mut body_vars, loop_stack, env); + loop_stack.pop(); let bend = bend_res?; - if let Some(bb) = f.get_block_mut(bend) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: cond_bb }); } } + if let Some(bb) = f.get_block_mut(bend) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { target: cond_bb }); + } + } let backedge_to_cond = matches!(f.blocks.get(&bend).and_then(|bb| bb.terminator.as_ref()), Some(MirInstruction::Jump { target, .. }) if *target == cond_bb); if backedge_to_cond { if no_phi { - for (name, &phi_dst) in &phi_map { if let Some(&latch_val) = body_vars.get(name) { if let Some(bb) = f.get_block_mut(bend) { bb.add_instruction(MirInstruction::Copy { dst: phi_dst, src: latch_val }); } } } + for (name, &phi_dst) in &phi_map { + if let Some(&latch_val) = body_vars.get(name) { + if let Some(bb) = f.get_block_mut(bend) { + bb.add_instruction(MirInstruction::Copy { + dst: phi_dst, + src: latch_val, + }); + } + } + } } else if let Some(bb) = f.get_block_mut(cond_bb) { - for (name, &phi_dst) in &phi_map { if let Some(&latch_val) = body_vars.get(name) { for inst in &mut bb.instructions { if let MirInstruction::Phi { dst, inputs } = inst { if *dst == phi_dst { inputs.push((bend, latch_val)); break; } } } } } + for (name, &phi_dst) in &phi_map { + if let Some(&latch_val) = body_vars.get(name) { + for inst in &mut bb.instructions { + if let MirInstruction::Phi { dst, inputs } = inst { + if *dst == phi_dst { + inputs.push((bend, latch_val)); + break; + } + } + } + } + } } } - for (name, &phi) in &phi_map { vars.insert(name.clone(), phi); } + for (name, &phi) in &phi_map { + vars.insert(name.clone(), phi); + } Ok(exit_bb) } } @@ -555,29 +990,53 @@ fn lower_stmt_list_with_vars( let mut cur = start_bb; for s in stmts { cur = lower_stmt_with_vars(f, cur, s, vars, loop_stack, env)?; - if let Some(bb) = f.blocks.get(&cur) { if bb.is_terminated() { break; } } + if let Some(bb) = f.blocks.get(&cur) { + if bb.is_terminated() { + break; + } + } } Ok(cur) } pub(super) fn lower_program(prog: ProgramV0) -> Result { - if prog.body.is_empty() { return Err("empty body".into()); } + if prog.body.is_empty() { + return Err("empty body".into()); + } let env = BridgeEnv::load(); let mut module = MirModule::new("ny_json_v0".into()); - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let entry = BasicBlockId::new(0); let mut f = MirFunction::new(sig, entry); let mut var_map: HashMap = HashMap::new(); let mut loop_stack: Vec = Vec::new(); let start_bb = f.entry_block; - let end_bb = lower_stmt_list_with_vars(&mut f, start_bb, &prog.body, &mut var_map, &mut loop_stack, &env)?; + let end_bb = lower_stmt_list_with_vars( + &mut f, + start_bb, + &prog.body, + &mut var_map, + &mut loop_stack, + &env, + )?; let need_default_ret = f.blocks.iter().any(|(_k, b)| !b.is_terminated()); if need_default_ret { - let target_bb = end_bb; let dst_id = f.next_value_id(); + let target_bb = end_bb; + let dst_id = f.next_value_id(); if let Some(bb) = f.get_block_mut(target_bb) { if !bb.is_terminated() { - bb.add_instruction(MirInstruction::Const { dst: dst_id, value: ConstValue::Integer(0) }); - bb.set_terminator(MirInstruction::Return { value: Some(dst_id) }); + bb.add_instruction(MirInstruction::Const { + dst: dst_id, + value: ConstValue::Integer(0), + }); + bb.set_terminator(MirInstruction::Return { + value: Some(dst_id), + }); } } } @@ -592,4 +1051,3 @@ pub(super) fn maybe_dump_mir(module: &MirModule) { println!("{}", p.print_module(module)); } } - diff --git a/src/runner/json_v0_bridge/mod.rs b/src/runner/json_v0_bridge/mod.rs index 09d97f67..c3c71530 100644 --- a/src/runner/json_v0_bridge/mod.rs +++ b/src/runner/json_v0_bridge/mod.rs @@ -1,12 +1,13 @@ mod ast; -mod lowering; mod lexer; +mod lowering; use ast::ProgramV0; use lowering::lower_program; pub fn parse_json_v0_to_module(json: &str) -> Result { - let prog: ProgramV0 = serde_json::from_str(json).map_err(|e| format!("invalid JSON v0: {}", e))?; + let prog: ProgramV0 = + serde_json::from_str(json).map_err(|e| format!("invalid JSON v0: {}", e))?; if prog.version != 0 || prog.kind != "Program" { return Err("unsupported IR: expected {version:0, kind:\"Program\"}".into()); } @@ -21,4 +22,6 @@ pub fn parse_source_v0_to_module(input: &str) -> Result Result<(), String> { - use nyash_rust::mir::{MirInstruction as I, BinaryOp as B, CompareOp as C, MirType}; + use nyash_rust::mir::{BinaryOp as B, CompareOp as C, MirInstruction as I, MirType}; let mut funs = Vec::new(); for (name, f) in &module.functions { let mut blocks = Vec::new(); @@ -24,20 +24,23 @@ pub fn emit_mir_json_for_harness( .map(|(b, v)| json!([v.as_u32(), b.as_u32()])) .collect(); // dst_type hint: if all incoming values are String-ish, annotate result as String handle - let all_str = inputs.iter().all(|(_b, v)| { - match f.metadata.value_types.get(v) { - Some(MirType::String) => true, - Some(MirType::Box(bt)) if bt == "StringBox" => true, - _ => false, - } - }); + let all_str = + inputs + .iter() + .all(|(_b, v)| match f.metadata.value_types.get(v) { + Some(MirType::String) => true, + Some(MirType::Box(bt)) if bt == "StringBox" => true, + _ => false, + }); if all_str { insts.push(json!({ "op":"phi","dst": dst.as_u32(), "incoming": incoming, "dst_type": {"kind":"handle","box_type":"StringBox"} })); } else { - insts.push(json!({"op":"phi","dst": dst.as_u32(), "incoming": incoming})); + insts.push( + json!({"op":"phi","dst": dst.as_u32(), "incoming": incoming}), + ); } } } @@ -45,7 +48,11 @@ pub fn emit_mir_json_for_harness( for inst in &bb.instructions { match inst { I::UnaryOp { dst, op, operand } => { - let kind = match op { nyash_rust::mir::UnaryOp::Neg => "neg", nyash_rust::mir::UnaryOp::Not => "not", nyash_rust::mir::UnaryOp::BitNot => "bitnot" }; + let kind = match op { + nyash_rust::mir::UnaryOp::Neg => "neg", + nyash_rust::mir::UnaryOp::Not => "not", + nyash_rust::mir::UnaryOp::BitNot => "bitnot", + }; insts.push(json!({"op":"unop","kind": kind, "src": operand.as_u32(), "dst": dst.as_u32()})); } I::Const { dst, value } => { @@ -70,13 +77,27 @@ pub fn emit_mir_json_for_harness( } })); } - nyash_rust::mir::ConstValue::Null | nyash_rust::mir::ConstValue::Void => { + nyash_rust::mir::ConstValue::Null + | nyash_rust::mir::ConstValue::Void => { insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "void", "value": 0}})); } } } I::BinOp { dst, op, lhs, rhs } => { - let op_s = match op { B::Add=>"+",B::Sub=>"-",B::Mul=>"*",B::Div=>"/",B::Mod=>"%",B::BitAnd=>"&",B::BitOr=>"|",B::BitXor=>"^",B::Shl=>"<<",B::Shr=>">>",B::And=>"&",B::Or=>"|"}; + let op_s = match op { + B::Add => "+", + B::Sub => "-", + B::Mul => "*", + B::Div => "/", + B::Mod => "%", + B::BitAnd => "&", + B::BitOr => "|", + B::BitXor => "^", + B::Shl => "<<", + B::Shr => ">>", + B::And => "&", + B::Or => "|", + }; let mut obj = json!({"op":"binop","operation": op_s, "lhs": lhs.as_u32(), "rhs": rhs.as_u32(), "dst": dst.as_u32()}); // dst_type hint for string concatenation: if either side is String-ish and op is '+', mark result as String handle if matches!(op, B::Add) { @@ -91,16 +112,24 @@ pub fn emit_mir_json_for_harness( _ => false, }; if lhs_is_str || rhs_is_str { - obj["dst_type"] = json!({"kind":"handle","box_type":"StringBox"}); + obj["dst_type"] = + json!({"kind":"handle","box_type":"StringBox"}); } } insts.push(obj); } I::Compare { dst, op, lhs, rhs } => { - let op_s = match op { C::Lt=>"<", C::Le=>"<=", C::Gt=>">", C::Ge=>">=", C::Eq=>"==", C::Ne=>"!=" }; + let op_s = match op { + C::Lt => "<", + C::Le => "<=", + C::Gt => ">", + C::Ge => ">=", + C::Eq => "==", + C::Ne => "!=", + }; let mut obj = json!({"op":"compare","operation": op_s, "lhs": lhs.as_u32(), "rhs": rhs.as_u32(), "dst": dst.as_u32()}); // cmp_kind hint for string equality - if matches!(op, C::Eq|C::Ne) { + if matches!(op, C::Eq | C::Ne) { let lhs_is_str = match f.metadata.value_types.get(lhs) { Some(MirType::String) => true, Some(MirType::Box(bt)) if bt == "StringBox" => true, @@ -117,15 +146,25 @@ pub fn emit_mir_json_for_harness( } insts.push(obj); } - I::Call { dst, func, args, .. } => { + I::Call { + dst, func, args, .. + } => { let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect(); insts.push(json!({"op":"call","func": func.as_u32(), "args": args_a, "dst": dst.map(|d| d.as_u32())})); } - I::ExternCall { dst, iface_name, method_name, args, .. } => { + I::ExternCall { + dst, + iface_name, + method_name, + args, + .. + } => { let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect(); let func_name = if iface_name == "env.console" { format!("nyash.console.{}", method_name) - } else { format!("{}.{}", iface_name, method_name) }; + } else { + format!("{}.{}", iface_name, method_name) + }; let mut obj = json!({ "op": "externcall", "func": func_name, @@ -135,30 +174,55 @@ pub fn emit_mir_json_for_harness( // Minimal dst_type hints for known externs if iface_name == "env.console" { // console.* returns i64 status (ignored by user code) - if dst.is_some() { obj["dst_type"] = json!("i64"); } + if dst.is_some() { + obj["dst_type"] = json!("i64"); + } } insts.push(obj); } - I::BoxCall { dst, box_val, method, args, .. } => { + I::BoxCall { + dst, + box_val, + method, + args, + .. + } => { let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect(); // Minimal dst_type hints let mut obj = json!({ "op":"boxcall","box": box_val.as_u32(), "method": method, "args": args_a, "dst": dst.map(|d| d.as_u32()) }); let m = method.as_str(); - let dst_ty = if m == "substring" || m == "dirname" || m == "join" || m == "read_all" || m == "read" { + let dst_ty = if m == "substring" + || m == "dirname" + || m == "join" + || m == "read_all" + || m == "read" + { Some(json!({"kind":"handle","box_type":"StringBox"})) } else if m == "length" || m == "lastIndexOf" { Some(json!("i64")) - } else { None }; - if let Some(t) = dst_ty { obj["dst_type"] = t; } + } else { + None + }; + if let Some(t) = dst_ty { + obj["dst_type"] = t; + } insts.push(obj); } - I::NewBox { dst, box_type, args } => { + I::NewBox { + dst, + box_type, + args, + } => { let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect(); insts.push(json!({"op":"newbox","type": box_type, "args": args_a, "dst": dst.as_u32()})); } - I::Branch { condition, then_bb, else_bb } => { + I::Branch { + condition, + then_bb, + else_bb, + } => { insts.push(json!({"op":"branch","cond": condition.as_u32(), "then": then_bb.as_u32(), "else": else_bb.as_u32()})); } I::Jump { target } => { @@ -195,7 +259,7 @@ pub fn emit_mir_json_for_harness_bin( module: &crate::mir::MirModule, path: &std::path::Path, ) -> Result<(), String> { - use crate::mir::{MirInstruction as I, BinaryOp as B, CompareOp as C, MirType}; + use crate::mir::{BinaryOp as B, CompareOp as C, MirInstruction as I, MirType}; let mut funs = Vec::new(); for (name, f) in &module.functions { let mut blocks = Vec::new(); @@ -210,53 +274,67 @@ pub fn emit_mir_json_for_harness_bin( .iter() .map(|(b, v)| json!([v.as_u32(), b.as_u32()])) .collect(); - let all_str = inputs.iter().all(|(_b, v)| { - match f.metadata.value_types.get(v) { - Some(MirType::String) => true, - Some(MirType::Box(bt)) if bt == "StringBox" => true, - _ => false, - } - }); + let all_str = + inputs + .iter() + .all(|(_b, v)| match f.metadata.value_types.get(v) { + Some(MirType::String) => true, + Some(MirType::Box(bt)) if bt == "StringBox" => true, + _ => false, + }); if all_str { insts.push(json!({ "op":"phi","dst": dst.as_u32(), "incoming": incoming, "dst_type": {"kind":"handle","box_type":"StringBox"} })); } else { - insts.push(json!({"op":"phi","dst": dst.as_u32(), "incoming": incoming})); + insts.push( + json!({"op":"phi","dst": dst.as_u32(), "incoming": incoming}), + ); } } } for inst in &bb.instructions { match inst { - I::Const { dst, value } => { - match value { - crate::mir::ConstValue::Integer(i) => { - insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "i64", "value": i}})); - } - crate::mir::ConstValue::Float(fv) => { - insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "f64", "value": fv}})); - } - crate::mir::ConstValue::Bool(b) => { - insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "i64", "value": if *b {1} else {0}}})); - } - crate::mir::ConstValue::String(s) => { - insts.push(json!({ - "op":"const", - "dst": dst.as_u32(), - "value": { - "type": {"kind":"handle","box_type":"StringBox"}, - "value": s - } - })); - } - crate::mir::ConstValue::Null | crate::mir::ConstValue::Void => { - insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "void", "value": 0}})); - } + I::Const { dst, value } => match value { + crate::mir::ConstValue::Integer(i) => { + insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "i64", "value": i}})); } - } + crate::mir::ConstValue::Float(fv) => { + insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "f64", "value": fv}})); + } + crate::mir::ConstValue::Bool(b) => { + insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "i64", "value": if *b {1} else {0}}})); + } + crate::mir::ConstValue::String(s) => { + insts.push(json!({ + "op":"const", + "dst": dst.as_u32(), + "value": { + "type": {"kind":"handle","box_type":"StringBox"}, + "value": s + } + })); + } + crate::mir::ConstValue::Null | crate::mir::ConstValue::Void => { + insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "void", "value": 0}})); + } + }, I::BinOp { dst, op, lhs, rhs } => { - let op_s = match op { B::Add=>"+",B::Sub=>"-",B::Mul=>"*",B::Div=>"/",B::Mod=>"%",B::BitAnd=>"&",B::BitOr=>"|",B::BitXor=>"^",B::Shl=>"<<",B::Shr=>">>",B::And=>"&",B::Or=>"|"}; + let op_s = match op { + B::Add => "+", + B::Sub => "-", + B::Mul => "*", + B::Div => "/", + B::Mod => "%", + B::BitAnd => "&", + B::BitOr => "|", + B::BitXor => "^", + B::Shl => "<<", + B::Shr => ">>", + B::And => "&", + B::Or => "|", + }; let mut obj = json!({"op":"binop","operation": op_s, "lhs": lhs.as_u32(), "rhs": rhs.as_u32(), "dst": dst.as_u32()}); if matches!(op, B::Add) { let lhs_is_str = match f.metadata.value_types.get(lhs) { @@ -270,43 +348,84 @@ pub fn emit_mir_json_for_harness_bin( _ => false, }; if lhs_is_str || rhs_is_str { - obj["dst_type"] = json!({"kind":"handle","box_type":"StringBox"}); + obj["dst_type"] = + json!({"kind":"handle","box_type":"StringBox"}); } } insts.push(obj); } I::Compare { dst, op, lhs, rhs } => { - let op_s = match op { C::Eq=>"==",C::Ne=>"!=",C::Lt=>"<",C::Le=>"<=",C::Gt=>">",C::Ge=>">=" }; + let op_s = match op { + C::Eq => "==", + C::Ne => "!=", + C::Lt => "<", + C::Le => "<=", + C::Gt => ">", + C::Ge => ">=", + }; insts.push(json!({"op":"compare","operation": op_s, "lhs": lhs.as_u32(), "rhs": rhs.as_u32(), "dst": dst.as_u32()})); } - I::ExternCall { dst, iface_name, method_name, args, .. } => { + I::ExternCall { + dst, + iface_name, + method_name, + args, + .. + } => { let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect(); let mut obj = json!({ "op":"externcall","func": format!("{}.{}", iface_name, method_name), "args": args_a, "dst": dst.map(|d| d.as_u32()), }); - if iface_name == "env.console" { if dst.is_some() { obj["dst_type"] = json!("i64"); } } + if iface_name == "env.console" { + if dst.is_some() { + obj["dst_type"] = json!("i64"); + } + } insts.push(obj); } - I::BoxCall { dst, box_val, method, args, .. } => { + I::BoxCall { + dst, + box_val, + method, + args, + .. + } => { let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect(); let mut obj = json!({ "op":"boxcall","box": box_val.as_u32(), "method": method, "args": args_a, "dst": dst.map(|d| d.as_u32()) }); let m = method.as_str(); - let dst_ty = if m == "substring" || m == "dirname" || m == "join" || m == "read_all" || m == "read" { + let dst_ty = if m == "substring" + || m == "dirname" + || m == "join" + || m == "read_all" + || m == "read" + { Some(json!({"kind":"handle","box_type":"StringBox"})) } else if m == "length" || m == "lastIndexOf" { Some(json!("i64")) - } else { None }; - if let Some(t) = dst_ty { obj["dst_type"] = t; } + } else { + None + }; + if let Some(t) = dst_ty { + obj["dst_type"] = t; + } insts.push(obj); } - I::NewBox { dst, box_type, args } => { + I::NewBox { + dst, + box_type, + args, + } => { let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect(); insts.push(json!({"op":"newbox","type": box_type, "args": args_a, "dst": dst.as_u32()})); } - I::Branch { condition, then_bb, else_bb } => { + I::Branch { + condition, + then_bb, + else_bb, + } => { insts.push(json!({"op":"branch","cond": condition.as_u32(), "then": then_bb.as_u32(), "else": else_bb.as_u32()})); } I::Jump { target } => { @@ -315,14 +434,16 @@ pub fn emit_mir_json_for_harness_bin( I::Return { value } => { insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())})); } - _ => { } + _ => {} } } - if let Some(term) = &bb.terminator { match term { + if let Some(term) = &bb.terminator { + match term { I::Return { value } => insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())})), I::Jump { target } => insts.push(json!({"op":"jump","target": target.as_u32()})), I::Branch { condition, then_bb, else_bb } => insts.push(json!({"op":"branch","cond": condition.as_u32(), "then": then_bb.as_u32(), "else": else_bb.as_u32()})), - _ => {} } } + _ => {} } + } blocks.push(json!({"id": bid.as_u32(), "instructions": insts})); } } diff --git a/src/runner/mod.rs b/src/runner/mod.rs index ef29afab..9a182ed4 100644 --- a/src/runner/mod.rs +++ b/src/runner/mod.rs @@ -1,6 +1,6 @@ /*! * Execution Runner Module - Nyash File and Mode Execution Coordinator - * + * * This module handles all execution logic, backend selection, and mode coordination, * separated from CLI parsing and the main entry point. */ @@ -10,28 +10,28 @@ use nyash_rust::cli::CliConfig; // pruned unused runtime imports in this module #[cfg(feature = "wasm-backend")] -use nyash_rust::backend::{wasm::WasmBackend, aot::AotBackend}; +use nyash_rust::backend::{aot::AotBackend, wasm::WasmBackend}; #[cfg(feature = "llvm-inkwell-legacy")] -use nyash_rust::backend::{llvm_compile_and_execute}; +use nyash_rust::backend::llvm_compile_and_execute; use std::{fs, process}; -mod modes; +mod box_index; +mod build; +mod cli_directives; mod demos; +mod dispatch; mod json_v0_bridge; mod mir_json_emit; +mod modes; mod pipe_io; mod pipeline; -mod cli_directives; -mod trace; -mod box_index; -mod tasks; -mod build; -mod dispatch; mod selfhost; +mod tasks; +mod trace; // v2 plugin system imports -use nyash_rust::runtime; use nyash_rust::runner_plugin_init; +use nyash_rust::runtime; // use std::path::PathBuf; // not used in current runner /// Resolve a using target according to priority: modules > relative > using-paths @@ -68,16 +68,30 @@ impl NyashRunner { // CLI --using SPEC entries (SPEC: 'ns', 'ns as Alias', '"path" as Alias') for spec in &self.config.cli_usings { let s = spec.trim(); - if s.is_empty() { continue; } + if s.is_empty() { + continue; + } let (target, alias) = if let Some(pos) = s.find(" as ") { - (s[..pos].trim().to_string(), Some(s[pos+4..].trim().to_string())) - } else { (s.to_string(), None) }; + ( + s[..pos].trim().to_string(), + Some(s[pos + 4..].trim().to_string()), + ) + } else { + (s.to_string(), None) + }; // Normalize quotes for path - let is_path = target.starts_with('"') || target.starts_with("./") || target.starts_with('/') || target.ends_with(".nyash"); + let is_path = target.starts_with('"') + || target.starts_with("./") + || target.starts_with('/') + || target.ends_with(".nyash"); if is_path { let path = target.trim_matches('"').to_string(); let name = alias.clone().unwrap_or_else(|| { - std::path::Path::new(&path).file_stem().and_then(|s| s.to_str()).unwrap_or("module").to_string() + std::path::Path::new(&path) + .file_stem() + .and_then(|s| s.to_str()) + .unwrap_or("module") + .to_string() }); pending_using.push((name, Some(path))); } else { @@ -96,9 +110,16 @@ impl NyashRunner { // Try to extract quick hints without failing let mut root_info = String::new(); if let Ok(v) = serde_json::from_str::(&s) { - if let Some(r) = v.get("root_path").and_then(|x| x.as_str()) { root_info = format!(" root='{}'", r); } + if let Some(r) = v.get("root_path").and_then(|x| x.as_str()) { + root_info = format!(" root='{}'", r); + } } - eprintln!("[deps] loaded {} bytes from{} {}", bytes, if root_info.is_empty(){""} else {":"}, root_info); + eprintln!( + "[deps] loaded {} bytes from{} {}", + bytes, + if root_info.is_empty() { "" } else { ":" }, + root_info + ); } Err(e) => { eprintln!("[deps] read error: {}", e); @@ -107,7 +128,9 @@ impl NyashRunner { } // Phase-15: JSON IR v0 bridge (stdin/file) - if self.try_run_json_v0_pipe() { return; } + if self.try_run_json_v0_pipe() { + return; + } // Run named task from nyash.toml (MVP) if let Some(task) = self.config.run_task.clone() { if let Err(e) = run_named_task(&task) { @@ -117,7 +140,9 @@ impl NyashRunner { return; } // Verbose CLI flag maps to env for downstream helpers/scripts - if self.config.cli_verbose { std::env::set_var("NYASH_CLI_VERBOSE", "1"); } + if self.config.cli_verbose { + std::env::set_var("NYASH_CLI_VERBOSE", "1"); + } // Script-level env directives (special comments) — parse early // Supported: // // @env KEY=VALUE @@ -126,8 +151,13 @@ impl NyashRunner { if let Some(ref filename) = self.config.file { if let Ok(code) = fs::read_to_string(filename) { // Apply script-level directives and lint - let strict_fields = std::env::var("NYASH_FIELDS_TOP_STRICT").ok().as_deref() == Some("1"); - if let Err(e) = cli_directives::apply_cli_directives_from_source(&code, strict_fields, self.config.cli_verbose) { + let strict_fields = + std::env::var("NYASH_FIELDS_TOP_STRICT").ok().as_deref() == Some("1"); + if let Err(e) = cli_directives::apply_cli_directives_from_source( + &code, + strict_fields, + self.config.cli_verbose, + ) { eprintln!("❌ Lint/Directive error: {}", e); std::process::exit(1); } @@ -135,13 +165,23 @@ impl NyashRunner { // Env overrides for using rules // Merge late env overrides (if any) if let Ok(paths) = std::env::var("NYASH_USING_PATH") { - for p in paths.split(':') { let p = p.trim(); if !p.is_empty() { using_ctx.using_paths.push(p.to_string()); } } + for p in paths.split(':') { + let p = p.trim(); + if !p.is_empty() { + using_ctx.using_paths.push(p.to_string()); + } + } } if let Ok(mods) = std::env::var("NYASH_MODULES") { for ent in mods.split(',') { - if let Some((k,v)) = ent.split_once('=') { - let k = k.trim(); let v = v.trim(); - if !k.is_empty() && !v.is_empty() { using_ctx.pending_modules.push((k.to_string(), v.to_string())); } + if let Some((k, v)) = ent.split_once('=') { + let k = k.trim(); + let v = v.trim(); + if !k.is_empty() && !v.is_empty() { + using_ctx + .pending_modules + .push((k.to_string(), v.to_string())); + } } } } @@ -156,9 +196,21 @@ impl NyashRunner { let verbose = std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1"); let ctx = std::path::Path::new(filename).parent(); for (ns, alias) in pending_using.iter() { - let value = match resolve_using_target(ns, false, &using_ctx.pending_modules, &using_ctx.using_paths, &using_ctx.aliases, ctx, strict, verbose) { + let value = match resolve_using_target( + ns, + false, + &using_ctx.pending_modules, + &using_ctx.using_paths, + &using_ctx.aliases, + ctx, + strict, + verbose, + ) { Ok(v) => v, - Err(e) => { eprintln!("❌ using: {}", e); std::process::exit(1); } + Err(e) => { + eprintln!("❌ using: {}", e); + std::process::exit(1); + } }; let sb = nyash_rust::box_trait::StringBox::new(value.clone()); nyash_rust::runtime::modules_registry::set(ns.clone(), Box::new(sb)); @@ -181,10 +233,10 @@ impl NyashRunner { } } - // 🏭 Phase 9.78b: Initialize unified registry - runtime::init_global_unified_registry(); - - // Try to initialize BID plugins from nyash.toml (best-effort) + // 🏭 Phase 9.78b: Initialize unified registry + runtime::init_global_unified_registry(); + + // Try to initialize BID plugins from nyash.toml (best-effort) // Allow disabling during snapshot/CI via NYASH_DISABLE_PLUGINS=1 if std::env::var("NYASH_DISABLE_PLUGINS").ok().as_deref() != Some("1") { runner_plugin_init::init_bid_plugins(); @@ -197,29 +249,53 @@ impl NyashRunner { std::env::set_var("NYASH_USE_PLUGIN_BUILTINS", "1"); } // Merge FileBox,TOMLBox with defaults if present - let mut override_types: Vec = 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() - } else { - vec!["ArrayBox".into(), "MapBox".into()] - }; - for t in ["FileBox", "TOMLBox"] { if !override_types.iter().any(|x| x==t) { override_types.push(t.into()); } } + let mut override_types: Vec = + 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() + } else { + vec!["ArrayBox".into(), "MapBox".into()] + }; + for t in ["FileBox", "TOMLBox"] { + if !override_types.iter().any(|x| x == t) { + override_types.push(t.into()); + } + } std::env::set_var("NYASH_PLUGIN_OVERRIDE_TYPES", override_types.join(",")); // Opt-in: load Ny script plugins listed in nyash.toml [ny_plugins] - if self.config.load_ny_plugins || std::env::var("NYASH_LOAD_NY_PLUGINS").ok().as_deref() == Some("1") { + if self.config.load_ny_plugins + || std::env::var("NYASH_LOAD_NY_PLUGINS").ok().as_deref() == Some("1") + { if let Ok(text) = std::fs::read_to_string("nyash.toml") { if let Ok(doc) = toml::from_str::(&text) { if let Some(np) = doc.get("ny_plugins") { let mut list: Vec = Vec::new(); if let Some(arr) = np.as_array() { - for v in arr { if let Some(s) = v.as_str() { list.push(s.to_string()); } } + for v in arr { + if let Some(s) = v.as_str() { + list.push(s.to_string()); + } + } } else if let Some(tbl) = np.as_table() { - for (_k, v) in tbl { if let Some(s) = v.as_str() { list.push(s.to_string()); } - else if let Some(arr) = v.as_array() { for e in arr { if let Some(s) = e.as_str() { list.push(s.to_string()); } } } + for (_k, v) in tbl { + if let Some(s) = v.as_str() { + list.push(s.to_string()); + } else if let Some(arr) = v.as_array() { + for e in arr { + if let Some(s) = e.as_str() { + list.push(s.to_string()); + } + } + } } } if !list.is_empty() { - let list_only = std::env::var("NYASH_NY_PLUGINS_LIST_ONLY").ok().as_deref() == Some("1"); + let list_only = + std::env::var("NYASH_NY_PLUGINS_LIST_ONLY").ok().as_deref() + == Some("1"); println!("🧩 Ny script plugins ({}):", list.len()); for p in list { if list_only { @@ -229,13 +305,20 @@ impl NyashRunner { // Execute each script best-effort via interpreter match std::fs::read_to_string(&p) { Ok(code) => { - match nyash_rust::parser::NyashParser::parse_from_string(&code) { + match nyash_rust::parser::NyashParser::parse_from_string( + &code, + ) { Ok(ast) => { - let mut interpreter = nyash_rust::interpreter::NyashInterpreter::new(); + let mut interpreter = + nyash_rust::interpreter::NyashInterpreter::new( + ); match interpreter.execute(ast) { Ok(_) => println!("[ny_plugins] {}: OK", p), Err(e) => { - println!("[ny_plugins] {}: FAIL ({})", p, e); + println!( + "[ny_plugins] {}: FAIL ({})", + p, e + ); // continue to next } } @@ -267,16 +350,26 @@ impl NyashRunner { // Optional: JIT controls via CLI flags (centralized) { // CLI opt-in for JSONL events - if self.config.jit_events { std::env::set_var("NYASH_JIT_EVENTS", "1"); } - if self.config.jit_events_compile { std::env::set_var("NYASH_JIT_EVENTS_COMPILE", "1"); } - if self.config.jit_events_runtime { std::env::set_var("NYASH_JIT_EVENTS_RUNTIME", "1"); } - if let Some(ref p) = self.config.jit_events_path { std::env::set_var("NYASH_JIT_EVENTS_PATH", p); } + if self.config.jit_events { + std::env::set_var("NYASH_JIT_EVENTS", "1"); + } + if self.config.jit_events_compile { + std::env::set_var("NYASH_JIT_EVENTS_COMPILE", "1"); + } + if self.config.jit_events_runtime { + std::env::set_var("NYASH_JIT_EVENTS_RUNTIME", "1"); + } + if let Some(ref p) = self.config.jit_events_path { + std::env::set_var("NYASH_JIT_EVENTS_PATH", p); + } let mut jc = nyash_rust::jit::config::JitConfig::from_env(); jc.exec |= self.config.jit_exec; jc.stats |= self.config.jit_stats; jc.stats_json |= self.config.jit_stats_json; jc.dump |= self.config.jit_dump; - if self.config.jit_threshold.is_some() { jc.threshold = self.config.jit_threshold; } + if self.config.jit_threshold.is_some() { + jc.threshold = self.config.jit_threshold; + } jc.phi_min |= self.config.jit_phi_min; jc.hostcall |= self.config.jit_hostcall; jc.handle_debug |= self.config.jit_handle_debug; @@ -286,8 +379,12 @@ impl NyashRunner { let events_on = std::env::var("NYASH_JIT_EVENTS").ok().as_deref() == Some("1") || std::env::var("NYASH_JIT_EVENTS_COMPILE").ok().as_deref() == Some("1") || std::env::var("NYASH_JIT_EVENTS_RUNTIME").ok().as_deref() == Some("1"); - if events_on && jc.threshold.is_none() { jc.threshold = Some(1); } - if self.config.jit_only { std::env::set_var("NYASH_JIT_ONLY", "1"); } + if events_on && jc.threshold.is_none() { + jc.threshold = Some(1); + } + if self.config.jit_only { + std::env::set_var("NYASH_JIT_ONLY", "1"); + } // Apply runtime capability probe (e.g., disable b1 ABI if unsupported) let caps = nyash_rust::jit::config::probe_capabilities(); jc = nyash_rust::jit::config::apply_runtime_caps(jc, caps); @@ -316,7 +413,7 @@ impl NyashRunner { println!("===================================="); println!("Running {} iterations per test...", self.config.iterations); println!(); - + self.execute_benchmark_mode(); return; } @@ -350,12 +447,13 @@ impl NyashRunner { impl NyashRunner { /// Run a file through independent JIT engine (no VM execute loop) fn run_file_jit_direct(&self, filename: &str) { + use nyash_rust::{mir::MirCompiler, parser::NyashParser}; use std::fs; - use nyash_rust::{parser::NyashParser, mir::MirCompiler}; // Small helper for unified error output (text or JSON) let emit_err = |phase: &str, code: &str, msg: &str| { if std::env::var("NYASH_JIT_STATS_JSON").ok().as_deref() == Some("1") - || std::env::var("NYASH_JIT_ERROR_JSON").ok().as_deref() == Some("1") { + || std::env::var("NYASH_JIT_ERROR_JSON").ok().as_deref() == Some("1") + { let payload = serde_json::json!({ "kind": "jit_direct_error", "phase": phase, @@ -371,14 +469,33 @@ impl NyashRunner { // Require cranelift feature at runtime by attempting compile; if unavailable compile_function returns None let code = match fs::read_to_string(filename) { Ok(s) => s, - Err(e) => { emit_err("read_file", "IO", &format!("{}", e)); std::process::exit(1); } + Err(e) => { + emit_err("read_file", "IO", &format!("{}", e)); + std::process::exit(1); + } }; let ast = match NyashParser::parse_from_string(&code) { - Ok(a) => a, Err(e) => { emit_err("parse", "SYNTAX", &format!("{}", e)); std::process::exit(1); } + Ok(a) => a, + Err(e) => { + emit_err("parse", "SYNTAX", &format!("{}", e)); + std::process::exit(1); + } }; let mut mc = MirCompiler::new(); - let cr = match mc.compile(ast) { Ok(m) => m, Err(e) => { emit_err("mir", "MIR_COMPILE", &format!("{}", e)); std::process::exit(1); } }; - let func = match cr.module.functions.get("main") { Some(f) => f, None => { emit_err("mir", "NO_MAIN", "No main function found"); std::process::exit(1); } }; + let cr = match mc.compile(ast) { + Ok(m) => m, + Err(e) => { + emit_err("mir", "MIR_COMPILE", &format!("{}", e)); + std::process::exit(1); + } + }; + let func = match cr.module.functions.get("main") { + Some(f) => f, + None => { + emit_err("mir", "NO_MAIN", "No main function found"); + std::process::exit(1); + } + }; // Guard: refuse write-effects in jit-direct when policy.read_only { @@ -393,11 +510,20 @@ impl NyashRunner { } } if let Some(term) = &bb.terminator { - if term.effects().contains(Effect::WriteHeap) { writes += 1; } + if term.effects().contains(Effect::WriteHeap) { + writes += 1; + } } } if policy.read_only && writes > 0 { - emit_err("policy", "WRITE_EFFECTS", &format!("write-effects detected ({} ops). jit-direct is read-only at this stage.", writes)); + emit_err( + "policy", + "WRITE_EFFECTS", + &format!( + "write-effects detected ({} ops). jit-direct is read-only at this stage.", + writes + ), + ); std::process::exit(1); } } @@ -417,63 +543,141 @@ impl NyashRunner { match engine.compile_function("main", func) { Some(h) => { // Optional event: compile - nyash_rust::jit::events::emit("compile", &func.signature.name, Some(h), None, serde_json::json!({})); + nyash_rust::jit::events::emit( + "compile", + &func.signature.name, + Some(h), + None, + serde_json::json!({}), + ); // Parse JIT args from env: NYASH_JIT_ARGS (comma-separated), with optional type prefixes // Formats per arg: i:123, f:3.14, b:true/false, h:42 (handle), or bare numbers (int), true/false (bool) let mut jit_args: Vec = Vec::new(); if let Ok(s) = std::env::var("NYASH_JIT_ARGS") { for raw in s.split(',') { let t = raw.trim(); - if t.is_empty() { continue; } + if t.is_empty() { + continue; + } let v = if let Some(rest) = t.strip_prefix("i:") { - rest.parse::().ok().map(nyash_rust::jit::abi::JitValue::I64) + rest.parse::() + .ok() + .map(nyash_rust::jit::abi::JitValue::I64) } else if let Some(rest) = t.strip_prefix("f:") { - rest.parse::().ok().map(nyash_rust::jit::abi::JitValue::F64) + rest.parse::() + .ok() + .map(nyash_rust::jit::abi::JitValue::F64) } else if let Some(rest) = t.strip_prefix("b:") { - let b = matches!(rest, "1"|"true"|"True"|"TRUE"); + let b = matches!(rest, "1" | "true" | "True" | "TRUE"); Some(nyash_rust::jit::abi::JitValue::Bool(b)) } else if let Some(rest) = t.strip_prefix("h:") { - rest.parse::().ok().map(nyash_rust::jit::abi::JitValue::Handle) - } else if t.eq_ignore_ascii_case("true") || t == "1" { Some(nyash_rust::jit::abi::JitValue::Bool(true)) } - else if t.eq_ignore_ascii_case("false") || t == "0" { Some(nyash_rust::jit::abi::JitValue::Bool(false)) } - else if let Ok(iv) = t.parse::() { Some(nyash_rust::jit::abi::JitValue::I64(iv)) } - else if let Ok(fv) = t.parse::() { Some(nyash_rust::jit::abi::JitValue::F64(fv)) } - else { None }; - if let Some(jv) = v { jit_args.push(jv); } + rest.parse::() + .ok() + .map(nyash_rust::jit::abi::JitValue::Handle) + } else if t.eq_ignore_ascii_case("true") || t == "1" { + Some(nyash_rust::jit::abi::JitValue::Bool(true)) + } else if t.eq_ignore_ascii_case("false") || t == "0" { + Some(nyash_rust::jit::abi::JitValue::Bool(false)) + } else if let Ok(iv) = t.parse::() { + Some(nyash_rust::jit::abi::JitValue::I64(iv)) + } else if let Ok(fv) = t.parse::() { + Some(nyash_rust::jit::abi::JitValue::F64(fv)) + } else { + None + }; + if let Some(jv) = v { + jit_args.push(jv); + } } } // Coerce args to expected MIR types use nyash_rust::mir::MirType; let expected = &func.signature.params; if expected.len() != jit_args.len() { - emit_err("args", "COUNT_MISMATCH", &format!("expected={}, passed={}", expected.len(), jit_args.len())); + emit_err( + "args", + "COUNT_MISMATCH", + &format!("expected={}, passed={}", expected.len(), jit_args.len()), + ); eprintln!("Hint: set NYASH_JIT_ARGS as comma-separated values, e.g., i:42,f:3.14,b:true"); std::process::exit(1); } - let mut coerced: Vec = Vec::with_capacity(jit_args.len()); + let mut coerced: Vec = + Vec::with_capacity(jit_args.len()); for (i, (exp, got)) in expected.iter().zip(jit_args.iter()).enumerate() { let cv = match exp { MirType::Integer => match got { - nyash_rust::jit::abi::JitValue::I64(v) => nyash_rust::jit::abi::JitValue::I64(*v), - nyash_rust::jit::abi::JitValue::F64(f) => nyash_rust::jit::abi::JitValue::I64(*f as i64), - nyash_rust::jit::abi::JitValue::Bool(b) => nyash_rust::jit::abi::JitValue::I64(if *b {1} else {0}), - _ => { emit_err("args", "TYPE_MISMATCH", &format!("param#{} expects Integer", i)); std::process::exit(1); } + nyash_rust::jit::abi::JitValue::I64(v) => { + nyash_rust::jit::abi::JitValue::I64(*v) + } + nyash_rust::jit::abi::JitValue::F64(f) => { + nyash_rust::jit::abi::JitValue::I64(*f as i64) + } + nyash_rust::jit::abi::JitValue::Bool(b) => { + nyash_rust::jit::abi::JitValue::I64(if *b { 1 } else { 0 }) + } + _ => { + emit_err( + "args", + "TYPE_MISMATCH", + &format!("param#{} expects Integer", i), + ); + std::process::exit(1); + } }, MirType::Float => match got { - nyash_rust::jit::abi::JitValue::F64(f) => nyash_rust::jit::abi::JitValue::F64(*f), - nyash_rust::jit::abi::JitValue::I64(v) => nyash_rust::jit::abi::JitValue::F64(*v as f64), - nyash_rust::jit::abi::JitValue::Bool(b) => nyash_rust::jit::abi::JitValue::F64(if *b {1.0} else {0.0}), - _ => { emit_err("args", "TYPE_MISMATCH", &format!("param#{} expects Float", i)); std::process::exit(1); } + nyash_rust::jit::abi::JitValue::F64(f) => { + nyash_rust::jit::abi::JitValue::F64(*f) + } + nyash_rust::jit::abi::JitValue::I64(v) => { + nyash_rust::jit::abi::JitValue::F64(*v as f64) + } + nyash_rust::jit::abi::JitValue::Bool(b) => { + nyash_rust::jit::abi::JitValue::F64(if *b { 1.0 } else { 0.0 }) + } + _ => { + emit_err( + "args", + "TYPE_MISMATCH", + &format!("param#{} expects Float", i), + ); + std::process::exit(1); + } }, MirType::Bool => match got { - nyash_rust::jit::abi::JitValue::Bool(b) => nyash_rust::jit::abi::JitValue::Bool(*b), - nyash_rust::jit::abi::JitValue::I64(v) => nyash_rust::jit::abi::JitValue::Bool(*v != 0), - nyash_rust::jit::abi::JitValue::F64(f) => nyash_rust::jit::abi::JitValue::Bool(*f != 0.0), - _ => { emit_err("args", "TYPE_MISMATCH", &format!("param#{} expects Bool", i)); std::process::exit(1); } + nyash_rust::jit::abi::JitValue::Bool(b) => { + nyash_rust::jit::abi::JitValue::Bool(*b) + } + nyash_rust::jit::abi::JitValue::I64(v) => { + nyash_rust::jit::abi::JitValue::Bool(*v != 0) + } + nyash_rust::jit::abi::JitValue::F64(f) => { + nyash_rust::jit::abi::JitValue::Bool(*f != 0.0) + } + _ => { + emit_err( + "args", + "TYPE_MISMATCH", + &format!("param#{} expects Bool", i), + ); + std::process::exit(1); + } }, - MirType::String | MirType::Box(_) | MirType::Array(_) | MirType::Future(_) => match got { - nyash_rust::jit::abi::JitValue::Handle(h) => nyash_rust::jit::abi::JitValue::Handle(*h), - _ => { emit_err("args", "TYPE_MISMATCH", &format!("param#{} expects handle (h:)", i)); std::process::exit(1); } + MirType::String + | MirType::Box(_) + | MirType::Array(_) + | MirType::Future(_) => match got { + nyash_rust::jit::abi::JitValue::Handle(h) => { + nyash_rust::jit::abi::JitValue::Handle(*h) + } + _ => { + emit_err( + "args", + "TYPE_MISMATCH", + &format!("param#{} expects handle (h:)", i), + ); + std::process::exit(1); + } }, MirType::Void | MirType::Unknown => { // Keep as-is @@ -488,32 +692,66 @@ impl NyashRunner { match out { Some(v) => { let ms = t0.elapsed().as_millis(); - nyash_rust::jit::events::emit("execute", &func.signature.name, Some(h), Some(ms), serde_json::json!({})); + nyash_rust::jit::events::emit( + "execute", + &func.signature.name, + Some(h), + Some(ms), + serde_json::json!({}), + ); // Normalize result according to MIR return type for friendly output use nyash_rust::mir::MirType; let ret_ty = &func.signature.return_type; let vmv = match (ret_ty, v) { - (MirType::Bool, nyash_rust::jit::abi::JitValue::I64(i)) => nyash_rust::backend::vm::VMValue::Bool(i != 0), - (MirType::Bool, nyash_rust::jit::abi::JitValue::Bool(b)) => nyash_rust::backend::vm::VMValue::Bool(b), - (MirType::Float, nyash_rust::jit::abi::JitValue::F64(f)) => nyash_rust::backend::vm::VMValue::Float(f), - (MirType::Float, nyash_rust::jit::abi::JitValue::I64(i)) => nyash_rust::backend::vm::VMValue::Float(i as f64), + (MirType::Bool, nyash_rust::jit::abi::JitValue::I64(i)) => { + nyash_rust::backend::vm::VMValue::Bool(i != 0) + } + (MirType::Bool, nyash_rust::jit::abi::JitValue::Bool(b)) => { + nyash_rust::backend::vm::VMValue::Bool(b) + } + (MirType::Float, nyash_rust::jit::abi::JitValue::F64(f)) => { + nyash_rust::backend::vm::VMValue::Float(f) + } + (MirType::Float, nyash_rust::jit::abi::JitValue::I64(i)) => { + nyash_rust::backend::vm::VMValue::Float(i as f64) + } // Default adapter for other combos _ => nyash_rust::jit::abi::adapter::from_jit_value(v), }; println!("✅ JIT-direct execution completed successfully!"); // Pretty print with expected type tag let (ety, sval) = match (ret_ty, &vmv) { - (MirType::Bool, nyash_rust::backend::vm::VMValue::Bool(b)) => ("Bool", b.to_string()), - (MirType::Float, nyash_rust::backend::vm::VMValue::Float(f)) => ("Float", format!("{}", f)), - (MirType::Integer, nyash_rust::backend::vm::VMValue::Integer(i)) => ("Integer", i.to_string()), + (MirType::Bool, nyash_rust::backend::vm::VMValue::Bool(b)) => { + ("Bool", b.to_string()) + } + (MirType::Float, nyash_rust::backend::vm::VMValue::Float(f)) => { + ("Float", format!("{}", f)) + } + (MirType::Integer, nyash_rust::backend::vm::VMValue::Integer(i)) => { + ("Integer", i.to_string()) + } // Fallbacks - (_, nyash_rust::backend::vm::VMValue::Integer(i)) => ("Integer", i.to_string()), - (_, nyash_rust::backend::vm::VMValue::Float(f)) => ("Float", format!("{}", f)), - (_, nyash_rust::backend::vm::VMValue::Bool(b)) => ("Bool", b.to_string()), - (_, nyash_rust::backend::vm::VMValue::String(s)) => ("String", s.clone()), - (_, nyash_rust::backend::vm::VMValue::BoxRef(arc)) => ("BoxRef", arc.type_name().to_string()), - (_, nyash_rust::backend::vm::VMValue::Future(_)) => ("Future", "".to_string()), - (_, nyash_rust::backend::vm::VMValue::Void) => ("Void", "void".to_string()), + (_, nyash_rust::backend::vm::VMValue::Integer(i)) => { + ("Integer", i.to_string()) + } + (_, nyash_rust::backend::vm::VMValue::Float(f)) => { + ("Float", format!("{}", f)) + } + (_, nyash_rust::backend::vm::VMValue::Bool(b)) => { + ("Bool", b.to_string()) + } + (_, nyash_rust::backend::vm::VMValue::String(s)) => { + ("String", s.clone()) + } + (_, nyash_rust::backend::vm::VMValue::BoxRef(arc)) => { + ("BoxRef", arc.type_name().to_string()) + } + (_, nyash_rust::backend::vm::VMValue::Future(_)) => { + ("Future", "".to_string()) + } + (_, nyash_rust::backend::vm::VMValue::Void) => { + ("Void", "void".to_string()) + } }; println!("ResultType(MIR): {}", ety); println!("Result: {}", sval); @@ -522,7 +760,11 @@ impl NyashRunner { let cfg = nyash_rust::jit::config::current(); let caps = nyash_rust::jit::config::probe_capabilities(); let (phi_t, phi_b1, ret_b) = engine.last_lower_stats(); - 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 payload = serde_json::json!({ "version": 1, "function": func.signature.name, @@ -539,14 +781,28 @@ impl NyashRunner { } } None => { - nyash_rust::jit::events::emit("fallback", &func.signature.name, Some(h), None, serde_json::json!({"reason":"trap_or_missing"})); - emit_err("execute", "TRAP_OR_MISSING", "execution failed (trap or missing handle)"); + nyash_rust::jit::events::emit( + "fallback", + &func.signature.name, + Some(h), + None, + serde_json::json!({"reason":"trap_or_missing"}), + ); + emit_err( + "execute", + "TRAP_OR_MISSING", + "execution failed (trap or missing handle)", + ); std::process::exit(1); } } } None => { - emit_err("compile", "UNAVAILABLE", "Build with --features cranelift-jit"); + emit_err( + "compile", + "UNAVAILABLE", + "Build with --features cranelift-jit", + ); std::process::exit(1); } } diff --git a/src/runner/modes/aot.rs b/src/runner/modes/aot.rs index 6d78eb94..64efc3eb 100644 --- a/src/runner/modes/aot.rs +++ b/src/runner/modes/aot.rs @@ -1,6 +1,6 @@ use super::super::NyashRunner; #[cfg(feature = "cranelift-jit")] -use std::{process::Command, process}; +use std::{process, process::Command}; impl NyashRunner { /// Execute AOT compilation mode (split) @@ -11,7 +11,16 @@ impl NyashRunner { let status = if cfg!(target_os = "windows") { // Use PowerShell helper; falls back to bash if available inside the script Command::new("powershell") - .args(["-ExecutionPolicy","Bypass","-File","tools/build_aot.ps1","-Input", filename, "-Out", &format!("{}.exe", output)]) + .args([ + "-ExecutionPolicy", + "Bypass", + "-File", + "tools/build_aot.ps1", + "-Input", + filename, + "-Out", + &format!("{}.exe", output), + ]) .status() } else { Command::new("bash") @@ -20,15 +29,24 @@ impl NyashRunner { }; match status { Ok(s) if s.success() => { - println!("✅ AOT compilation successful!\nExecutable written to: {}", output); + println!( + "✅ AOT compilation successful!\nExecutable written to: {}", + output + ); } Ok(s) => { - eprintln!("❌ AOT compilation failed (exit={} ). See logs above.", s.code().unwrap_or(-1)); + eprintln!( + "❌ AOT compilation failed (exit={} ). See logs above.", + s.code().unwrap_or(-1) + ); process::exit(1); } Err(e) => { eprintln!("❌ Failed to invoke build_aot.sh: {}", e); - eprintln!("Hint: ensure bash is available, or run: bash tools/build_aot.sh {} -o {}", filename, output); + eprintln!( + "Hint: ensure bash is available, or run: bash tools/build_aot.sh {} -o {}", + filename, output + ); process::exit(1); } } diff --git a/src/runner/modes/bench.rs b/src/runner/modes/bench.rs index 1aa82d68..c0f07a13 100644 --- a/src/runner/modes/bench.rs +++ b/src/runner/modes/bench.rs @@ -1,10 +1,15 @@ use super::super::NyashRunner; -use nyash_rust::{parser::NyashParser, interpreter::NyashInterpreter, mir::MirCompiler, backend::VM}; +use nyash_rust::{ + backend::VM, interpreter::NyashInterpreter, mir::MirCompiler, parser::NyashParser, +}; impl NyashRunner { /// Execute benchmark mode (split) pub(crate) fn execute_benchmark_mode(&self) { - println!("🏁 Running benchmark mode with {} iterations", self.config.iterations); + println!( + "🏁 Running benchmark mode with {} iterations", + self.config.iterations + ); // Tests: some run on all backends, some are JIT+f64 only // Third element indicates JIT+f64 only (skip VM/Interpreter) let tests: Vec<(&str, &str, bool)> = vec![ @@ -63,14 +68,21 @@ impl NyashRunner { println!("\n===================================="); println!("🧪 Test: {}", name); if jit_f64_only { - println!("(JIT+f64 only) Skipping VM/Interpreter; requires --features cranelift-jit"); + println!( + "(JIT+f64 only) Skipping VM/Interpreter; requires --features cranelift-jit" + ); // Warmup JIT let warmup = (self.config.iterations / 10).max(1); self.bench_jit(code, warmup); // Measured let jit_time = self.bench_jit(code, self.config.iterations); println!("\n📊 Performance Summary [{}]:", name); - println!(" JIT f64 ops: {} iters in {:?} ({:.2} ops/sec)", self.config.iterations, jit_time, self.config.iterations as f64 / jit_time.as_secs_f64()); + println!( + " JIT f64 ops: {} iters in {:?} ({:.2} ops/sec)", + self.config.iterations, + jit_time, + self.config.iterations as f64 / jit_time.as_secs_f64() + ); } else { // Quick correctness check across modes (golden): Interpreter vs VM vs VM+JIT if let Err(e) = self.verify_outputs_match(code) { @@ -93,8 +105,28 @@ impl NyashRunner { let vm_vs_interp = interpreter_time.as_secs_f64() / vm_time.as_secs_f64(); let jit_vs_vm = vm_time.as_secs_f64() / jit_time.as_secs_f64(); println!("\n📊 Performance Summary [{}]:", name); - println!(" VM is {:.2}x {} than Interpreter", if vm_vs_interp > 1.0 { vm_vs_interp } else { 1.0 / vm_vs_interp }, if vm_vs_interp > 1.0 { "faster" } else { "slower" }); - println!(" JIT is {:.2}x {} than VM (note: compile cost included)", if jit_vs_vm > 1.0 { jit_vs_vm } else { 1.0 / jit_vs_vm }, if jit_vs_vm > 1.0 { "faster" } else { "slower" }); + println!( + " VM is {:.2}x {} than Interpreter", + if vm_vs_interp > 1.0 { + vm_vs_interp + } else { + 1.0 / vm_vs_interp + }, + if vm_vs_interp > 1.0 { + "faster" + } else { + "slower" + } + ); + println!( + " JIT is {:.2}x {} than VM (note: compile cost included)", + if jit_vs_vm > 1.0 { + jit_vs_vm + } else { + 1.0 / jit_vs_vm + }, + if jit_vs_vm > 1.0 { "faster" } else { "slower" } + ); } } } @@ -110,7 +142,12 @@ impl NyashRunner { } } let elapsed = start.elapsed(); - println!(" ⚡ Interpreter: {} iters in {:?} ({:.2} ops/sec)", iters, elapsed, iters as f64 / elapsed.as_secs_f64()); + println!( + " ⚡ Interpreter: {} iters in {:?} ({:.2} ops/sec)", + iters, + elapsed, + iters as f64 / elapsed.as_secs_f64() + ); elapsed } @@ -126,7 +163,12 @@ impl NyashRunner { } } let elapsed = start.elapsed(); - println!(" 🚀 VM: {} iters in {:?} ({:.2} ops/sec)", iters, elapsed, iters as f64 / elapsed.as_secs_f64()); + println!( + " 🚀 VM: {} iters in {:?} ({:.2} ops/sec)", + iters, + elapsed, + iters as f64 / elapsed.as_secs_f64() + ); elapsed } @@ -134,8 +176,12 @@ impl NyashRunner { // Force JIT mode for this run std::env::set_var("NYASH_JIT_EXEC", "1"); std::env::set_var("NYASH_JIT_THRESHOLD", "1"); - if self.config.jit_stats { std::env::set_var("NYASH_JIT_STATS", "1"); } - if self.config.jit_stats_json { std::env::set_var("NYASH_JIT_STATS_JSON", "1"); } + if self.config.jit_stats { + std::env::set_var("NYASH_JIT_STATS", "1"); + } + if self.config.jit_stats_json { + std::env::set_var("NYASH_JIT_STATS_JSON", "1"); + } let start = std::time::Instant::now(); for _ in 0..iters { if let Ok(ast) = NyashParser::parse_from_string(code) { @@ -147,7 +193,12 @@ impl NyashRunner { } } let elapsed = start.elapsed(); - println!(" 🔥 JIT: {} iters in {:?} ({:.2} ops/sec)", iters, elapsed, iters as f64 / elapsed.as_secs_f64()); + println!( + " 🔥 JIT: {} iters in {:?} ({:.2} ops/sec)", + iters, + elapsed, + iters as f64 / elapsed.as_secs_f64() + ); elapsed } @@ -155,22 +206,28 @@ impl NyashRunner { fn verify_outputs_match(&self, code: &str) -> Result<(), String> { // VM let vm_out = { - let ast = NyashParser::parse_from_string(code).map_err(|e| format!("vm parse: {}", e))?; + let ast = + NyashParser::parse_from_string(code).map_err(|e| format!("vm parse: {}", e))?; let mut mc = MirCompiler::new(); let cr = mc.compile(ast).map_err(|e| format!("vm compile: {}", e))?; let mut vm = VM::new(); - let out = vm.execute_module(&cr.module).map_err(|e| format!("vm exec: {}", e))?; + let out = vm + .execute_module(&cr.module) + .map_err(|e| format!("vm exec: {}", e))?; out.to_string_box().value }; // VM+JIT let jit_out = { std::env::set_var("NYASH_JIT_EXEC", "1"); std::env::set_var("NYASH_JIT_THRESHOLD", "1"); - let ast = NyashParser::parse_from_string(code).map_err(|e| format!("jit parse: {}", e))?; + let ast = + NyashParser::parse_from_string(code).map_err(|e| format!("jit parse: {}", e))?; let mut mc = MirCompiler::new(); let cr = mc.compile(ast).map_err(|e| format!("jit compile: {}", e))?; let mut vm = VM::new(); - let out = vm.execute_module(&cr.module).map_err(|e| format!("jit exec: {}", e))?; + let out = vm + .execute_module(&cr.module) + .map_err(|e| format!("jit exec: {}", e))?; out.to_string_box().value }; if vm_out != jit_out { diff --git a/src/runner/modes/interpreter.rs b/src/runner/modes/interpreter.rs index 4a24638a..92c026d0 100644 --- a/src/runner/modes/interpreter.rs +++ b/src/runner/modes/interpreter.rs @@ -1,7 +1,7 @@ -use crate::parser::NyashParser; use crate::interpreter::NyashInterpreter; -use crate::runner_plugin_init; +use crate::parser::NyashParser; use crate::runner::pipeline::{resolve_using_target, UsingContext}; +use crate::runner_plugin_init; use std::{fs, process}; /// Execute Nyash file with interpreter @@ -22,11 +22,11 @@ pub fn execute_nyash_file(filename: &str, debug_fuel: Option) { println!("📝 File contents:\n{}", code); println!("\n🚀 Parsing and executing...\n"); - + // Test: immediate file creation (use relative path to avoid sandbox issues) std::fs::create_dir_all("development/debug_hang_issue").ok(); std::fs::write("development/debug_hang_issue/test.txt", "START").ok(); - + // Optional: using pre-processing (strip lines and register modules) let enable_using = std::env::var("NYASH_ENABLE_USING").ok().as_deref() == Some("1"); let mut code_ref: std::borrow::Cow<'_, str> = std::borrow::Cow::Borrowed(&code); @@ -39,13 +39,25 @@ pub fn execute_nyash_file(filename: &str, debug_fuel: Option) { let rest0 = t.strip_prefix("using ").unwrap().trim(); let rest0 = rest0.strip_suffix(';').unwrap_or(rest0).trim(); let (target, alias) = if let Some(pos) = rest0.find(" as ") { - (rest0[..pos].trim().to_string(), Some(rest0[pos+4..].trim().to_string())) - } else { (rest0.to_string(), None) }; - let is_path = target.starts_with('"') || target.starts_with("./") || target.starts_with('/') || target.ends_with(".nyash"); + ( + rest0[..pos].trim().to_string(), + Some(rest0[pos + 4..].trim().to_string()), + ) + } else { + (rest0.to_string(), None) + }; + let is_path = target.starts_with('"') + || target.starts_with("./") + || target.starts_with('/') + || target.ends_with(".nyash"); if is_path { let path = target.trim_matches('"').to_string(); let name = alias.clone().unwrap_or_else(|| { - std::path::Path::new(&path).file_stem().and_then(|s| s.to_str()).unwrap_or("module").to_string() + std::path::Path::new(&path) + .file_stem() + .and_then(|s| s.to_str()) + .unwrap_or("module") + .to_string() }); used_names.push((name, Some(path))); } else { @@ -57,7 +69,11 @@ pub fn execute_nyash_file(filename: &str, debug_fuel: Option) { out.push('\n'); } // Resolve and register - let using_ctx = UsingContext { using_paths: vec!["apps".into(), "lib".into(), ".".into()], pending_modules: vec![], aliases: std::collections::HashMap::new() }; + let using_ctx = UsingContext { + using_paths: vec!["apps".into(), "lib".into(), ".".into()], + pending_modules: vec![], + aliases: std::collections::HashMap::new(), + }; let strict = std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1"); let verbose = std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1"); let ctx_dir = std::path::Path::new(filename).parent(); @@ -66,12 +82,24 @@ pub fn execute_nyash_file(filename: &str, debug_fuel: Option) { let sb = crate::box_trait::StringBox::new(path); crate::runtime::modules_registry::set(ns_or_alias, Box::new(sb)); } else { - match resolve_using_target(&ns_or_alias, false, &using_ctx.pending_modules, &using_ctx.using_paths, &using_ctx.aliases, ctx_dir, strict, verbose) { + match resolve_using_target( + &ns_or_alias, + false, + &using_ctx.pending_modules, + &using_ctx.using_paths, + &using_ctx.aliases, + ctx_dir, + strict, + verbose, + ) { Ok(value) => { let sb = crate::box_trait::StringBox::new(value); crate::runtime::modules_registry::set(ns_or_alias, Box::new(sb)); } - Err(e) => { eprintln!("❌ using: {}", e); std::process::exit(1); } + Err(e) => { + eprintln!("❌ using: {}", e); + std::process::exit(1); + } } } } @@ -84,30 +112,30 @@ pub fn execute_nyash_file(filename: &str, debug_fuel: Option) { Ok(ast) => { eprintln!("🔍 DEBUG: Parse completed, AST created"); ast - }, + } Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); } }; - + eprintln!("🔍 DEBUG: About to print parse success message..."); println!("✅ Parse successful!"); eprintln!("🔍 DEBUG: Parse success message printed"); - + // Debug log file write if let Ok(mut file) = std::fs::OpenOptions::new() .create(true) .append(true) - .open("development/debug_hang_issue/debug_trace.log") + .open("development/debug_hang_issue/debug_trace.log") { use std::io::Write; let _ = writeln!(file, "=== MAIN: Parse successful ==="); let _ = file.flush(); } - + eprintln!("🔍 DEBUG: Creating interpreter..."); - + // Execute the AST let mut interpreter = NyashInterpreter::new(); eprintln!("🔍 DEBUG: Starting execution..."); @@ -116,9 +144,12 @@ pub fn execute_nyash_file(filename: &str, debug_fuel: Option) { println!("✅ Execution completed successfully!"); println!("Result: {}", result.to_string_box().value); // Structured concurrency: best-effort join of spawned tasks at program end - let join_ms: u64 = std::env::var("NYASH_JOIN_ALL_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(2000); + let join_ms: u64 = std::env::var("NYASH_JOIN_ALL_MS") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(2000); crate::runtime::global_hooks::join_all_registered_futures(join_ms); - }, + } Err(e) => { // Use enhanced error reporting with source context eprintln!("❌ Runtime error:\n{}", e.detailed_message(Some(&code))); diff --git a/src/runner/modes/llvm.rs b/src/runner/modes/llvm.rs index aff28937..1e233ec4 100644 --- a/src/runner/modes/llvm.rs +++ b/src/runner/modes/llvm.rs @@ -1,6 +1,9 @@ use super::super::NyashRunner; -use nyash_rust::{parser::NyashParser, mir::{MirCompiler, MirInstruction}}; use nyash_rust::mir::passes::method_id_inject::inject_method_ids; +use nyash_rust::{ + mir::{MirCompiler, MirInstruction}, + parser::NyashParser, +}; use std::{fs, process}; impl NyashRunner { @@ -12,20 +15,29 @@ impl NyashRunner { // Read the file let code = match fs::read_to_string(filename) { Ok(content) => content, - Err(e) => { eprintln!("❌ Error reading file {}: {}", filename, e); process::exit(1); } + Err(e) => { + eprintln!("❌ Error reading file {}: {}", filename, e); + process::exit(1); + } }; // Parse to AST let ast = match NyashParser::parse_from_string(&code) { Ok(ast) => ast, - Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); } + Err(e) => { + eprintln!("❌ Parse error: {}", e); + process::exit(1); + } }; // Compile to MIR let mut mir_compiler = MirCompiler::new(); let compile_result = match mir_compiler.compile(ast) { Ok(result) => result, - Err(e) => { eprintln!("❌ MIR compilation error: {}", e); process::exit(1); } + Err(e) => { + eprintln!("❌ MIR compilation error: {}", e); + process::exit(1); + } }; println!("📊 MIR Module compiled successfully!"); @@ -46,7 +58,9 @@ impl NyashRunner { // Harness path (optional): if NYASH_LLVM_USE_HARNESS=1, try Python/llvmlite first. let use_harness = crate::config::env::llvm_use_harness(); if use_harness { - if let Some(parent) = std::path::Path::new(&_out_path).parent() { let _ = std::fs::create_dir_all(parent); } + if let Some(parent) = std::path::Path::new(&_out_path).parent() { + let _ = std::fs::create_dir_all(parent); + } let py = which::which("python3").ok(); if let Some(py3) = py { let harness = std::path::Path::new("tools/llvmlite_harness.py"); @@ -55,34 +69,61 @@ impl NyashRunner { let tmp_dir = std::path::Path::new("tmp"); let _ = std::fs::create_dir_all(tmp_dir); let mir_json_path = tmp_dir.join("nyash_harness_mir.json"); - if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness(&module, &mir_json_path) { + if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness( + &module, + &mir_json_path, + ) { eprintln!("❌ MIR JSON emit error: {}", e); process::exit(1); } if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[Runner/LLVM] using llvmlite harness → {} (mir={})", _out_path, mir_json_path.display()); + eprintln!( + "[Runner/LLVM] using llvmlite harness → {} (mir={})", + _out_path, + mir_json_path.display() + ); } // 2) Run harness with --in/--out(失敗時は即エラー) let status = std::process::Command::new(py3) - .args([harness.to_string_lossy().as_ref(), "--in", &mir_json_path.display().to_string(), "--out", &_out_path]) - .status().map_err(|e| format!("spawn harness: {}", e)).unwrap(); + .args([ + harness.to_string_lossy().as_ref(), + "--in", + &mir_json_path.display().to_string(), + "--out", + &_out_path, + ]) + .status() + .map_err(|e| format!("spawn harness: {}", e)) + .unwrap(); if !status.success() { - eprintln!("❌ llvmlite harness failed (status={})", status.code().unwrap_or(-1)); + eprintln!( + "❌ llvmlite harness failed (status={})", + status.code().unwrap_or(-1) + ); process::exit(1); } // Verify - match std::fs::metadata(&_out_path) { - Ok(meta) if meta.len() > 0 => { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] object emitted by harness: {} ({} bytes)", _out_path, meta.len()); - } - return; - } - _ => { - eprintln!("❌ harness output not found or empty: {}", _out_path); - process::exit(1); + match std::fs::metadata(&_out_path) { + Ok(meta) if meta.len() > 0 => { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() + == Some("1") + { + eprintln!( + "[LLVM] object emitted by harness: {} ({} bytes)", + _out_path, + meta.len() + ); } + return; } + _ => { + eprintln!( + "❌ harness output not found or empty: {}", + _out_path + ); + process::exit(1); + } + } } else { eprintln!("❌ harness script not found: {}", harness.display()); process::exit(1); @@ -99,11 +140,18 @@ impl NyashRunner { process::exit(1); } if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] object emitted: {} ({} bytes)", _out_path, meta.len()); + eprintln!( + "[LLVM] object emitted: {} ({} bytes)", + _out_path, + meta.len() + ); } } Err(e) => { - eprintln!("❌ harness output not found after emit: {} ({})", _out_path, e); + eprintln!( + "❌ harness output not found after emit: {} ({})", + _out_path, e + ); process::exit(1); } } @@ -117,7 +165,13 @@ impl NyashRunner { let _ = std::fs::create_dir_all(parent); } if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[Runner/LLVM] emitting object to {} (cwd={})", _out_path, std::env::current_dir().map(|p| p.display().to_string()).unwrap_or_default()); + eprintln!( + "[Runner/LLVM] emitting object to {} (cwd={})", + _out_path, + std::env::current_dir() + .map(|p| p.display().to_string()) + .unwrap_or_default() + ); } if let Err(e) = llvm_compile_to_object(&module, &_out_path) { eprintln!("❌ LLVM object emit error: {}", e); @@ -126,10 +180,17 @@ impl NyashRunner { match std::fs::metadata(&_out_path) { Ok(meta) if meta.len() > 0 => { if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] object emitted: {} ({} bytes)", _out_path, meta.len()); + eprintln!( + "[LLVM] object emitted: {} ({} bytes)", + _out_path, + meta.len() + ); } } - _ => { eprintln!("❌ LLVM object not found or empty: {}", _out_path); process::exit(1); } + _ => { + eprintln!("❌ LLVM object not found or empty: {}", _out_path); + process::exit(1); + } } return; } @@ -156,8 +217,11 @@ impl NyashRunner { println!("✅ LLVM execution completed (non-integer result)!"); println!("📊 Result: {}", result.to_string_box().value); } - }, - Err(e) => { eprintln!("❌ LLVM execution error: {}", e); process::exit(1); } + } + Err(e) => { + eprintln!("❌ LLVM execution error: {}", e); + process::exit(1); + } } } #[cfg(all(not(feature = "llvm-inkwell-legacy")))] @@ -168,8 +232,14 @@ impl NyashRunner { for (_bid, block) in &main_func.blocks { for inst in &block.instructions { match inst { - MirInstruction::Return { value: Some(_) } => { println!("✅ Mock exit code: 42"); process::exit(42); } - MirInstruction::Return { value: None } => { println!("✅ Mock exit code: 0"); process::exit(0); } + MirInstruction::Return { value: Some(_) } => { + println!("✅ Mock exit code: 42"); + process::exit(42); + } + MirInstruction::Return { value: None } => { + println!("✅ Mock exit code: 0"); + process::exit(0); + } _ => {} } } diff --git a/src/runner/modes/mir.rs b/src/runner/modes/mir.rs index 4340d015..b60b36ec 100644 --- a/src/runner/modes/mir.rs +++ b/src/runner/modes/mir.rs @@ -1,5 +1,8 @@ use super::super::NyashRunner; -use nyash_rust::{parser::NyashParser, mir::{MirCompiler, MirPrinter}}; +use nyash_rust::{ + mir::{MirCompiler, MirPrinter}, + parser::NyashParser, +}; use std::{fs, process}; impl NyashRunner { @@ -8,20 +11,29 @@ impl NyashRunner { // Read the file let code = match fs::read_to_string(filename) { Ok(content) => content, - Err(e) => { eprintln!("❌ Error reading file {}: {}", filename, e); process::exit(1); } + Err(e) => { + eprintln!("❌ Error reading file {}: {}", filename, e); + process::exit(1); + } }; // Parse to AST let ast = match NyashParser::parse_from_string(&code) { Ok(ast) => ast, - Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); } + Err(e) => { + eprintln!("❌ Parse error: {}", e); + process::exit(1); + } }; // Compile to MIR (opt passes configurable) let mut mir_compiler = MirCompiler::with_options(!self.config.no_optimize); let compile_result = match mir_compiler.compile(ast) { Ok(result) => result, - Err(e) => { eprintln!("❌ MIR compilation error: {}", e); process::exit(1); } + Err(e) => { + eprintln!("❌ MIR compilation error: {}", e); + process::exit(1); + } }; // Verify MIR if requested @@ -31,7 +43,9 @@ impl NyashRunner { Ok(()) => println!("✅ MIR verification passed!"), Err(errors) => { eprintln!("❌ MIR verification failed:"); - for error in errors { eprintln!(" • {}", error); } + for error in errors { + eprintln!(" • {}", error); + } process::exit(1); } } @@ -39,11 +53,16 @@ impl NyashRunner { // Dump MIR if requested if self.config.dump_mir { - let mut printer = if self.config.mir_verbose { MirPrinter::verbose() } else { MirPrinter::new() }; - if self.config.mir_verbose_effects { printer.set_show_effects_inline(true); } + let mut printer = if self.config.mir_verbose { + MirPrinter::verbose() + } else { + MirPrinter::new() + }; + if self.config.mir_verbose_effects { + printer.set_show_effects_inline(true); + } println!("🚀 MIR Output for {}:", filename); println!("{}", printer.print_module(&compile_result.module)); } } } - diff --git a/src/runner/modes/mod.rs b/src/runner/modes/mod.rs index 612fd5f4..1c416642 100644 --- a/src/runner/modes/mod.rs +++ b/src/runner/modes/mod.rs @@ -1,8 +1,8 @@ +pub mod bench; pub mod interpreter; +pub mod llvm; pub mod mir; pub mod vm; -pub mod llvm; -pub mod bench; #[cfg(feature = "cranelift-jit")] pub mod aot; diff --git a/src/runner/modes/vm.rs b/src/runner/modes/vm.rs index 67fcd3c8..58be5833 100644 --- a/src/runner/modes/vm.rs +++ b/src/runner/modes/vm.rs @@ -1,7 +1,16 @@ use super::super::NyashRunner; -use nyash_rust::{parser::NyashParser, mir::MirCompiler, backend::VM, runtime::{NyashRuntime, NyashRuntimeBuilder}, ast::ASTNode, core::model::BoxDeclaration as CoreBoxDecl, interpreter::SharedState, box_factory::user_defined::UserDefinedBoxFactory}; -use std::{fs, process}; +use nyash_rust::{ + ast::ASTNode, + backend::VM, + box_factory::user_defined::UserDefinedBoxFactory, + core::model::BoxDeclaration as CoreBoxDecl, + interpreter::SharedState, + mir::MirCompiler, + parser::NyashParser, + runtime::{NyashRuntime, NyashRuntimeBuilder}, +}; use std::sync::Arc; +use std::{fs, process}; impl NyashRunner { /// Execute VM mode (split) @@ -18,7 +27,9 @@ impl NyashRunner { // Init plugin host from nyash.toml if not yet loaded let need_init = { let host = nyash_rust::runtime::get_global_plugin_host(); - host.read().map(|h| h.config_ref().is_none()).unwrap_or(true) + host.read() + .map(|h| h.config_ref().is_none()) + .unwrap_or(true) }; if need_init { let _ = nyash_rust::runtime::init_global_plugin_host("nyash.toml"); @@ -29,16 +40,29 @@ impl NyashRunner { std::env::set_var("NYASH_USE_PLUGIN_BUILTINS", "1"); } // Build stable override list - let mut override_types: Vec = 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() - } else { vec![] }; + let mut override_types: Vec = + 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() + } else { + vec![] + }; for t in [ - "FileBox", "TOMLBox", // IO/config - "ConsoleBox", "StringBox", "IntegerBox", // core value-ish - "ArrayBox", "MapBox", // collections - "MathBox", "TimeBox" // math/time helpers + "FileBox", + "TOMLBox", // IO/config + "ConsoleBox", + "StringBox", + "IntegerBox", // core value-ish + "ArrayBox", + "MapBox", // collections + "MathBox", + "TimeBox", // math/time helpers ] { - if !override_types.iter().any(|x| x == t) { override_types.push(t.to_string()); } + if !override_types.iter().any(|x| x == t) { + override_types.push(t.to_string()); + } } std::env::set_var("NYASH_PLUGIN_OVERRIDE_TYPES", override_types.join(",")); @@ -46,11 +70,23 @@ impl NyashRunner { if std::env::var("NYASH_VM_PLUGIN_STRICT").ok().as_deref() == Some("1") { let v2 = nyash_rust::runtime::get_global_registry(); let mut missing: Vec = Vec::new(); - for t in ["FileBox","ConsoleBox","ArrayBox","MapBox","StringBox","IntegerBox"] { - if v2.get_provider(t).is_none() { missing.push(t.to_string()); } + for t in [ + "FileBox", + "ConsoleBox", + "ArrayBox", + "MapBox", + "StringBox", + "IntegerBox", + ] { + if v2.get_provider(t).is_none() { + missing.push(t.to_string()); + } } if !missing.is_empty() { - eprintln!("❌ VM plugin-first strict: missing providers for: {:?}", missing); + eprintln!( + "❌ VM plugin-first strict: missing providers for: {:?}", + missing + ); std::process::exit(1); } } @@ -59,13 +95,19 @@ impl NyashRunner { // Read the file let code = match fs::read_to_string(filename) { Ok(content) => content, - Err(e) => { eprintln!("❌ Error reading file {}: {}", filename, e); process::exit(1); } + Err(e) => { + eprintln!("❌ Error reading file {}: {}", filename, e); + process::exit(1); + } }; // Parse to AST let ast = match NyashParser::parse_from_string(&code) { Ok(ast) => ast, - Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); } + Err(e) => { + eprintln!("❌ Parse error: {}", e); + process::exit(1); + } }; // Prepare runtime and collect Box declarations for VM user-defined types @@ -80,7 +122,9 @@ impl NyashRunner { let mut shared = SharedState::new(); shared.box_declarations = rt.box_declarations.clone(); 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 }; @@ -88,20 +132,30 @@ impl NyashRunner { let mut mir_compiler = MirCompiler::with_options(!self.config.no_optimize); let compile_result = match mir_compiler.compile(ast) { Ok(result) => result, - Err(e) => { eprintln!("❌ MIR compilation error: {}", e); process::exit(1); } + Err(e) => { + eprintln!("❌ MIR compilation error: {}", e); + process::exit(1); + } }; // Optional: demo scheduling hook if std::env::var("NYASH_SCHED_DEMO").ok().as_deref() == Some("1") { if let Some(s) = &runtime.scheduler { // Immediate task - s.spawn("demo-immediate", Box::new(|| { - println!("[SCHED] immediate task ran at safepoint"); - })); + s.spawn( + "demo-immediate", + Box::new(|| { + println!("[SCHED] immediate task ran at safepoint"); + }), + ); // Delayed task - s.spawn_after(0, "demo-delayed", Box::new(|| { - println!("[SCHED] delayed task ran at safepoint"); - })); + s.spawn_after( + 0, + "demo-delayed", + Box::new(|| { + println!("[SCHED] delayed task ran at safepoint"); + }), + ); } } @@ -130,17 +184,28 @@ impl NyashRunner { let tmp_dir = std::path::Path::new("tmp"); let _ = std::fs::create_dir_all(tmp_dir); let mir_json_path = tmp_dir.join("nyash_pyvm_mir.json"); - if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness(&module_vm, &mir_json_path) { + if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness( + &module_vm, + &mir_json_path, + ) { eprintln!("❌ PyVM MIR JSON emit error: {}", e); process::exit(1); } if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[Runner/VM] using PyVM → {} (mir={})", filename, mir_json_path.display()); + eprintln!( + "[Runner/VM] using PyVM → {} (mir={})", + filename, + mir_json_path.display() + ); } // Determine entry function hint (prefer Main.main if present) let entry = if module_vm.functions.contains_key("Main.main") { "Main.main" - } else if module_vm.functions.contains_key("main") { "main" } else { "Main.main" }; + } else if module_vm.functions.contains_key("main") { + "main" + } else { + "Main.main" + }; // Spawn runner let status = std::process::Command::new(py3) .args([ @@ -178,20 +243,24 @@ impl NyashRunner { let mut vm = VM::with_runtime(runtime); match vm.execute_module(&module_vm) { Ok(result) => { - if !quiet_pipe { println!("✅ VM execution completed successfully!"); } + if !quiet_pipe { + println!("✅ VM execution completed successfully!"); + } // Pretty-print with coercions for plugin-backed values // Prefer MIR signature when available, but fall back to runtime coercions to keep VM/JIT consistent. let (ety, sval) = if let Some(func) = compile_result.module.functions.get("main") { - use nyash_rust::mir::MirType; - use nyash_rust::box_trait::{IntegerBox, BoolBox, StringBox}; + use nyash_rust::box_trait::{BoolBox, IntegerBox, StringBox}; use nyash_rust::boxes::FloatBox; + use nyash_rust::mir::MirType; match &func.signature.return_type { MirType::Float => { if let Some(fb) = result.as_any().downcast_ref::() { ("Float", format!("{}", fb.value)) } else if let Some(ib) = result.as_any().downcast_ref::() { ("Float", format!("{}", ib.value as f64)) - } else if let Some(s) = nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) { + } else if let Some(s) = + nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) + { ("String", s) } else { (result.type_name(), result.to_string_box().value) @@ -200,7 +269,9 @@ impl NyashRunner { MirType::Integer => { if let Some(ib) = result.as_any().downcast_ref::() { ("Integer", ib.value.to_string()) - } else if let Some(i) = nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref()) { + } else if let Some(i) = + nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref()) + { ("Integer", i.to_string()) } else { (result.type_name(), result.to_string_box().value) @@ -218,50 +289,72 @@ impl NyashRunner { MirType::String => { if let Some(sb) = result.as_any().downcast_ref::() { ("String", sb.value.clone()) - } else if let Some(s) = nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) { + } else if let Some(s) = + nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) + { ("String", s) } else { (result.type_name(), result.to_string_box().value) } } _ => { - if let Some(i) = nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref()) { + if let Some(i) = + nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref()) + { ("Integer", i.to_string()) - } else if let Some(s) = nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) { + } else if let Some(s) = + nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) + { ("String", s) - } else { (result.type_name(), result.to_string_box().value) } + } else { + (result.type_name(), result.to_string_box().value) + } } } } else { - if let Some(i) = nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref()) { + if let Some(i) = nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref()) + { ("Integer", i.to_string()) - } else if let Some(s) = nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) { + } else if let Some(s) = + nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) + { ("String", s) - } else { (result.type_name(), result.to_string_box().value) } + } else { + (result.type_name(), result.to_string_box().value) + } }; if !quiet_pipe { println!("ResultType(MIR): {}", ety); println!("Result: {}", sval); } - }, - Err(e) => { eprintln!("❌ VM execution error: {}", e); process::exit(1); } + } + Err(e) => { + eprintln!("❌ VM execution error: {}", e); + process::exit(1); + } } } /// Collect Box declarations from AST and register into runtime pub(crate) fn collect_box_declarations(&self, ast: &ASTNode, runtime: &NyashRuntime) { fn resolve_include_path(filename: &str) -> String { - if filename.starts_with("./") || filename.starts_with("../") { return filename.to_string(); } + if filename.starts_with("./") || filename.starts_with("../") { + return filename.to_string(); + } let parts: Vec<&str> = filename.splitn(2, '/').collect(); if parts.len() == 2 { - let root = parts[0]; let rest = parts[1]; + let root = parts[0]; + let rest = parts[1]; let cfg_path = "nyash.toml"; if let Ok(toml_str) = std::fs::read_to_string(cfg_path) { if let Ok(toml_val) = toml::from_str::(&toml_str) { if let Some(include) = toml_val.get("include") { if let Some(roots) = include.get("roots").and_then(|v| v.as_table()) { if let Some(base) = roots.get(root).and_then(|v| v.as_str()) { - let mut b = base.to_string(); if !b.ends_with('/') && !b.ends_with('\\') { b.push('/'); } + let mut b = base.to_string(); + if !b.ends_with('/') && !b.ends_with('\\') { + b.push('/'); + } return format!("{}{}", b, rest); } } @@ -274,10 +367,23 @@ impl NyashRunner { use std::collections::HashSet; - fn walk_with_state(node: &ASTNode, runtime: &NyashRuntime, stack: &mut Vec, visited: &mut HashSet) { + fn walk_with_state( + node: &ASTNode, + runtime: &NyashRuntime, + stack: &mut Vec, + visited: &mut HashSet, + ) { match node { - ASTNode::Program { statements, .. } => { for st in statements { walk_with_state(st, runtime, stack, visited); } } - ASTNode::FunctionDeclaration { body, .. } => { for st in body { walk_with_state(st, runtime, stack, visited); } } + ASTNode::Program { statements, .. } => { + for st in statements { + walk_with_state(st, runtime, stack, visited); + } + } + ASTNode::FunctionDeclaration { body, .. } => { + for st in body { + walk_with_state(st, runtime, stack, visited); + } + } ASTNode::Include { filename, .. } => { let mut path = resolve_include_path(filename); if std::path::Path::new(&path).is_dir() { @@ -305,42 +411,139 @@ impl NyashRunner { stack.pop(); } ASTNode::Assignment { target, value, .. } => { - walk_with_state(target, runtime, stack, visited); walk_with_state(value, runtime, stack, visited); + walk_with_state(target, runtime, stack, visited); + walk_with_state(value, runtime, stack, visited); } - ASTNode::Return { value, .. } => { if let Some(v) = value { walk_with_state(v, runtime, stack, visited); } } - ASTNode::Print { expression, .. } => { walk_with_state(expression, runtime, stack, visited); } - ASTNode::If { condition, then_body, else_body, .. } => { + ASTNode::Return { value, .. } => { + if let Some(v) = value { + walk_with_state(v, runtime, stack, visited); + } + } + ASTNode::Print { expression, .. } => { + walk_with_state(expression, runtime, stack, visited); + } + ASTNode::If { + condition, + then_body, + else_body, + .. + } => { walk_with_state(condition, runtime, stack, visited); - for st in then_body { walk_with_state(st, runtime, stack, visited); } - if let Some(eb) = else_body { for st in eb { walk_with_state(st, runtime, stack, visited); } } + for st in then_body { + walk_with_state(st, runtime, stack, visited); + } + if let Some(eb) = else_body { + for st in eb { + walk_with_state(st, runtime, stack, visited); + } + } } - ASTNode::Loop { condition, body, .. } => { - walk_with_state(condition, runtime, stack, visited); for st in body { walk_with_state(st, runtime, stack, visited); } + ASTNode::Loop { + condition, body, .. + } => { + walk_with_state(condition, runtime, stack, visited); + for st in body { + walk_with_state(st, runtime, stack, visited); + } } - ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => { - for st in try_body { walk_with_state(st, runtime, stack, visited); } - for cc in catch_clauses { for st in &cc.body { walk_with_state(st, runtime, stack, visited); } } - if let Some(fb) = finally_body { for st in fb { walk_with_state(st, runtime, stack, visited); } } + ASTNode::TryCatch { + try_body, + catch_clauses, + finally_body, + .. + } => { + for st in try_body { + walk_with_state(st, runtime, stack, visited); + } + for cc in catch_clauses { + for st in &cc.body { + walk_with_state(st, runtime, stack, visited); + } + } + if let Some(fb) = finally_body { + for st in fb { + walk_with_state(st, runtime, stack, visited); + } + } + } + ASTNode::Throw { expression, .. } => { + walk_with_state(expression, runtime, stack, visited); } - ASTNode::Throw { expression, .. } => { walk_with_state(expression, runtime, stack, visited); } ASTNode::Local { initial_values, .. } => { - for iv in initial_values { if let Some(v) = iv { walk_with_state(v, runtime, stack, visited); } } + for iv in initial_values { + if let Some(v) = iv { + walk_with_state(v, runtime, stack, visited); + } + } } ASTNode::Outbox { initial_values, .. } => { - for iv in initial_values { if let Some(v) = iv { walk_with_state(v, runtime, stack, visited); } } + for iv in initial_values { + if let Some(v) = iv { + walk_with_state(v, runtime, stack, visited); + } + } } - ASTNode::FunctionCall { arguments, .. } => { for a in arguments { walk_with_state(a, runtime, stack, visited); } } - ASTNode::MethodCall { object, arguments, .. } => { walk_with_state(object, runtime, stack, visited); for a in arguments { walk_with_state(a, runtime, stack, visited); } } - ASTNode::FieldAccess { object, .. } => { walk_with_state(object, runtime, stack, visited); } - ASTNode::New { arguments, .. } => { for a in arguments { walk_with_state(a, runtime, stack, visited); } } - ASTNode::BinaryOp { left, right, .. } => { walk_with_state(left, runtime, stack, visited); walk_with_state(right, runtime, stack, visited); } - ASTNode::UnaryOp { operand, .. } => { walk_with_state(operand, runtime, stack, visited); } - ASTNode::AwaitExpression { expression, .. } => { walk_with_state(expression, runtime, stack, visited); } - ASTNode::Arrow { sender, receiver, .. } => { walk_with_state(sender, runtime, stack, visited); walk_with_state(receiver, runtime, stack, visited); } - ASTNode::Nowait { expression, .. } => { walk_with_state(expression, runtime, stack, visited); } - ASTNode::BoxDeclaration { name, fields, public_fields, private_fields, methods, constructors, init_fields, weak_fields, is_interface, extends, implements, type_parameters, .. } => { - for (_mname, mnode) in methods { walk_with_state(mnode, runtime, stack, visited); } - for (_ckey, cnode) in constructors { walk_with_state(cnode, runtime, stack, visited); } + ASTNode::FunctionCall { arguments, .. } => { + for a in arguments { + walk_with_state(a, runtime, stack, visited); + } + } + ASTNode::MethodCall { + object, arguments, .. + } => { + walk_with_state(object, runtime, stack, visited); + for a in arguments { + walk_with_state(a, runtime, stack, visited); + } + } + ASTNode::FieldAccess { object, .. } => { + walk_with_state(object, runtime, stack, visited); + } + ASTNode::New { arguments, .. } => { + for a in arguments { + walk_with_state(a, runtime, stack, visited); + } + } + ASTNode::BinaryOp { left, right, .. } => { + walk_with_state(left, runtime, stack, visited); + walk_with_state(right, runtime, stack, visited); + } + ASTNode::UnaryOp { operand, .. } => { + walk_with_state(operand, runtime, stack, visited); + } + ASTNode::AwaitExpression { expression, .. } => { + walk_with_state(expression, runtime, stack, visited); + } + ASTNode::Arrow { + sender, receiver, .. + } => { + walk_with_state(sender, runtime, stack, visited); + walk_with_state(receiver, runtime, stack, visited); + } + ASTNode::Nowait { expression, .. } => { + walk_with_state(expression, runtime, stack, visited); + } + ASTNode::BoxDeclaration { + name, + fields, + public_fields, + private_fields, + methods, + constructors, + init_fields, + weak_fields, + is_interface, + extends, + implements, + type_parameters, + .. + } => { + for (_mname, mnode) in methods { + walk_with_state(mnode, runtime, stack, visited); + } + for (_ckey, cnode) in constructors { + walk_with_state(cnode, runtime, stack, visited); + } let decl = CoreBoxDecl { name: name.clone(), fields: fields.clone(), @@ -355,7 +558,9 @@ impl NyashRunner { implements: implements.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); + } } _ => {} } diff --git a/src/runner/pipe_io.rs b/src/runner/pipe_io.rs index f99183c4..ee14eb85 100644 --- a/src/runner/pipe_io.rs +++ b/src/runner/pipe_io.rs @@ -21,13 +21,17 @@ impl NyashRunner { let json = if let Some(path) = &self.config.json_file { match std::fs::read_to_string(path) { Ok(s) => s, - Err(e) => { eprintln!("❌ json-file read error: {}", e); std::process::exit(1); } + Err(e) => { + eprintln!("❌ json-file read error: {}", e); + std::process::exit(1); + } } } else { use std::io::Read; let mut buf = String::new(); if let Err(e) = std::io::stdin().read_to_string(&mut buf) { - eprintln!("❌ stdin read error: {}", e); std::process::exit(1); + eprintln!("❌ stdin read error: {}", e); + std::process::exit(1); } buf }; @@ -45,16 +49,27 @@ impl NyashRunner { let tmp_dir = std::path::Path::new("tmp"); let _ = std::fs::create_dir_all(tmp_dir); let mir_json_path = tmp_dir.join("nyash_pyvm_mir.json"); - if let Err(e) = super::mir_json_emit::emit_mir_json_for_harness_bin(&module, &mir_json_path) { + if let Err(e) = super::mir_json_emit::emit_mir_json_for_harness_bin( + &module, + &mir_json_path, + ) { eprintln!("❌ PyVM MIR JSON emit error: {}", e); std::process::exit(1); } if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[Bridge] using PyVM (pipe) → {}", mir_json_path.display()); + eprintln!( + "[Bridge] using PyVM (pipe) → {}", + mir_json_path.display() + ); } // Determine entry function hint (prefer Main.main if present) - let entry = if module.functions.contains_key("Main.main") { "Main.main" } - else if module.functions.contains_key("main") { "main" } else { "Main.main" }; + let entry = if module.functions.contains_key("Main.main") { + "Main.main" + } else if module.functions.contains_key("main") { + "main" + } else { + "Main.main" + }; let status = std::process::Command::new(py3) .args([ runner.to_string_lossy().as_ref(), diff --git a/src/runner/pipeline.rs b/src/runner/pipeline.rs index a4559c81..4b47c495 100644 --- a/src/runner/pipeline.rs +++ b/src/runner/pipeline.rs @@ -22,7 +22,8 @@ impl NyashRunner { pub(super) fn init_using_context(&self) -> UsingContext { let mut using_paths: Vec = Vec::new(); let mut pending_modules: Vec<(String, String)> = Vec::new(); - let mut aliases: std::collections::HashMap = std::collections::HashMap::new(); + let mut aliases: std::collections::HashMap = + std::collections::HashMap::new(); // Defaults using_paths.extend(["apps", "lib", "."].into_iter().map(|s| s.to_string())); @@ -43,7 +44,9 @@ impl NyashRunner { for p in paths_arr { if let Some(s) = p.as_str() { let s = s.trim(); - if !s.is_empty() { using_paths.push(s.to_string()); } + if !s.is_empty() { + using_paths.push(s.to_string()); + } } } } @@ -75,20 +78,29 @@ impl NyashRunner { if let Ok(p) = std::env::var("NYASH_USING_PATH") { for s in p.split(':') { let s = s.trim(); - if !s.is_empty() { using_paths.push(s.to_string()); } + if !s.is_empty() { + using_paths.push(s.to_string()); + } } } // Env aliases: comma-separated k=v pairs if let Ok(raw) = std::env::var("NYASH_ALIASES") { for ent in raw.split(',') { - if let Some((k,v)) = ent.split_once('=') { - let k = k.trim(); let v = v.trim(); - if !k.is_empty() && !v.is_empty() { aliases.insert(k.to_string(), v.to_string()); } + if let Some((k, v)) = ent.split_once('=') { + let k = k.trim(); + let v = v.trim(); + if !k.is_empty() && !v.is_empty() { + aliases.insert(k.to_string(), v.to_string()); + } } } } - UsingContext { using_paths, pending_modules, aliases } + UsingContext { + using_paths, + pending_modules, + aliases, + } } } @@ -96,19 +108,25 @@ impl NyashRunner { pub(super) fn suggest_in_base(base: &str, leaf: &str, out: &mut Vec) { use std::fs; fn walk(dir: &std::path::Path, leaf: &str, out: &mut Vec, depth: usize) { - if depth == 0 || out.len() >= 5 { return; } + if depth == 0 || out.len() >= 5 { + return; + } if let Ok(entries) = fs::read_dir(dir) { for e in entries.flatten() { let path = e.path(); if path.is_dir() { walk(&path, leaf, out, depth - 1); - if out.len() >= 5 { return; } + if out.len() >= 5 { + return; + } } else if let Some(ext) = path.extension().and_then(|s| s.to_str()) { if ext == "nyash" { if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) { if stem == leaf { out.push(path.to_string_lossy().to_string()); - if out.len() >= 5 { return; } + if out.len() >= 5 { + return; + } } } } @@ -132,17 +150,29 @@ pub(super) fn resolve_using_target( strict: bool, verbose: bool, ) -> Result { - if is_path { return Ok(tgt.to_string()); } + if is_path { + return Ok(tgt.to_string()); + } let trace = verbose || std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1"); let idx = super::box_index::get_box_index(); let mut strict_effective = strict || idx.plugins_require_prefix_global; - if std::env::var("NYASH_PLUGIN_REQUIRE_PREFIX").ok().as_deref() == Some("1") { strict_effective = true; } + if std::env::var("NYASH_PLUGIN_REQUIRE_PREFIX").ok().as_deref() == Some("1") { + strict_effective = true; + } let meta_for_target = idx.plugin_meta_by_box.get(tgt).cloned(); - let mut require_prefix_target = meta_for_target.as_ref().map(|m| m.require_prefix).unwrap_or(false); - if let Some(m) = &meta_for_target { if !m.expose_short_names { require_prefix_target = true; } } + let mut require_prefix_target = meta_for_target + .as_ref() + .map(|m| m.require_prefix) + .unwrap_or(false); + if let Some(m) = &meta_for_target { + if !m.expose_short_names { + require_prefix_target = true; + } + } let mut is_plugin_short = meta_for_target.is_some(); if !is_plugin_short { - is_plugin_short = idx.plugin_boxes.contains(tgt) || super::box_index::BoxIndex::is_known_plugin_short(tgt); + is_plugin_short = idx.plugin_boxes.contains(tgt) + || super::box_index::BoxIndex::is_known_plugin_short(tgt); } if (strict_effective || require_prefix_target) && is_plugin_short && !tgt.contains('.') { let mut msg = format!("plugin short name '{}' requires prefix", tgt); @@ -155,25 +185,40 @@ pub(super) fn resolve_using_target( } let key = { let base = context_dir.and_then(|p| p.to_str()).unwrap_or(""); - format!("{}|{}|{}|{}", tgt, base, strict as i32, using_paths.join(":")) + format!( + "{}|{}|{}|{}", + tgt, + base, + strict as i32, + using_paths.join(":") + ) }; if let Some(hit) = crate::runner::box_index::cache_get(&key) { - if trace { crate::runner::trace::log(format!("[using/cache] '{}' -> '{}'", tgt, hit)); } + if trace { + crate::runner::trace::log(format!("[using/cache] '{}' -> '{}'", tgt, hit)); + } return Ok(hit); } // Resolve aliases early (provided map) if let Some(v) = aliases.get(tgt) { - if trace { crate::runner::trace::log(format!("[using/resolve] alias '{}' -> '{}'", tgt, v)); } + if trace { + crate::runner::trace::log(format!("[using/resolve] alias '{}' -> '{}'", tgt, v)); + } crate::runner::box_index::cache_put(&key, v.clone()); return Ok(v.clone()); } // Also consult env aliases if let Ok(raw) = std::env::var("NYASH_ALIASES") { for ent in raw.split(',') { - if let Some((k,v)) = ent.split_once('=') { + if let Some((k, v)) = ent.split_once('=') { if k.trim() == tgt { let out = v.trim().to_string(); - if trace { crate::runner::trace::log(format!("[using/resolve] env-alias '{}' -> '{}'", tgt, out)); } + if trace { + crate::runner::trace::log(format!( + "[using/resolve] env-alias '{}' -> '{}'", + tgt, out + )); + } crate::runner::box_index::cache_put(&key, out.clone()); return Ok(out); } @@ -183,17 +228,26 @@ pub(super) fn resolve_using_target( // 1) modules mapping if let Some((_, p)) = modules.iter().find(|(n, _)| n == tgt) { let out = p.clone(); - if trace { crate::runner::trace::log(format!("[using/resolve] modules '{}' -> '{}'", tgt, out)); } + if trace { + crate::runner::trace::log(format!("[using/resolve] modules '{}' -> '{}'", tgt, out)); + } crate::runner::box_index::cache_put(&key, out.clone()); return Ok(out); } // 2) build candidate list: relative then using-paths let rel = tgt.replace('.', "/") + ".nyash"; let mut cand: Vec = Vec::new(); - if let Some(dir) = context_dir { let c = dir.join(&rel); if c.exists() { cand.push(c.to_string_lossy().to_string()); } } + if let Some(dir) = context_dir { + let c = dir.join(&rel); + if c.exists() { + cand.push(c.to_string_lossy().to_string()); + } + } for base in using_paths { let c = std::path::Path::new(base).join(&rel); - if c.exists() { cand.push(c.to_string_lossy().to_string()); } + if c.exists() { + cand.push(c.to_string_lossy().to_string()); + } } if cand.is_empty() { if trace { @@ -201,10 +255,17 @@ pub(super) fn resolve_using_target( let leaf = tgt.split('.').last().unwrap_or(tgt); let mut cands: Vec = Vec::new(); suggest_in_base("apps", leaf, &mut cands); - if cands.len() < 5 { suggest_in_base("lib", leaf, &mut cands); } - if cands.len() < 5 { suggest_in_base(".", leaf, &mut cands); } + if cands.len() < 5 { + suggest_in_base("lib", leaf, &mut cands); + } + if cands.len() < 5 { + suggest_in_base(".", leaf, &mut cands); + } if cands.is_empty() { - crate::runner::trace::log(format!("[using] unresolved '{}' (searched: rel+paths)", tgt)); + crate::runner::trace::log(format!( + "[using] unresolved '{}' (searched: rel+paths)", + tgt + )); } else { crate::runner::trace::log(format!( "[using] unresolved '{}' (searched: rel+paths) candidates: {}", @@ -219,7 +280,9 @@ pub(super) fn resolve_using_target( return Err(format!("ambiguous using '{}': {}", tgt, cand.join(", "))); } let out = cand.remove(0); - if trace { crate::runner::trace::log(format!("[using/resolve] '{}' -> '{}'", tgt, out)); } + if trace { + crate::runner::trace::log(format!("[using/resolve] '{}' -> '{}'", tgt, out)); + } crate::runner::box_index::cache_put(&key, out.clone()); Ok(out) } @@ -246,9 +309,17 @@ pub(super) fn lint_fields_top(code: &str, strict: bool, verbose: bool) -> Result if !in_box && trimmed.starts_with("box ") || trimmed.starts_with("static box ") { // capture name let mut name = String::new(); - let after = if let Some(rest) = trimmed.strip_prefix("static box ") { rest } else { trimmed.strip_prefix("box ").unwrap_or("") }; + let after = if let Some(rest) = trimmed.strip_prefix("static box ") { + rest + } else { + trimmed.strip_prefix("box ").unwrap_or("") + }; for ch in after.chars() { - if ch.is_alphanumeric() || ch == '_' { name.push(ch); } else { break; } + if ch.is_alphanumeric() || ch == '_' { + name.push(ch); + } else { + break; + } } // require K&R brace on same line to start tracking if opens > 0 { @@ -269,11 +340,29 @@ pub(super) fn lint_fields_top(code: &str, strict: bool, verbose: bool) -> Result // starts with identifier then '(' and later '{' let mut it = trimmed.chars(); let mut ident = String::new(); - while let Some(c) = it.next() { if c.is_whitespace() { continue; } if c.is_alphabetic() || c=='_' { ident.push(c); break; } else { break; } } - while let Some(c) = it.next() { if c.is_alphanumeric() || c=='_' { ident.push(c); } else { break; } } + while let Some(c) = it.next() { + if c.is_whitespace() { + continue; + } + if c.is_alphabetic() || c == '_' { + ident.push(c); + break; + } else { + break; + } + } + while let Some(c) = it.next() { + if c.is_alphanumeric() || c == '_' { + ident.push(c); + } else { + break; + } + } trimmed.contains('(') && trimmed.ends_with('{') && !ident.is_empty() }; - if is_method { seen_method = true; } + if is_method { + seen_method = true; + } // Detect field: ident ':' Type (rough heuristic) let is_field = { @@ -281,10 +370,22 @@ pub(super) fn lint_fields_top(code: &str, strict: bool, verbose: bool) -> Result if parts.len() == 2 { let lhs = parts[0].trim(); let rhs = parts[1].trim(); - let lhs_ok = !lhs.is_empty() && lhs.chars().next().map(|c| c.is_alphabetic() || c=='_').unwrap_or(false); - let rhs_ok = !rhs.is_empty() && rhs.chars().next().map(|c| c.is_alphabetic() || c=='_').unwrap_or(false); + let lhs_ok = !lhs.is_empty() + && lhs + .chars() + .next() + .map(|c| c.is_alphabetic() || c == '_') + .unwrap_or(false); + let rhs_ok = !rhs.is_empty() + && rhs + .chars() + .next() + .map(|c| c.is_alphabetic() || c == '_') + .unwrap_or(false); lhs_ok && rhs_ok && !trimmed.contains('(') && !trimmed.contains(')') - } else { false } + } else { + false + } }; if is_field && seen_method { violations.push((lno, trimmed.to_string(), cur_box.clone())); @@ -293,7 +394,10 @@ pub(super) fn lint_fields_top(code: &str, strict: bool, verbose: bool) -> Result } // Exit box when closing brace reduces depth below box_depth let post_brace = pre_brace + opens - closes; - if post_brace < box_depth { in_box = false; cur_box.clear(); } + if post_brace < box_depth { + in_box = false; + cur_box.clear(); + } } // Update brace after processing @@ -305,17 +409,30 @@ pub(super) fn lint_fields_top(code: &str, strict: bool, verbose: bool) -> Result } if strict { // Compose error message - let mut msg = String::from("Field declarations must appear at the top of box. Violations:\n"); + let mut msg = + String::from("Field declarations must appear at the top of box. Violations:\n"); for (lno, fld, bx) in violations.iter().take(10) { - msg.push_str(&format!(" line {} in box {}: '{}" , lno, if bx.is_empty(){""} else {bx}, fld)); + msg.push_str(&format!( + " line {} in box {}: '{}", + lno, + if bx.is_empty() { "" } else { bx }, + fld + )); msg.push_str("'\n"); } - if violations.len() > 10 { msg.push_str(&format!(" ... and {} more\n", violations.len()-10)); } + if violations.len() > 10 { + msg.push_str(&format!(" ... and {} more\n", violations.len() - 10)); + } return Err(msg); } if verbose || std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1") { for (lno, fld, bx) in violations { - eprintln!("[lint] fields-top: line {} in box {} -> {}", lno, if bx.is_empty(){""} else {&bx}, fld); + eprintln!( + "[lint] fields-top: line {} in box {} -> {}", + lno, + if bx.is_empty() { "" } else { &bx }, + fld + ); } } Ok(()) diff --git a/src/runner/selfhost.rs b/src/runner/selfhost.rs index ace17cb3..da993180 100644 --- a/src/runner/selfhost.rs +++ b/src/runner/selfhost.rs @@ -8,12 +8,12 @@ use super::*; -use nyash_rust::{parser::NyashParser, interpreter::NyashInterpreter}; -use std::{fs, process}; +use nyash_rust::{interpreter::NyashInterpreter, parser::NyashParser}; use std::io::Read; use std::process::Stdio; -use std::time::{Duration, Instant}; use std::thread::sleep; +use std::time::{Duration, Instant}; +use std::{fs, process}; impl NyashRunner { /// Selfhost (Ny -> JSON v0) pipeline: EXE/VM/Python フォールバック含む @@ -22,7 +22,10 @@ impl NyashRunner { // Read input source let code = match fs::read_to_string(filename) { Ok(c) => c, - Err(e) => { eprintln!("[ny-compiler] read error: {}", e); return false; } + Err(e) => { + eprintln!("[ny-compiler] read error: {}", e); + return false; + } }; // Optional Phase-15: strip `using` lines and register modules (same policy as execute_nyash_file) let enable_using = crate::config::env::enable_using(); @@ -39,13 +42,25 @@ impl NyashRunner { let rest0 = t.strip_prefix("using ").unwrap().trim(); let rest0 = rest0.strip_suffix(';').unwrap_or(rest0).trim(); let (target, alias) = if let Some(pos) = rest0.find(" as ") { - (rest0[..pos].trim().to_string(), Some(rest0[pos+4..].trim().to_string())) - } else { (rest0.to_string(), None) }; - let is_path = target.starts_with('"') || target.starts_with("./") || target.starts_with('/') || target.ends_with(".nyash"); + ( + rest0[..pos].trim().to_string(), + Some(rest0[pos + 4..].trim().to_string()), + ) + } else { + (rest0.to_string(), None) + }; + let is_path = target.starts_with('"') + || target.starts_with("./") + || target.starts_with('/') + || target.ends_with(".nyash"); if is_path { let path = target.trim_matches('"').to_string(); let name = alias.clone().unwrap_or_else(|| { - std::path::Path::new(&path).file_stem().and_then(|s| s.to_str()).unwrap_or("module").to_string() + std::path::Path::new(&path) + .file_stem() + .and_then(|s| s.to_str()) + .unwrap_or("module") + .to_string() }); used_names.push((name, Some(path))); } else { @@ -88,13 +103,17 @@ impl NyashRunner { return false; } } - Err(e) => { eprintln!("[ny-compiler] open tmp failed: {}", e); return false; } + Err(e) => { + eprintln!("[ny-compiler] open tmp failed: {}", e); + return false; + } } } // Preferred: run Ny selfhost compiler program (apps/selfhost-compiler/compiler.nyash) // This avoids inline embedding pitfalls and supports Stage-3 gating via args. { - let exe = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash")); + let exe = std::env::current_exe() + .unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash")); let parser_prog = std::path::Path::new("apps/selfhost-compiler/compiler.nyash"); if parser_prog.exists() { let mut cmd = std::process::Command::new(&exe); @@ -113,7 +132,9 @@ impl NyashRunner { cmd.env_remove("NYASH_CLI_VERBOSE"); cmd.env("NYASH_JSON_ONLY", "1"); let timeout_ms: u64 = crate::config::env::ny_compiler_timeout_ms(); - let mut cmd = cmd.stdout(std::process::Stdio::piped()).stderr(std::process::Stdio::piped()); + let mut cmd = cmd + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()); if let Ok(mut child) = cmd.spawn() { let mut ch_stdout = child.stdout.take(); let mut ch_stderr = child.stderr.take(); @@ -124,7 +145,10 @@ impl NyashRunner { Ok(Some(_)) => break, Ok(None) => { if start.elapsed() >= std::time::Duration::from_millis(timeout_ms) { - let _ = child.kill(); let _ = child.wait(); timed_out = true; break; + let _ = child.kill(); + let _ = child.wait(); + timed_out = true; + break; } std::thread::sleep(std::time::Duration::from_millis(10)); } @@ -133,21 +157,41 @@ impl NyashRunner { } let mut out_buf = Vec::new(); let mut err_buf = Vec::new(); - if let Some(mut s) = ch_stdout { let _ = s.read_to_end(&mut out_buf); } - if let Some(mut s) = ch_stderr { let _ = s.read_to_end(&mut err_buf); } + if let Some(mut s) = ch_stdout { + let _ = s.read_to_end(&mut out_buf); + } + if let Some(mut s) = ch_stderr { + let _ = s.read_to_end(&mut err_buf); + } if timed_out { - let head = String::from_utf8_lossy(&out_buf).chars().take(200).collect::(); - eprintln!("[ny-compiler] child timeout after {} ms; stdout(head)='{}'", timeout_ms, head.replace('\n', "\\n")); + let head = String::from_utf8_lossy(&out_buf) + .chars() + .take(200) + .collect::(); + eprintln!( + "[ny-compiler] child timeout after {} ms; stdout(head)='{}'", + timeout_ms, + head.replace('\n', "\\n") + ); } let stdout = String::from_utf8_lossy(&out_buf).to_string(); let mut json_line = String::new(); - for line in stdout.lines() { let t = line.trim(); if t.starts_with('{') && t.contains("\"version\"") && t.contains("\"kind\"") { json_line = t.to_string(); break; } } + for line in stdout.lines() { + let t = line.trim(); + if t.starts_with('{') && t.contains("\"version\"") && t.contains("\"kind\"") + { + json_line = t.to_string(); + break; + } + } if !json_line.is_empty() { match super::json_v0_bridge::parse_json_v0_to_module(&json_line) { Ok(module) => { super::json_v0_bridge::maybe_dump_mir(&module); let emit_only = crate::config::env::ny_compiler_emit_only(); - if emit_only { return false; } + if emit_only { + return false; + } // Prefer PyVM path when requested if crate::config::env::vm_use_py() { if let Ok(py3) = which::which("python3") { @@ -160,10 +204,25 @@ impl NyashRunner { eprintln!("❌ PyVM MIR JSON emit error: {}", e); std::process::exit(1); } - let entry = if module.functions.contains_key("Main.main") { "Main.main" } else if module.functions.contains_key("main") { "main" } else { "Main.main" }; + let entry = + if module.functions.contains_key("Main.main") { + "Main.main" + } else if module.functions.contains_key("main") { + "main" + } else { + "Main.main" + }; let status = std::process::Command::new(py3) - .args(["tools/pyvm_runner.py", "--in", &mir_json_path.display().to_string(), "--entry", entry]) - .status().map_err(|e| format!("spawn pyvm: {}", e)).unwrap(); + .args([ + "tools/pyvm_runner.py", + "--in", + &mir_json_path.display().to_string(), + "--entry", + entry, + ]) + .status() + .map_err(|e| format!("spawn pyvm: {}", e)) + .unwrap(); let code = status.code().unwrap_or(1); println!("Result: {}", code); std::process::exit(code); @@ -173,7 +232,9 @@ impl NyashRunner { self.execute_mir_module(&module); return true; } - Err(e) => { eprintln!("[ny-compiler] json parse error (child): {}", e); } + Err(e) => { + eprintln!("[ny-compiler] json parse error (child): {}", e); + } } } } @@ -182,56 +243,102 @@ impl NyashRunner { // Python MVP-first: prefer the lightweight harness to produce JSON v0 (unless skipped) if std::env::var("NYASH_NY_COMPILER_SKIP_PY").ok().as_deref() != Some("1") { - if let Ok(py3) = which::which("python3") { - let py = std::path::Path::new("tools/ny_parser_mvp.py"); - if py.exists() { - let mut cmd = std::process::Command::new(&py3); - cmd.arg(py).arg(&tmp_path); - let out = match cmd.output() { Ok(o) => o, Err(e) => { eprintln!("[ny-compiler] python harness failed to spawn: {}", e); return false; } }; - if out.status.success() { - if let Ok(line) = String::from_utf8(out.stdout).map(|s| s.lines().next().unwrap_or("").to_string()) { - if line.contains("\"version\"") && line.contains("\"kind\"") { - match super::json_v0_bridge::parse_json_v0_to_module(&line) { - Ok(module) => { - super::json_v0_bridge::maybe_dump_mir(&module); - let emit_only = std::env::var("NYASH_NY_COMPILER_EMIT_ONLY").unwrap_or_else(|_| "1".to_string()) == "1"; - if emit_only { return false; } - // Prefer PyVM for selfhost pipeline (parity reference) - if std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1") { - // Reuse the common PyVM runner path - let tmp_dir = std::path::Path::new("tmp"); - let _ = std::fs::create_dir_all(tmp_dir); - let mir_json_path = tmp_dir.join("nyash_pyvm_mir.json"); - if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(&module, &mir_json_path) { + if let Ok(py3) = which::which("python3") { + let py = std::path::Path::new("tools/ny_parser_mvp.py"); + if py.exists() { + let mut cmd = std::process::Command::new(&py3); + cmd.arg(py).arg(&tmp_path); + let out = match cmd.output() { + Ok(o) => o, + Err(e) => { + eprintln!("[ny-compiler] python harness failed to spawn: {}", e); + return false; + } + }; + if out.status.success() { + if let Ok(line) = String::from_utf8(out.stdout) + .map(|s| s.lines().next().unwrap_or("").to_string()) + { + if line.contains("\"version\"") && line.contains("\"kind\"") { + match super::json_v0_bridge::parse_json_v0_to_module(&line) { + Ok(module) => { + super::json_v0_bridge::maybe_dump_mir(&module); + let emit_only = + std::env::var("NYASH_NY_COMPILER_EMIT_ONLY") + .unwrap_or_else(|_| "1".to_string()) + == "1"; + if emit_only { + return false; + } + // Prefer PyVM for selfhost pipeline (parity reference) + if std::env::var("NYASH_VM_USE_PY").ok().as_deref() + == Some("1") + { + // Reuse the common PyVM runner path + let tmp_dir = std::path::Path::new("tmp"); + let _ = std::fs::create_dir_all(tmp_dir); + let mir_json_path = tmp_dir.join("nyash_pyvm_mir.json"); + if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(&module, &mir_json_path) { eprintln!("❌ PyVM MIR JSON emit error: {}", e); process::exit(1); } - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[Bridge] using PyVM (selfhost-py) → {}", mir_json_path.display()); - } - let entry = if module.functions.contains_key("Main.main") { "Main.main" } else if module.functions.contains_key("main") { "main" } else { "Main.main" }; - let status = std::process::Command::new(&py3) - .args(["tools/pyvm_runner.py", "--in", &mir_json_path.display().to_string(), "--entry", entry]) - .status().map_err(|e| format!("spawn pyvm: {}", e)).unwrap(); - let code = status.code().unwrap_or(1); - if !status.success() { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("❌ PyVM (selfhost-py) failed (status={})", code); + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() + == Some("1") + { + eprintln!( + "[Bridge] using PyVM (selfhost-py) → {}", + mir_json_path.display() + ); } + let entry = + if module.functions.contains_key("Main.main") { + "Main.main" + } else if module.functions.contains_key("main") { + "main" + } else { + "Main.main" + }; + let status = std::process::Command::new(&py3) + .args([ + "tools/pyvm_runner.py", + "--in", + &mir_json_path.display().to_string(), + "--entry", + entry, + ]) + .status() + .map_err(|e| format!("spawn pyvm: {}", e)) + .unwrap(); + let code = status.code().unwrap_or(1); + if !status.success() { + if std::env::var("NYASH_CLI_VERBOSE") + .ok() + .as_deref() + == Some("1") + { + eprintln!( + "❌ PyVM (selfhost-py) failed (status={})", + code + ); + } + } + println!("Result: {}", code); + std::process::exit(code); } - println!("Result: {}", code); - std::process::exit(code); + self.execute_mir_module(&module); + return true; + } + Err(e) => { + eprintln!("[ny-compiler] json parse error: {}", e); + return false; } - self.execute_mir_module(&module); - return true; } - Err(e) => { eprintln!("[ny-compiler] json parse error: {}", e); return false; } } } } } } - } } + } // EXE-first: if requested, try external parser EXE (nyash_compiler) if std::env::var("NYASH_USE_NY_COMPILER_EXE").ok().as_deref() == Some("1") { // Resolve parser EXE path @@ -240,56 +347,118 @@ impl NyashRunner { } else { let mut p = std::path::PathBuf::from("dist/nyash_compiler"); #[cfg(windows)] - { p.push("nyash_compiler.exe"); } + { + p.push("nyash_compiler.exe"); + } #[cfg(not(windows))] - { p.push("nyash_compiler"); } + { + p.push("nyash_compiler"); + } if !p.exists() { // Try PATH - if let Ok(w) = which::which("nyash_compiler") { w } else { p } - } else { p } + if let Ok(w) = which::which("nyash_compiler") { + w + } else { + p + } + } else { + p + } }; if exe_path.exists() { let mut cmd = std::process::Command::new(&exe_path); // Prefer passing the original filename directly (parser EXE accepts positional path) cmd.arg(filename); // Gates - if std::env::var("NYASH_NY_COMPILER_MIN_JSON").ok().as_deref() == Some("1") { cmd.arg("--min-json"); } - if std::env::var("NYASH_SELFHOST_READ_TMP").ok().as_deref() == Some("1") { cmd.arg("--read-tmp"); } - if std::env::var("NYASH_NY_COMPILER_STAGE3").ok().as_deref() == Some("1") { cmd.arg("--stage3"); } - if let Ok(raw) = std::env::var("NYASH_NY_COMPILER_CHILD_ARGS") { for tok in raw.split_whitespace() { cmd.arg(tok); } } - let timeout_ms: u64 = std::env::var("NYASH_NY_COMPILER_TIMEOUT_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(2000); + if std::env::var("NYASH_NY_COMPILER_MIN_JSON").ok().as_deref() == Some("1") { + cmd.arg("--min-json"); + } + if std::env::var("NYASH_SELFHOST_READ_TMP").ok().as_deref() == Some("1") { + cmd.arg("--read-tmp"); + } + if std::env::var("NYASH_NY_COMPILER_STAGE3").ok().as_deref() == Some("1") { + cmd.arg("--stage3"); + } + if let Ok(raw) = std::env::var("NYASH_NY_COMPILER_CHILD_ARGS") { + for tok in raw.split_whitespace() { + cmd.arg(tok); + } + } + let timeout_ms: u64 = std::env::var("NYASH_NY_COMPILER_TIMEOUT_MS") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(2000); let mut cmd = cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); - let mut child = match cmd.spawn() { Ok(c) => c, Err(e) => { eprintln!("[ny-compiler] exe spawn failed: {}", e); return false; } }; + let mut child = match cmd.spawn() { + Ok(c) => c, + Err(e) => { + eprintln!("[ny-compiler] exe spawn failed: {}", e); + return false; + } + }; let mut ch_stdout = child.stdout.take(); let mut ch_stderr = child.stderr.take(); let start = Instant::now(); let mut timed_out = false; loop { match child.try_wait() { - Ok(Some(_status)) => { break; } + Ok(Some(_status)) => { + break; + } Ok(None) => { - if start.elapsed() >= Duration::from_millis(timeout_ms) { let _ = child.kill(); let _ = child.wait(); timed_out = true; break; } + if start.elapsed() >= Duration::from_millis(timeout_ms) { + let _ = child.kill(); + let _ = child.wait(); + timed_out = true; + break; + } sleep(Duration::from_millis(10)); } - Err(e) => { eprintln!("[ny-compiler] exe wait error: {}", e); return false; } + Err(e) => { + eprintln!("[ny-compiler] exe wait error: {}", e); + return false; + } } } let mut out_buf = Vec::new(); let mut err_buf = Vec::new(); - if let Some(mut s) = ch_stdout { let _ = s.read_to_end(&mut out_buf); } - if let Some(mut s) = ch_stderr { let _ = s.read_to_end(&mut err_buf); } + if let Some(mut s) = ch_stdout { + let _ = s.read_to_end(&mut out_buf); + } + if let Some(mut s) = ch_stderr { + let _ = s.read_to_end(&mut err_buf); + } if timed_out { - let head = String::from_utf8_lossy(&out_buf).chars().take(200).collect::(); - eprintln!("[ny-compiler] exe timeout after {} ms; stdout(head)='{}'", timeout_ms, head.replace('\n', "\\n")); + let head = String::from_utf8_lossy(&out_buf) + .chars() + .take(200) + .collect::(); + eprintln!( + "[ny-compiler] exe timeout after {} ms; stdout(head)='{}'", + timeout_ms, + head.replace('\n', "\\n") + ); return false; } - let stdout = match String::from_utf8(out_buf) { Ok(s) => s, Err(_) => String::new() }; + let stdout = match String::from_utf8(out_buf) { + Ok(s) => s, + Err(_) => String::new(), + }; let mut json_line = String::new(); - for line in stdout.lines() { let t = line.trim(); if t.starts_with('{') && t.contains("\"version\"") && t.contains("\"kind\"") { json_line = t.to_string(); break; } } + for line in stdout.lines() { + let t = line.trim(); + if t.starts_with('{') && t.contains("\"version\"") && t.contains("\"kind\"") { + json_line = t.to_string(); + break; + } + } if json_line.is_empty() { if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { let head: String = stdout.chars().take(200).collect(); - let errh: String = String::from_utf8_lossy(&err_buf).chars().take(200).collect(); + let errh: String = String::from_utf8_lossy(&err_buf) + .chars() + .take(200) + .collect(); eprintln!("[ny-compiler] exe produced no JSON; stdout(head)='{}' stderr(head)='{}'", head.replace('\n', "\\n"), errh.replace('\n', "\\n")); } return false; @@ -299,12 +468,15 @@ impl NyashRunner { Ok(module) => { println!("🚀 Ny compiler EXE path (ny→json_v0) ON"); super::json_v0_bridge::maybe_dump_mir(&module); - let emit_only = std::env::var("NYASH_NY_COMPILER_EMIT_ONLY").unwrap_or_else(|_| "1".to_string()) == "1"; + let emit_only = std::env::var("NYASH_NY_COMPILER_EMIT_ONLY") + .unwrap_or_else(|_| "1".to_string()) + == "1"; if emit_only { return false; } else { // Prefer PyVM when requested (reference semantics), regardless of BoxCall presence - let prefer_pyvm = std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1"); + let prefer_pyvm = + std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1"); if prefer_pyvm { if let Ok(py3) = which::which("python3") { let runner = std::path::Path::new("tools/pyvm_runner.py"); @@ -316,17 +488,41 @@ impl NyashRunner { eprintln!("❌ PyVM MIR JSON emit error: {}", e); process::exit(1); } - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[Bridge] using PyVM (selfhost) → {}", mir_json_path.display()); + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() + == Some("1") + { + eprintln!( + "[Bridge] using PyVM (selfhost) → {}", + mir_json_path.display() + ); } - let entry = if module.functions.contains_key("Main.main") { "Main.main" } else if module.functions.contains_key("main") { "main" } else { "Main.main" }; + let entry = if module.functions.contains_key("Main.main") { + "Main.main" + } else if module.functions.contains_key("main") { + "main" + } else { + "Main.main" + }; let status = std::process::Command::new(py3) - .args(["tools/pyvm_runner.py", "--in", &mir_json_path.display().to_string(), "--entry", entry]) - .status().map_err(|e| format!("spawn pyvm: {}", e)).unwrap(); + .args([ + "tools/pyvm_runner.py", + "--in", + &mir_json_path.display().to_string(), + "--entry", + entry, + ]) + .status() + .map_err(|e| format!("spawn pyvm: {}", e)) + .unwrap(); let code = status.code().unwrap_or(1); if !status.success() { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("❌ PyVM (selfhost) failed (status={})", code); + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() + == Some("1") + { + eprintln!( + "❌ PyVM (selfhost) failed (status={})", + code + ); } } // Harmonize with interpreter path for smokes: print Result then exit code @@ -339,7 +535,10 @@ impl NyashRunner { return true; } } - Err(e) => { eprintln!("[ny-compiler] json parse error: {}", e); return false; } + Err(e) => { + eprintln!("[ny-compiler] json parse error: {}", e); + return false; + } } } } @@ -368,15 +567,25 @@ impl NyashRunner { eprintln!("[ny-compiler] write inline failed: {}", e); return false; } - let exe = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash")); + let exe = std::env::current_exe() + .unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash")); let mut cmd = std::process::Command::new(exe); cmd.arg("--backend").arg("vm").arg(&inline_path); cmd.env_remove("NYASH_USE_NY_COMPILER"); cmd.env_remove("NYASH_CLI_VERBOSE"); cmd.env("NYASH_JSON_ONLY", "1"); - let timeout_ms: u64 = std::env::var("NYASH_NY_COMPILER_TIMEOUT_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(2000); + let timeout_ms: u64 = std::env::var("NYASH_NY_COMPILER_TIMEOUT_MS") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(2000); let mut cmd = cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); - let mut child = match cmd.spawn() { Ok(c) => c, Err(e) => { eprintln!("[ny-compiler] spawn inline vm failed: {}", e); return false; } }; + let mut child = match cmd.spawn() { + Ok(c) => c, + Err(e) => { + eprintln!("[ny-compiler] spawn inline vm failed: {}", e); + return false; + } + }; let mut ch_stdout = child.stdout.take(); let mut ch_stderr = child.stderr.take(); let start = Instant::now(); @@ -386,39 +595,68 @@ impl NyashRunner { Ok(Some(_)) => break, Ok(None) => { if start.elapsed() >= Duration::from_millis(timeout_ms) { - let _ = child.kill(); let _ = child.wait(); timed_out = true; break; + let _ = child.kill(); + let _ = child.wait(); + timed_out = true; + break; } sleep(Duration::from_millis(10)); } - Err(e) => { eprintln!("[ny-compiler] inline wait error: {}", e); break; } + Err(e) => { + eprintln!("[ny-compiler] inline wait error: {}", e); + break; + } } } let mut out_buf = Vec::new(); - if let Some(mut s) = ch_stdout { let _ = s.read_to_end(&mut out_buf); } + if let Some(mut s) = ch_stdout { + let _ = s.read_to_end(&mut out_buf); + } if timed_out { - let head = String::from_utf8_lossy(&out_buf).chars().take(200).collect::(); - eprintln!("[ny-compiler] inline timeout after {} ms; stdout(head)='{}'", timeout_ms, head.replace('\n', "\\n")); + let head = String::from_utf8_lossy(&out_buf) + .chars() + .take(200) + .collect::(); + eprintln!( + "[ny-compiler] inline timeout after {} ms; stdout(head)='{}'", + timeout_ms, + head.replace('\n', "\\n") + ); } raw = String::from_utf8_lossy(&out_buf).to_string(); } let mut json_line = String::new(); for line in raw.lines() { let t = line.trim(); - if t.starts_with('{') && t.contains("\"version\"") && t.contains("\"kind\"") { json_line = t.to_string(); break; } + if t.starts_with('{') && t.contains("\"version\"") && t.contains("\"kind\"") { + json_line = t.to_string(); + break; + } + } + if json_line.is_empty() { + return false; } - if json_line.is_empty() { return false; } match super::json_v0_bridge::parse_json_v0_to_module(&json_line) { Ok(module) => { super::json_v0_bridge::maybe_dump_mir(&module); - let emit_only = std::env::var("NYASH_NY_COMPILER_EMIT_ONLY").unwrap_or_else(|_| "1".to_string()) == "1"; - if emit_only { return false; } + let emit_only = std::env::var("NYASH_NY_COMPILER_EMIT_ONLY") + .unwrap_or_else(|_| "1".to_string()) + == "1"; + if emit_only { + return false; + } // Phase-15 policy: when NYASH_VM_USE_PY=1, prefer PyVM as reference executor // regardless of BoxCall presence to ensure semantics parity (e.g., PHI merges). let prefer_pyvm = std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1"); // Backward compatibility: if not preferring PyVM explicitly, still auto-enable when BoxCalls exist. - let needs_pyvm = !prefer_pyvm && module.functions.values().any(|f| { - f.blocks.values().any(|bb| bb.instructions.iter().any(|inst| matches!(inst, crate::mir::MirInstruction::BoxCall { .. }))) - }); + let needs_pyvm = !prefer_pyvm + && module.functions.values().any(|f| { + f.blocks.values().any(|bb| { + bb.instructions.iter().any(|inst| { + matches!(inst, crate::mir::MirInstruction::BoxCall { .. }) + }) + }) + }); if prefer_pyvm || needs_pyvm { if let Ok(py3) = which::which("python3") { let runner = std::path::Path::new("tools/pyvm_runner.py"); @@ -426,22 +664,52 @@ impl NyashRunner { let tmp_dir = std::path::Path::new("tmp"); let _ = std::fs::create_dir_all(tmp_dir); let mir_json_path = tmp_dir.join("nyash_pyvm_mir.json"); - if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(&module, &mir_json_path) { + if let Err(e) = + crate::runner::mir_json_emit::emit_mir_json_for_harness_bin( + &module, + &mir_json_path, + ) + { eprintln!("❌ PyVM MIR JSON emit error: {}", e); process::exit(1); } if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - let mode = if prefer_pyvm { "selfhost" } else { "selfhost-fallback" }; - eprintln!("[Bridge] using PyVM ({}) → {}", mode, mir_json_path.display()); + let mode = if prefer_pyvm { + "selfhost" + } else { + "selfhost-fallback" + }; + eprintln!( + "[Bridge] using PyVM ({}) → {}", + mode, + mir_json_path.display() + ); } - let entry = if module.functions.contains_key("Main.main") { "Main.main" } else if module.functions.contains_key("main") { "main" } else { "Main.main" }; + let entry = if module.functions.contains_key("Main.main") { + "Main.main" + } else if module.functions.contains_key("main") { + "main" + } else { + "Main.main" + }; let status = std::process::Command::new(py3) - .args(["tools/pyvm_runner.py", "--in", &mir_json_path.display().to_string(), "--entry", entry]) - .status().map_err(|e| format!("spawn pyvm: {}", e)).unwrap(); + .args([ + "tools/pyvm_runner.py", + "--in", + &mir_json_path.display().to_string(), + "--entry", + entry, + ]) + .status() + .map_err(|e| format!("spawn pyvm: {}", e)) + .unwrap(); let code = status.code().unwrap_or(1); if !status.success() { if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("❌ PyVM (selfhost-fallback) failed (status={})", code); + eprintln!( + "❌ PyVM (selfhost-fallback) failed (status={})", + code + ); } } // Harmonize with interpreter path for smokes @@ -453,7 +721,10 @@ impl NyashRunner { self.execute_mir_module(&module); true } - Err(e) => { eprintln!("❌ JSON v0 bridge error: {}", e); false } + Err(e) => { + eprintln!("❌ JSON v0 bridge error: {}", e); + false + } } } } diff --git a/src/runner/tasks.rs b/src/runner/tasks.rs index ef4d03e5..45c0bc6f 100644 --- a/src/runner/tasks.rs +++ b/src/runner/tasks.rs @@ -3,26 +3,51 @@ use std::path::PathBuf; /// Minimal task runner: read nyash.toml [env] and [tasks], run the named task via shell pub(super) fn run_named_task(name: &str) -> Result<(), String> { let cfg_path = "nyash.toml"; - let text = std::fs::read_to_string(cfg_path).map_err(|e| format!("read {}: {}", cfg_path, e))?; - let doc = toml::from_str::(&text).map_err(|e| format!("parse {}: {}", cfg_path, e))?; + let text = + std::fs::read_to_string(cfg_path).map_err(|e| format!("read {}: {}", cfg_path, e))?; + let doc = + toml::from_str::(&text).map_err(|e| format!("parse {}: {}", cfg_path, e))?; // Apply [env] if let Some(env_tbl) = doc.get("env").and_then(|v| v.as_table()) { for (k, v) in env_tbl.iter() { - if let Some(s) = v.as_str() { std::env::set_var(k, s); } + if let Some(s) = v.as_str() { + std::env::set_var(k, s); + } } } // Lookup [tasks] - let tasks = doc.get("tasks").and_then(|v| v.as_table()).ok_or("[tasks] not found in nyash.toml")?; - let cmd = tasks.get(name).and_then(|v| v.as_str()).ok_or_else(|| format!("task '{}' not found", name))?; + let tasks = doc + .get("tasks") + .and_then(|v| v.as_table()) + .ok_or("[tasks] not found in nyash.toml")?; + let cmd = tasks + .get(name) + .and_then(|v| v.as_str()) + .ok_or_else(|| format!("task '{}' not found", name))?; // Basic variable substitution - let root = std::env::current_dir().unwrap_or(PathBuf::from(".")).display().to_string(); + let root = std::env::current_dir() + .unwrap_or(PathBuf::from(".")) + .display() + .to_string(); let cmd = cmd.replace("{root}", &root); // Run via shell #[cfg(windows)] - let status = std::process::Command::new("cmd").args(["/C", &cmd]).status().map_err(|e| e.to_string())?; + let status = std::process::Command::new("cmd") + .args(["/C", &cmd]) + .status() + .map_err(|e| e.to_string())?; #[cfg(not(windows))] - let status = std::process::Command::new("sh").arg("-lc").arg(&cmd).status().map_err(|e| e.to_string())?; - if !status.success() { return Err(format!("task '{}' failed with status {:?}", name, status.code())); } + let status = std::process::Command::new("sh") + .arg("-lc") + .arg(&cmd) + .status() + .map_err(|e| e.to_string())?; + if !status.success() { + return Err(format!( + "task '{}' failed with status {:?}", + name, + status.code() + )); + } Ok(()) } - diff --git a/src/runner/trace.rs b/src/runner/trace.rs index 64b454d6..c0e6fa23 100644 --- a/src/runner/trace.rs +++ b/src/runner/trace.rs @@ -1,7 +1,9 @@ //! Runner tracing helpers (verbose-guarded) /// Return whether CLI verbose logging is enabled -pub fn cli_verbose() -> bool { crate::config::env::cli_verbose() } +pub fn cli_verbose() -> bool { + crate::config::env::cli_verbose() +} #[macro_export] macro_rules! cli_v { @@ -11,4 +13,6 @@ macro_rules! cli_v { } /// Unstructured trace output function used by pipeline helpers -pub fn log>(msg: S) { eprintln!("{}", msg.as_ref()); } +pub fn log>(msg: S) { + eprintln!("{}", msg.as_ref()); +} diff --git a/src/runner_plugin_init.rs b/src/runner_plugin_init.rs index 8e1dd215..69590dc0 100644 --- a/src/runner_plugin_init.rs +++ b/src/runner_plugin_init.rs @@ -5,18 +5,25 @@ * Behavior: Quiet by default; use NYASH_CLI_VERBOSE=1 or NYASH_DEBUG_PLUGIN=1 for logs */ -use crate::runtime::{init_global_plugin_host, get_global_registry, get_global_plugin_host, PluginConfig}; +use crate::runtime::{ + get_global_plugin_host, get_global_registry, init_global_plugin_host, PluginConfig, +}; pub fn init_bid_plugins() { let cli_verbose = std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1"); let plugin_debug = std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1"); - if plugin_debug { eprintln!("🔍 DEBUG: Initializing v2 plugin system"); } + if plugin_debug { + eprintln!("🔍 DEBUG: Initializing v2 plugin system"); + } if let Ok(()) = init_global_plugin_host("nyash.toml") { if plugin_debug || cli_verbose { eprintln!("🔌 plugin host initialized from nyash.toml"); // Show which plugin loader backend compiled in (enabled/stub) - println!("[plugin-loader] backend={}", crate::runtime::plugin_loader_v2::backend_kind()); + println!( + "[plugin-loader] backend={}", + crate::runtime::plugin_loader_v2::backend_kind() + ); } let host = get_global_plugin_host(); let host = host.read().unwrap(); @@ -24,8 +31,12 @@ pub fn init_bid_plugins() { let registry = get_global_registry(); for (lib_name, lib_def) in &config.libraries { for box_name in &lib_def.boxes { - if plugin_debug { eprintln!(" 📦 Registering plugin provider for {}", box_name); } - registry.apply_plugin_config(&PluginConfig { plugins: [(box_name.clone(), lib_name.clone())].into(), }); + if plugin_debug { + eprintln!(" 📦 Registering plugin provider for {}", box_name); + } + registry.apply_plugin_config(&PluginConfig { + plugins: [(box_name.clone(), lib_name.clone())].into(), + }); } } if plugin_debug || cli_verbose { diff --git a/src/runtime/box_registry.rs b/src/runtime/box_registry.rs index 0cac06ab..cef0f3d6 100644 --- a/src/runtime/box_registry.rs +++ b/src/runtime/box_registry.rs @@ -1,5 +1,5 @@ //! Boxファクトリレジストリ - Box生成の中央管理 -//! +//! //! プラグインBoxを中心にBox生成を管理する(Plugin-First)。 //! 旧ビルトイン経路は互換目的のAPIとして最小限に保持(テスト用途)。 @@ -12,7 +12,7 @@ use std::sync::{Arc, RwLock}; pub enum BoxProvider { /// 互換用ビルトイン実装(Rust関数、現在は原則未使用) Builtin(BoxConstructor), - + /// プラグイン実装(プラグイン名を保持) Plugin(String), } @@ -33,36 +33,38 @@ impl BoxFactoryRegistry { providers: RwLock::new(HashMap::new()), } } - + /// 互換用ビルトインBoxを登録(通常は使用しない) pub fn register_builtin(&self, name: &str, constructor: BoxConstructor) { let mut providers = self.providers.write().unwrap(); providers.insert(name.to_string(), BoxProvider::Builtin(constructor)); } - + /// プラグイン設定を適用(既存のビルトインを上書き) pub fn apply_plugin_config(&self, config: &PluginConfig) { let mut providers = self.providers.write().unwrap(); - + for (box_name, plugin_name) in &config.plugins { - providers.insert( - box_name.clone(), - BoxProvider::Plugin(plugin_name.clone()) - ); + providers.insert(box_name.clone(), BoxProvider::Plugin(plugin_name.clone())); } } - + /// Box名からプロバイダーを取得 pub fn get_provider(&self, name: &str) -> Option { let providers = self.providers.read().unwrap(); providers.get(name).cloned() } - + /// Boxを生成 - pub fn create_box(&self, name: &str, args: &[Box]) -> Result, String> { - let provider = self.get_provider(name) + pub fn create_box( + &self, + name: &str, + args: &[Box], + ) -> Result, String> { + let provider = self + .get_provider(name) .ok_or_else(|| format!("Unknown Box type: {}", name))?; - + match provider { BoxProvider::Builtin(constructor) => { // ビルトイン実装を直接呼び出し @@ -74,17 +76,29 @@ impl BoxFactoryRegistry { } } } - + /// プラグインBoxを生成(unified facade→v2) - fn create_plugin_box(&self, plugin_name: &str, box_name: &str, args: &[Box]) -> Result, String> { + fn create_plugin_box( + &self, + plugin_name: &str, + box_name: &str, + args: &[Box], + ) -> Result, String> { use crate::runtime::get_global_plugin_host; let host = get_global_plugin_host(); let host = host.read().unwrap(); if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") { - eprintln!("[BoxFactoryRegistry] create_plugin_box: plugin={} box_type={}", plugin_name, box_name); + eprintln!( + "[BoxFactoryRegistry] create_plugin_box: plugin={} box_type={}", + plugin_name, box_name + ); } - host.create_box(box_name, args) - .map_err(|e| format!("Failed to create {} from plugin {}: {:?}", box_name, plugin_name, e)) + host.create_box(box_name, args).map_err(|e| { + format!( + "Failed to create {} from plugin {}: {:?}", + box_name, plugin_name, e + ) + }) } } @@ -100,7 +114,7 @@ impl Clone for BoxProvider { // グローバルレジストリインスタンス use once_cell::sync::Lazy; -static GLOBAL_REGISTRY: Lazy> = +static GLOBAL_REGISTRY: Lazy> = Lazy::new(|| Arc::new(BoxFactoryRegistry::new())); /// グローバルレジストリを取得 @@ -112,7 +126,7 @@ pub fn get_global_registry() -> Arc { mod tests { use super::*; use crate::box_trait::StringBox; - + fn test_string_constructor(args: &[Box]) -> Result, String> { if args.is_empty() { Ok(Box::new(StringBox::new(""))) @@ -120,26 +134,28 @@ mod tests { Ok(Box::new(StringBox::new(&args[0].to_string_box().value))) } } - + #[test] fn test_builtin_registration() { let registry = BoxFactoryRegistry::new(); registry.register_builtin("StringBox", test_string_constructor); - + let result = registry.create_box("StringBox", &[]).unwrap(); assert_eq!(result.to_string_box().value, ""); } - + #[test] fn test_plugin_override() { let registry = BoxFactoryRegistry::new(); registry.register_builtin("FileBox", test_string_constructor); - + // プラグイン設定で上書き let mut config = PluginConfig::default(); - config.plugins.insert("FileBox".to_string(), "filebox".to_string()); + config + .plugins + .insert("FileBox".to_string(), "filebox".to_string()); registry.apply_plugin_config(&config); - + // プロバイダーがプラグインに変わっているか確認 match registry.get_provider("FileBox").unwrap() { BoxProvider::Plugin(name) => assert_eq!(name, "filebox"), diff --git a/src/runtime/cache_versions.rs b/src/runtime/cache_versions.rs index 0df4ab66..57f95041 100644 --- a/src/runtime/cache_versions.rs +++ b/src/runtime/cache_versions.rs @@ -27,4 +27,3 @@ pub fn bump_many(labels: &[String]) { *e = e.saturating_add(1); } } - diff --git a/src/runtime/extern_registry.rs b/src/runtime/extern_registry.rs index 6bc018ab..8a8ffee5 100644 --- a/src/runtime/extern_registry.rs +++ b/src/runtime/extern_registry.rs @@ -13,45 +13,166 @@ pub struct ExternSpec { pub slot: Option, } -static EXTERNS: Lazy> = Lazy::new(|| vec![ - // console - ExternSpec { iface: "env.console", method: "log", min_arity: 1, max_arity: 255, slot: Some(10) }, - ExternSpec { iface: "env.console", method: "warn", min_arity: 1, max_arity: 255, slot: Some(10) }, - ExternSpec { iface: "env.console", method: "error", min_arity: 1, max_arity: 255, slot: Some(10) }, - ExternSpec { iface: "env.console", method: "info", min_arity: 1, max_arity: 255, slot: Some(10) }, - ExternSpec { iface: "env.console", method: "debug", min_arity: 1, max_arity: 255, slot: Some(10) }, - // debug - ExternSpec { iface: "env.debug", method: "trace", min_arity: 1, max_arity: 255, slot: Some(11) }, - // runtime - ExternSpec { iface: "env.runtime", method: "checkpoint", min_arity: 0, max_arity: 0, slot: Some(12) }, - // task - ExternSpec { iface: "env.task", method: "cancelCurrent", min_arity: 0, max_arity: 0, slot: Some(30) }, - ExternSpec { iface: "env.task", method: "currentToken", min_arity: 0, max_arity: 0, slot: Some(31) }, - ExternSpec { iface: "env.task", method: "yieldNow", min_arity: 0, max_arity: 0, slot: Some(32) }, - ExternSpec { iface: "env.task", method: "sleepMs", min_arity: 1, max_arity: 1, slot: Some(33) }, - // future (scaffold) - ExternSpec { iface: "env.future", method: "new", min_arity: 1, max_arity: 1, slot: Some(20) }, - ExternSpec { iface: "env.future", method: "birth", min_arity: 1, max_arity: 1, slot: Some(20) }, - ExternSpec { iface: "env.future", method: "set", min_arity: 2, max_arity: 2, slot: Some(21) }, - ExternSpec { iface: "env.future", method: "await", min_arity: 1, max_arity: 1, slot: Some(22) }, - // core-13 pure support shims - ExternSpec { iface: "env.local", method: "get", min_arity: 1, max_arity: 1, slot: Some(40) }, - ExternSpec { iface: "env.local", method: "set", min_arity: 2, max_arity: 2, slot: Some(41) }, - ExternSpec { iface: "env.box", method: "new", min_arity: 1, max_arity: 255, slot: Some(50) }, -]); +static EXTERNS: Lazy> = Lazy::new(|| { + vec![ + // console + ExternSpec { + iface: "env.console", + method: "log", + min_arity: 1, + max_arity: 255, + slot: Some(10), + }, + ExternSpec { + iface: "env.console", + method: "warn", + min_arity: 1, + max_arity: 255, + slot: Some(10), + }, + ExternSpec { + iface: "env.console", + method: "error", + min_arity: 1, + max_arity: 255, + slot: Some(10), + }, + ExternSpec { + iface: "env.console", + method: "info", + min_arity: 1, + max_arity: 255, + slot: Some(10), + }, + ExternSpec { + iface: "env.console", + method: "debug", + min_arity: 1, + max_arity: 255, + slot: Some(10), + }, + // debug + ExternSpec { + iface: "env.debug", + method: "trace", + min_arity: 1, + max_arity: 255, + slot: Some(11), + }, + // runtime + ExternSpec { + iface: "env.runtime", + method: "checkpoint", + min_arity: 0, + max_arity: 0, + slot: Some(12), + }, + // task + ExternSpec { + iface: "env.task", + method: "cancelCurrent", + min_arity: 0, + max_arity: 0, + slot: Some(30), + }, + ExternSpec { + iface: "env.task", + method: "currentToken", + min_arity: 0, + max_arity: 0, + slot: Some(31), + }, + ExternSpec { + iface: "env.task", + method: "yieldNow", + min_arity: 0, + max_arity: 0, + slot: Some(32), + }, + ExternSpec { + iface: "env.task", + method: "sleepMs", + min_arity: 1, + max_arity: 1, + slot: Some(33), + }, + // future (scaffold) + ExternSpec { + iface: "env.future", + method: "new", + min_arity: 1, + max_arity: 1, + slot: Some(20), + }, + ExternSpec { + iface: "env.future", + method: "birth", + min_arity: 1, + max_arity: 1, + slot: Some(20), + }, + ExternSpec { + iface: "env.future", + method: "set", + min_arity: 2, + max_arity: 2, + slot: Some(21), + }, + ExternSpec { + iface: "env.future", + method: "await", + min_arity: 1, + max_arity: 1, + slot: Some(22), + }, + // core-13 pure support shims + ExternSpec { + iface: "env.local", + method: "get", + min_arity: 1, + max_arity: 1, + slot: Some(40), + }, + ExternSpec { + iface: "env.local", + method: "set", + min_arity: 2, + max_arity: 2, + slot: Some(41), + }, + ExternSpec { + iface: "env.box", + method: "new", + min_arity: 1, + max_arity: 255, + slot: Some(50), + }, + ] +}); pub fn resolve(iface: &str, method: &str) -> Option { - EXTERNS.iter().copied().find(|e| e.iface == iface && e.method == method) + EXTERNS + .iter() + .copied() + .find(|e| e.iface == iface && e.method == method) } pub fn known_for_iface(iface: &str) -> Vec<&'static str> { - let mut v: Vec<&'static str> = EXTERNS.iter().filter(|e| e.iface == iface).map(|e| e.method).collect(); - v.sort(); v.dedup(); v + let mut v: Vec<&'static str> = EXTERNS + .iter() + .filter(|e| e.iface == iface) + .map(|e| e.method) + .collect(); + v.sort(); + v.dedup(); + v } pub fn all_ifaces() -> Vec<&'static str> { let mut v: Vec<&'static str> = EXTERNS.iter().map(|e| e.iface).collect(); - v.sort(); v.dedup(); v + v.sort(); + v.dedup(); + v } /// Resolve slot id for an extern call (if assigned) @@ -62,7 +183,15 @@ pub fn resolve_slot(iface: &str, method: &str) -> Option { /// Check arity against registry; returns Ok or an explanatory error string pub fn check_arity(iface: &str, method: &str, argc: usize) -> Result<(), String> { if let Some(s) = resolve(iface, method) { - if argc as u8 >= s.min_arity && argc as u8 <= s.max_arity { Ok(()) } - else { Err(format!("arity {} out of range {}..{}", argc, s.min_arity, s.max_arity)) } - } else { Err("unknown extern".to_string()) } + if argc as u8 >= s.min_arity && argc as u8 <= s.max_arity { + Ok(()) + } else { + Err(format!( + "arity {} out of range {}..{}", + argc, s.min_arity, s.max_arity + )) + } + } else { + Err("unknown extern".to_string()) + } } diff --git a/src/runtime/gc.rs b/src/runtime/gc.rs index a67c6546..e2d8ec66 100644 --- a/src/runtime/gc.rs +++ b/src/runtime/gc.rs @@ -4,7 +4,10 @@ //! Default implementation is a no-op. Real collectors can plug later. #[derive(Debug, Clone, Copy)] -pub enum BarrierKind { Read, Write } +pub enum BarrierKind { + Read, + Write, +} /// GC hooks that execution engines may call at key points. /// Implementations must be Send + Sync for multi-thread preparation. @@ -14,7 +17,9 @@ pub trait GcHooks: Send + Sync { /// Memory barrier hint for loads/stores. fn barrier(&self, _kind: BarrierKind) {} /// Optional counters snapshot for diagnostics. Default: None. - fn snapshot_counters(&self) -> Option<(u64, u64, u64)> { None } + fn snapshot_counters(&self) -> Option<(u64, u64, u64)> { + None + } } /// Default no-op hooks. @@ -55,8 +60,12 @@ impl GcHooks for CountingGc { } fn barrier(&self, kind: BarrierKind) { match kind { - BarrierKind::Read => { self.barrier_reads.fetch_add(1, Ordering::Relaxed); } - BarrierKind::Write => { self.barrier_writes.fetch_add(1, Ordering::Relaxed); } + BarrierKind::Read => { + self.barrier_reads.fetch_add(1, Ordering::Relaxed); + } + BarrierKind::Write => { + self.barrier_writes.fetch_add(1, Ordering::Relaxed); + } } } fn snapshot_counters(&self) -> Option<(u64, u64, u64)> { diff --git a/src/runtime/global_hooks.rs b/src/runtime/global_hooks.rs index 566bb130..5ef620c8 100644 --- a/src/runtime/global_hooks.rs +++ b/src/runtime/global_hooks.rs @@ -3,51 +3,100 @@ use once_cell::sync::OnceCell; use std::sync::{Arc, RwLock}; -use super::{gc::GcHooks, scheduler::Scheduler}; use super::scheduler::CancellationToken; +use super::{gc::GcHooks, scheduler::Scheduler}; static GLOBAL_GC: OnceCell>>> = OnceCell::new(); static GLOBAL_SCHED: OnceCell>>> = OnceCell::new(); // Phase 2 scaffold: current task group's cancellation token (no-op default) static GLOBAL_CUR_TOKEN: OnceCell>> = OnceCell::new(); // Phase 2 scaffold: current group's child futures registry (best-effort) -static GLOBAL_GROUP_FUTURES: OnceCell>> = OnceCell::new(); +static GLOBAL_GROUP_FUTURES: OnceCell>> = + OnceCell::new(); // Strong ownership list for implicit group (pre-TaskGroup actualization) -static GLOBAL_GROUP_STRONG: OnceCell>> = OnceCell::new(); +static GLOBAL_GROUP_STRONG: OnceCell>> = + OnceCell::new(); // Simple scope depth counter for implicit group (join-at-scope-exit footing) static TASK_SCOPE_DEPTH: OnceCell> = OnceCell::new(); // TaskGroup scope stack (explicit group ownership per function scope) -static TASK_GROUP_STACK: OnceCell>>> = OnceCell::new(); +static TASK_GROUP_STACK: OnceCell< + RwLock>>, +> = OnceCell::new(); -fn gc_cell() -> &'static RwLock>> { GLOBAL_GC.get_or_init(|| RwLock::new(None)) } -fn sched_cell() -> &'static RwLock>> { GLOBAL_SCHED.get_or_init(|| RwLock::new(None)) } -fn token_cell() -> &'static RwLock> { GLOBAL_CUR_TOKEN.get_or_init(|| RwLock::new(None)) } -fn futures_cell() -> &'static RwLock> { GLOBAL_GROUP_FUTURES.get_or_init(|| RwLock::new(Vec::new())) } -fn strong_cell() -> &'static RwLock> { GLOBAL_GROUP_STRONG.get_or_init(|| RwLock::new(Vec::new())) } -fn scope_depth_cell() -> &'static RwLock { TASK_SCOPE_DEPTH.get_or_init(|| RwLock::new(0)) } -fn group_stack_cell() -> &'static RwLock>> { TASK_GROUP_STACK.get_or_init(|| RwLock::new(Vec::new())) } - -pub fn set_from_runtime(rt: &crate::runtime::nyash_runtime::NyashRuntime) { - if let Ok(mut g) = gc_cell().write() { *g = Some(rt.gc.clone()); } - if let Ok(mut s) = sched_cell().write() { *s = rt.scheduler.as_ref().cloned(); } - // Optional: initialize a fresh token for the runtime's root group (Phase 2 wiring) - if let Ok(mut t) = token_cell().write() { if t.is_none() { *t = Some(CancellationToken::new()); } } - // Reset group futures registry on new runtime - if let Ok(mut f) = futures_cell().write() { f.clear(); } - if let Ok(mut s) = strong_cell().write() { s.clear(); } - if let Ok(mut d) = scope_depth_cell().write() { *d = 0; } - if let Ok(mut st) = group_stack_cell().write() { st.clear(); } +fn gc_cell() -> &'static RwLock>> { + GLOBAL_GC.get_or_init(|| RwLock::new(None)) +} +fn sched_cell() -> &'static RwLock>> { + GLOBAL_SCHED.get_or_init(|| RwLock::new(None)) +} +fn token_cell() -> &'static RwLock> { + GLOBAL_CUR_TOKEN.get_or_init(|| RwLock::new(None)) +} +fn futures_cell() -> &'static RwLock> { + GLOBAL_GROUP_FUTURES.get_or_init(|| RwLock::new(Vec::new())) +} +fn strong_cell() -> &'static RwLock> { + GLOBAL_GROUP_STRONG.get_or_init(|| RwLock::new(Vec::new())) +} +fn scope_depth_cell() -> &'static RwLock { + TASK_SCOPE_DEPTH.get_or_init(|| RwLock::new(0)) +} +fn group_stack_cell( +) -> &'static RwLock>> { + TASK_GROUP_STACK.get_or_init(|| RwLock::new(Vec::new())) } -pub fn set_gc(gc: Arc) { if let Ok(mut g) = gc_cell().write() { *g = Some(gc); } } -pub fn set_scheduler(s: Arc) { if let Ok(mut w) = sched_cell().write() { *w = Some(s); } } +pub fn set_from_runtime(rt: &crate::runtime::nyash_runtime::NyashRuntime) { + if let Ok(mut g) = gc_cell().write() { + *g = Some(rt.gc.clone()); + } + if let Ok(mut s) = sched_cell().write() { + *s = rt.scheduler.as_ref().cloned(); + } + // Optional: initialize a fresh token for the runtime's root group (Phase 2 wiring) + if let Ok(mut t) = token_cell().write() { + if t.is_none() { + *t = Some(CancellationToken::new()); + } + } + // Reset group futures registry on new runtime + if let Ok(mut f) = futures_cell().write() { + f.clear(); + } + if let Ok(mut s) = strong_cell().write() { + s.clear(); + } + if let Ok(mut d) = scope_depth_cell().write() { + *d = 0; + } + if let Ok(mut st) = group_stack_cell().write() { + st.clear(); + } +} + +pub fn set_gc(gc: Arc) { + if let Ok(mut g) = gc_cell().write() { + *g = Some(gc); + } +} +pub fn set_scheduler(s: Arc) { + if let Ok(mut w) = sched_cell().write() { + *w = Some(s); + } +} /// Set the current task group's cancellation token (scaffold). -pub fn set_current_group_token(tok: CancellationToken) { if let Ok(mut w) = token_cell().write() { *w = Some(tok); } } +pub fn set_current_group_token(tok: CancellationToken) { + if let Ok(mut w) = token_cell().write() { + *w = Some(tok); + } +} /// Get the current task group's cancellation token (no-op default). pub fn current_group_token() -> CancellationToken { if let Ok(r) = token_cell().read() { - if let Some(t) = r.as_ref() { return t.clone(); } + if let Some(t) = r.as_ref() { + return t.clone(); + } } CancellationToken::new() } @@ -57,12 +106,19 @@ pub fn register_future_to_current_group(fut: &crate::boxes::future::FutureBox) { // Prefer explicit current TaskGroup at top of stack if let Ok(st) = group_stack_cell().read() { if let Some(inner) = st.last() { - if let Ok(mut v) = inner.strong.lock() { v.push(fut.clone()); return; } + if let Ok(mut v) = inner.strong.lock() { + v.push(fut.clone()); + return; + } } } // Fallback to implicit global group - if let Ok(mut list) = futures_cell().write() { list.push(fut.downgrade()); } - if let Ok(mut s) = strong_cell().write() { s.push(fut.clone()); } + if let Ok(mut list) = futures_cell().write() { + list.push(fut.downgrade()); + } + if let Ok(mut s) = strong_cell().write() { + s.push(fut.clone()); + } } /// Join all currently registered futures with a coarse timeout guard. @@ -74,36 +130,57 @@ pub fn join_all_registered_futures(timeout_ms: u64) { // purge list of dropped or completed futures opportunistically { // purge weak list: keep only upgradeable futures - if let Ok(mut list) = futures_cell().write() { list.retain(|fw| fw.is_ready().is_some()); } + if let Ok(mut list) = futures_cell().write() { + list.retain(|fw| fw.is_ready().is_some()); + } // purge strong list: remove completed futures to reduce retention - if let Ok(mut s) = strong_cell().write() { s.retain(|f| !f.ready()); } + if let Ok(mut s) = strong_cell().write() { + s.retain(|f| !f.ready()); + } } // check readiness { if let Ok(list) = futures_cell().read() { for fw in list.iter() { if let Some(ready) = fw.is_ready() { - if !ready { all_ready = false; break; } + if !ready { + all_ready = false; + break; + } } } } } - if all_ready { break; } - if Instant::now() >= deadline { break; } + if all_ready { + break; + } + if Instant::now() >= deadline { + break; + } safepoint_and_poll(); std::thread::yield_now(); } // Final sweep - if let Ok(mut s) = strong_cell().write() { s.retain(|f| !f.ready()); } - if let Ok(mut list) = futures_cell().write() { list.retain(|fw| matches!(fw.is_ready(), Some(false))); } + if let Ok(mut s) = strong_cell().write() { + s.retain(|f| !f.ready()); + } + if let Ok(mut list) = futures_cell().write() { + list.retain(|fw| matches!(fw.is_ready(), Some(false))); + } } /// Push a task scope (footing). On pop of the outermost scope, perform a best-effort join. pub fn push_task_scope() { - if let Ok(mut d) = scope_depth_cell().write() { *d += 1; } + if let Ok(mut d) = scope_depth_cell().write() { + *d += 1; + } // Push a new explicit TaskGroup for this scope if let Ok(mut st) = group_stack_cell().write() { - st.push(std::sync::Arc::new(crate::boxes::task_group_box::TaskGroupInner { strong: std::sync::Mutex::new(Vec::new()) })); + st.push(std::sync::Arc::new( + crate::boxes::task_group_box::TaskGroupInner { + strong: std::sync::Mutex::new(Vec::new()), + }, + )); } // Set a fresh cancellation token for this scope (best-effort) set_current_group_token(CancellationToken::new()); @@ -115,22 +192,40 @@ pub fn pop_task_scope() { let mut popped: Option> = None; { if let Ok(mut d) = scope_depth_cell().write() { - if *d > 0 { *d -= 1; } - if *d == 0 { do_join = true; } + if *d > 0 { + *d -= 1; + } + if *d == 0 { + do_join = true; + } } } // Pop explicit group for this scope - if let Ok(mut st) = group_stack_cell().write() { popped = st.pop(); } + if let Ok(mut st) = group_stack_cell().write() { + popped = st.pop(); + } if do_join { - let ms: u64 = std::env::var("NYASH_TASK_SCOPE_JOIN_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(1000); + let ms: u64 = std::env::var("NYASH_TASK_SCOPE_JOIN_MS") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(1000); if let Some(inner) = popped { // Join this group's outstanding futures let deadline = std::time::Instant::now() + std::time::Duration::from_millis(ms); loop { let mut all_ready = true; - if let Ok(mut list) = inner.strong.lock() { list.retain(|f| !f.ready()); if !list.is_empty() { all_ready = false; } } - if all_ready { break; } - if std::time::Instant::now() >= deadline { break; } + if let Ok(mut list) = inner.strong.lock() { + list.retain(|f| !f.ready()); + if !list.is_empty() { + all_ready = false; + } + } + if all_ready { + break; + } + if std::time::Instant::now() >= deadline { + break; + } safepoint_and_poll(); std::thread::yield_now(); } @@ -146,10 +241,14 @@ pub fn pop_task_scope() { /// Perform a runtime safepoint and poll the scheduler if available. pub fn safepoint_and_poll() { if let Ok(g) = gc_cell().read() { - if let Some(gc) = g.as_ref() { gc.safepoint(); } + if let Some(gc) = g.as_ref() { + gc.safepoint(); + } } if let Ok(s) = sched_cell().read() { - if let Some(sched) = s.as_ref() { sched.poll(); } + if let Some(sched) = s.as_ref() { + sched.poll(); + } } } @@ -168,7 +267,11 @@ pub fn spawn_task(name: &str, f: Box) -> bool { } /// Spawn a task bound to a cancellation token when available (skeleton). -pub fn spawn_task_with_token(name: &str, token: crate::runtime::scheduler::CancellationToken, f: Box) -> bool { +pub fn spawn_task_with_token( + name: &str, + token: crate::runtime::scheduler::CancellationToken, + f: Box, +) -> bool { if let Ok(s) = sched_cell().read() { if let Some(sched) = s.as_ref() { sched.spawn_with_token(name, token, f); diff --git a/src/runtime/host_api.rs b/src/runtime/host_api.rs index f4075f85..924ab099 100644 --- a/src/runtime/host_api.rs +++ b/src/runtime/host_api.rs @@ -13,13 +13,23 @@ thread_local! { static CURRENT_VM: std::cell::Cell<*mut crate::backend::vm::VM> = std::cell::Cell::new(std::ptr::null_mut()); } -pub fn set_current_vm(ptr: *mut crate::backend::vm::VM) { CURRENT_VM.with(|c| c.set(ptr)); } -pub fn clear_current_vm() { CURRENT_VM.with(|c| c.set(std::ptr::null_mut())); } +pub fn set_current_vm(ptr: *mut crate::backend::vm::VM) { + CURRENT_VM.with(|c| c.set(ptr)); +} +pub fn clear_current_vm() { + CURRENT_VM.with(|c| c.set(std::ptr::null_mut())); +} fn with_current_vm_mut(f: F) -> Option -where F: FnOnce(&mut crate::backend::vm::VM) -> R { +where + F: FnOnce(&mut crate::backend::vm::VM) -> R, +{ CURRENT_VM.with(|c| { let p = c.get(); - if p.is_null() { None } else { Some(unsafe { f(&mut *p) }) } + if p.is_null() { + None + } else { + Some(unsafe { f(&mut *p) }) + } }) } @@ -45,35 +55,59 @@ fn tlv_encode_one(val: &crate::backend::vm::VMValue) -> Vec { fn vmvalue_from_tlv(tag: u8, payload: &[u8]) -> Option { use crate::runtime::plugin_ffi_common as tlv; match tag { - 1 => Some(crate::backend::vm::VMValue::Bool(tlv::decode::bool(payload).unwrap_or(false))), + 1 => Some(crate::backend::vm::VMValue::Bool( + tlv::decode::bool(payload).unwrap_or(false), + )), 2 => tlv::decode::i32(payload).map(|v| crate::backend::vm::VMValue::Integer(v as i64)), 3 => { - if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); Some(crate::backend::vm::VMValue::Integer(i64::from_le_bytes(b))) } else { None } + if payload.len() == 8 { + let mut b = [0u8; 8]; + b.copy_from_slice(payload); + Some(crate::backend::vm::VMValue::Integer(i64::from_le_bytes(b))) + } else { + None + } } 5 => tlv::decode::f64(payload).map(crate::backend::vm::VMValue::Float), - 6|7 => Some(crate::backend::vm::VMValue::String(tlv::decode::string(payload))), + 6 | 7 => Some(crate::backend::vm::VMValue::String(tlv::decode::string( + payload, + ))), 8 => { // PluginHandle(type_id, instance_id) → reconstruct PluginBoxV2 (when plugins enabled) if let Some((type_id, instance_id)) = tlv::decode::plugin_handle(payload) { - if let Some(arc) = plugin_box_from_handle(type_id, instance_id) { return Some(crate::backend::vm::VMValue::BoxRef(arc)); } + if let Some(arc) = plugin_box_from_handle(type_id, instance_id) { + return Some(crate::backend::vm::VMValue::BoxRef(arc)); + } } None } 9 => { - if let Some(h) = tlv::decode::u64(payload) { crate::runtime::host_handles::get(h).map(crate::backend::vm::VMValue::BoxRef) } else { None } + if let Some(h) = tlv::decode::u64(payload) { + crate::runtime::host_handles::get(h).map(crate::backend::vm::VMValue::BoxRef) + } else { + None + } } _ => None, } } -unsafe fn slice_from_raw<'a>(ptr: *const u8, len: usize) -> &'a [u8] { std::slice::from_raw_parts(ptr, len) } -unsafe fn slice_from_raw_mut<'a>(ptr: *mut u8, len: usize) -> &'a mut [u8] { std::slice::from_raw_parts_mut(ptr, len) } +unsafe fn slice_from_raw<'a>(ptr: *const u8, len: usize) -> &'a [u8] { + std::slice::from_raw_parts(ptr, len) +} +unsafe fn slice_from_raw_mut<'a>(ptr: *mut u8, len: usize) -> &'a mut [u8] { + std::slice::from_raw_parts_mut(ptr, len) +} fn encode_out(out_ptr: *mut u8, out_len: *mut usize, buf: &[u8]) -> i32 { unsafe { - if out_ptr.is_null() || out_len.is_null() { return -2; } + if out_ptr.is_null() || out_len.is_null() { + return -2; + } let cap = *out_len; - if cap < buf.len() { return -3; } + if cap < buf.len() { + return -3; + } let out = slice_from_raw_mut(out_ptr, cap); out[..buf.len()].copy_from_slice(buf); *out_len = buf.len(); @@ -82,62 +116,115 @@ fn encode_out(out_ptr: *mut u8, out_len: *mut usize, buf: &[u8]) -> i32 { } #[cfg_attr(all(not(test), feature = "c-abi-export"), no_mangle)] -pub extern "C" fn nyrt_host_call_name(handle: u64, method_ptr: *const u8, method_len: usize, - args_ptr: *const u8, args_len: usize, - out_ptr: *mut u8, out_len: *mut usize) -> i32 { +pub extern "C" fn nyrt_host_call_name( + handle: u64, + method_ptr: *const u8, + method_len: usize, + args_ptr: *const u8, + args_len: usize, + out_ptr: *mut u8, + out_len: *mut usize, +) -> i32 { // Resolve receiver - let recv_arc = match crate::runtime::host_handles::get(handle) { Some(a) => a, None => return -1 }; - let method = unsafe { std::str::from_utf8(slice_from_raw(method_ptr, method_len)).unwrap_or("") }.to_string(); + let recv_arc = match crate::runtime::host_handles::get(handle) { + Some(a) => a, + None => return -1, + }; + let method = + unsafe { std::str::from_utf8(slice_from_raw(method_ptr, method_len)).unwrap_or("") } + .to_string(); // Parse TLV args (header + entries) let mut argv: Vec = Vec::new(); - if args_ptr.is_null() || args_len < 4 { /* no args */ } else { + if args_ptr.is_null() || args_len < 4 { /* no args */ + } else { let buf = unsafe { slice_from_raw(args_ptr, args_len) }; // Iterate entries let mut off = 4usize; while buf.len() >= off + 4 { - let tag = buf[off]; let _rsv = buf[off+1]; let sz = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize; - if buf.len() < off + 4 + sz { break; } - let payload = &buf[off+4..off+4+sz]; - if let Some(v) = vmvalue_from_tlv(tag, payload) { argv.push(v); } + let tag = buf[off]; + let _rsv = buf[off + 1]; + let sz = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize; + if buf.len() < off + 4 + sz { + break; + } + let payload = &buf[off + 4..off + 4 + sz]; + if let Some(v) = vmvalue_from_tlv(tag, payload) { + argv.push(v); + } off += 4 + sz; } } // Dispatch minimal supported methods // InstanceBox getField/setField - if let Some(inst) = recv_arc.as_any().downcast_ref::() { + if let Some(inst) = recv_arc + .as_any() + .downcast_ref::() + { match method.as_str() { "getField" if argv.len() >= 1 => { - let field = match &argv[0] { crate::backend::vm::VMValue::String(s) => s.clone(), v => v.to_string() }; - let out = inst.get_field_unified(&field).map(|nv| match nv { - crate::value::NyashValue::Integer(i) => crate::backend::vm::VMValue::Integer(i), - crate::value::NyashValue::Float(f) => crate::backend::vm::VMValue::Float(f), - crate::value::NyashValue::Bool(b) => crate::backend::vm::VMValue::Bool(b), - crate::value::NyashValue::String(s) => crate::backend::vm::VMValue::String(s), - crate::value::NyashValue::Void | crate::value::NyashValue::Null => crate::backend::vm::VMValue::String("".to_string()), - crate::value::NyashValue::Box(b) => { - if let Ok(g) = b.lock() { crate::backend::vm::VMValue::BoxRef(std::sync::Arc::from(g.share_box())) } else { crate::backend::vm::VMValue::String("".to_string()) } - } - _ => crate::backend::vm::VMValue::String("".to_string()), - }).unwrap_or(crate::backend::vm::VMValue::String("".to_string())); + let field = match &argv[0] { + crate::backend::vm::VMValue::String(s) => s.clone(), + v => v.to_string(), + }; + let out = inst + .get_field_unified(&field) + .map(|nv| match nv { + crate::value::NyashValue::Integer(i) => { + crate::backend::vm::VMValue::Integer(i) + } + crate::value::NyashValue::Float(f) => crate::backend::vm::VMValue::Float(f), + crate::value::NyashValue::Bool(b) => crate::backend::vm::VMValue::Bool(b), + crate::value::NyashValue::String(s) => { + crate::backend::vm::VMValue::String(s) + } + crate::value::NyashValue::Void | crate::value::NyashValue::Null => { + crate::backend::vm::VMValue::String("".to_string()) + } + crate::value::NyashValue::Box(b) => { + if let Ok(g) = b.lock() { + crate::backend::vm::VMValue::BoxRef(std::sync::Arc::from( + g.share_box(), + )) + } else { + crate::backend::vm::VMValue::String("".to_string()) + } + } + _ => crate::backend::vm::VMValue::String("".to_string()), + }) + .unwrap_or(crate::backend::vm::VMValue::String("".to_string())); let buf = tlv_encode_one(&out); return encode_out(out_ptr, out_len, &buf); } "setField" if argv.len() >= 2 => { - let field = match &argv[0] { crate::backend::vm::VMValue::String(s) => s.clone(), v => v.to_string() }; + let field = match &argv[0] { + crate::backend::vm::VMValue::String(s) => s.clone(), + v => v.to_string(), + }; // Barrier: use current VM runtime if available let _ = with_current_vm_mut(|vm| { - crate::backend::gc_helpers::gc_write_barrier_site(vm.runtime_ref(), "HostAPI.setField"); + crate::backend::gc_helpers::gc_write_barrier_site( + vm.runtime_ref(), + "HostAPI.setField", + ); }); // Accept primitives only for now let nv_opt = match argv[1].clone() { - crate::backend::vm::VMValue::Integer(i) => Some(crate::value::NyashValue::Integer(i)), - crate::backend::vm::VMValue::Float(f) => Some(crate::value::NyashValue::Float(f)), + crate::backend::vm::VMValue::Integer(i) => { + Some(crate::value::NyashValue::Integer(i)) + } + crate::backend::vm::VMValue::Float(f) => { + Some(crate::value::NyashValue::Float(f)) + } crate::backend::vm::VMValue::Bool(b) => Some(crate::value::NyashValue::Bool(b)), - crate::backend::vm::VMValue::String(s) => Some(crate::value::NyashValue::String(s)), + crate::backend::vm::VMValue::String(s) => { + Some(crate::value::NyashValue::String(s)) + } crate::backend::vm::VMValue::BoxRef(_) => None, _ => None, }; - if let Some(nv) = nv_opt { let _ = inst.set_field_unified(field, nv); } + if let Some(nv) = nv_opt { + let _ = inst.set_field_unified(field, nv); + } let buf = tlv_encode_one(&crate::backend::vm::VMValue::Bool(true)); return encode_out(out_ptr, out_len, &buf); } @@ -145,27 +232,47 @@ pub extern "C" fn nyrt_host_call_name(handle: u64, method_ptr: *const u8, method } } // ArrayBox get/set - if let Some(arr) = recv_arc.as_any().downcast_ref::() { + if let Some(arr) = recv_arc + .as_any() + .downcast_ref::() + { match method.as_str() { "get" if argv.len() >= 1 => { - let idx = match argv[0].clone() { crate::backend::vm::VMValue::Integer(i) => i, v => v.to_string().parse::().unwrap_or(0) }; + let idx = match argv[0].clone() { + crate::backend::vm::VMValue::Integer(i) => i, + v => v.to_string().parse::().unwrap_or(0), + }; let out = arr.get(Box::new(crate::box_trait::IntegerBox::new(idx))); let vmv = crate::backend::vm::VMValue::from_nyash_box(out); let buf = tlv_encode_one(&vmv); return encode_out(out_ptr, out_len, &buf); } "set" if argv.len() >= 2 => { - let idx = match argv[0].clone() { crate::backend::vm::VMValue::Integer(i) => i, v => v.to_string().parse::().unwrap_or(0) }; + let idx = match argv[0].clone() { + crate::backend::vm::VMValue::Integer(i) => i, + v => v.to_string().parse::().unwrap_or(0), + }; let vb = match argv[1].clone() { - crate::backend::vm::VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)) as Box, - crate::backend::vm::VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)), - crate::backend::vm::VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)), - crate::backend::vm::VMValue::String(s) => Box::new(crate::box_trait::StringBox::new(s)), + crate::backend::vm::VMValue::Integer(i) => { + Box::new(crate::box_trait::IntegerBox::new(i)) as Box + } + crate::backend::vm::VMValue::Float(f) => { + Box::new(crate::boxes::math_box::FloatBox::new(f)) + } + crate::backend::vm::VMValue::Bool(b) => { + Box::new(crate::box_trait::BoolBox::new(b)) + } + crate::backend::vm::VMValue::String(s) => { + Box::new(crate::box_trait::StringBox::new(s)) + } crate::backend::vm::VMValue::BoxRef(b) => b.share_box(), _ => Box::new(crate::box_trait::VoidBox::new()), }; let _ = with_current_vm_mut(|vm| { - crate::backend::gc_helpers::gc_write_barrier_site(vm.runtime_ref(), "HostAPI.Array.set"); + crate::backend::gc_helpers::gc_write_barrier_site( + vm.runtime_ref(), + "HostAPI.Array.set", + ); }); let out = arr.set(Box::new(crate::box_trait::IntegerBox::new(idx)), vb); let vmv = crate::backend::vm::VMValue::from_nyash_box(out); @@ -188,7 +295,12 @@ fn plugin_box_from_handle(type_id: u32, instance_id: u32) -> Option Option> { None } +fn plugin_box_from_handle( + _type_id: u32, + _instance_id: u32, +) -> Option> { + None +} // ---- by-slot variant (selector_id: u64) ---- // Minimal slot mapping (subject to consolidation with TypeRegistry): @@ -206,66 +318,125 @@ fn plugin_box_from_handle(_type_id: u32, _instance_id: u32) -> Option any // 300: StringBox.len() -> i64 #[cfg_attr(all(not(test), feature = "c-abi-export"), no_mangle)] -pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64, - args_ptr: *const u8, args_len: usize, - out_ptr: *mut u8, out_len: *mut usize) -> i32 { - let recv_arc = match crate::runtime::host_handles::get(handle) { Some(a) => a, None => return -1 }; +pub extern "C" fn nyrt_host_call_slot( + handle: u64, + selector_id: u64, + args_ptr: *const u8, + args_len: usize, + out_ptr: *mut u8, + out_len: *mut usize, +) -> i32 { + let recv_arc = match crate::runtime::host_handles::get(handle) { + Some(a) => a, + None => return -1, + }; // Parse TLV args let mut argv: Vec = Vec::new(); if !args_ptr.is_null() && args_len >= 4 { let buf = unsafe { slice_from_raw(args_ptr, args_len) }; let mut off = 4usize; while buf.len() >= off + 4 { - let tag = buf[off]; let sz = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize; - if buf.len() < off + 4 + sz { break; } - let payload = &buf[off+4..off+4+sz]; - if let Some(v) = vmvalue_from_tlv(tag, payload) { argv.push(v); } + let tag = buf[off]; + let sz = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize; + if buf.len() < off + 4 + sz { + break; + } + let payload = &buf[off + 4..off + 4 + sz]; + if let Some(v) = vmvalue_from_tlv(tag, payload) { + argv.push(v); + } off += 4 + sz; } } match selector_id { 1 | 2 | 3 | 4 => { - if let Some(inst) = recv_arc.as_any().downcast_ref::() { + if let Some(inst) = recv_arc + .as_any() + .downcast_ref::() + { if selector_id == 1 { // getField(name) if argv.len() >= 1 { - let field = match &argv[0] { crate::backend::vm::VMValue::String(s) => s.clone(), v => v.to_string() }; - let out = inst.get_field_unified(&field).map(|nv| match nv { - crate::value::NyashValue::Integer(i) => crate::backend::vm::VMValue::Integer(i), - crate::value::NyashValue::Float(f) => crate::backend::vm::VMValue::Float(f), - crate::value::NyashValue::Bool(b) => crate::backend::vm::VMValue::Bool(b), - crate::value::NyashValue::String(s) => crate::backend::vm::VMValue::String(s), - crate::value::NyashValue::Void | crate::value::NyashValue::Null => crate::backend::vm::VMValue::String("".to_string()), - crate::value::NyashValue::Box(b) => { - if let Ok(g) = b.lock() { crate::backend::vm::VMValue::BoxRef(std::sync::Arc::from(g.share_box())) } else { crate::backend::vm::VMValue::String("".to_string()) } - } - _ => crate::backend::vm::VMValue::String("".to_string()), - }).unwrap_or(crate::backend::vm::VMValue::String("".to_string())); + let field = match &argv[0] { + crate::backend::vm::VMValue::String(s) => s.clone(), + v => v.to_string(), + }; + let out = inst + .get_field_unified(&field) + .map(|nv| match nv { + crate::value::NyashValue::Integer(i) => { + crate::backend::vm::VMValue::Integer(i) + } + crate::value::NyashValue::Float(f) => { + crate::backend::vm::VMValue::Float(f) + } + crate::value::NyashValue::Bool(b) => { + crate::backend::vm::VMValue::Bool(b) + } + crate::value::NyashValue::String(s) => { + crate::backend::vm::VMValue::String(s) + } + crate::value::NyashValue::Void | crate::value::NyashValue::Null => { + crate::backend::vm::VMValue::String("".to_string()) + } + crate::value::NyashValue::Box(b) => { + if let Ok(g) = b.lock() { + crate::backend::vm::VMValue::BoxRef(std::sync::Arc::from( + g.share_box(), + )) + } else { + crate::backend::vm::VMValue::String("".to_string()) + } + } + _ => crate::backend::vm::VMValue::String("".to_string()), + }) + .unwrap_or(crate::backend::vm::VMValue::String("".to_string())); let buf = tlv_encode_one(&out); return encode_out(out_ptr, out_len, &buf); } } else if selector_id == 2 { // setField(name, value) if argv.len() >= 2 { - let field = match &argv[0] { crate::backend::vm::VMValue::String(s) => s.clone(), v => v.to_string() }; - let _ = with_current_vm_mut(|vm| { crate::backend::gc_helpers::gc_write_barrier_site(vm.runtime_ref(), "HostAPI.setField"); }); + let field = match &argv[0] { + crate::backend::vm::VMValue::String(s) => s.clone(), + v => v.to_string(), + }; + let _ = with_current_vm_mut(|vm| { + crate::backend::gc_helpers::gc_write_barrier_site( + vm.runtime_ref(), + "HostAPI.setField", + ); + }); let nv_opt = match argv[1].clone() { - crate::backend::vm::VMValue::Integer(i) => Some(crate::value::NyashValue::Integer(i)), - crate::backend::vm::VMValue::Float(f) => Some(crate::value::NyashValue::Float(f)), - crate::backend::vm::VMValue::Bool(b) => Some(crate::value::NyashValue::Bool(b)), - crate::backend::vm::VMValue::String(s) => Some(crate::value::NyashValue::String(s)), + crate::backend::vm::VMValue::Integer(i) => { + Some(crate::value::NyashValue::Integer(i)) + } + crate::backend::vm::VMValue::Float(f) => { + Some(crate::value::NyashValue::Float(f)) + } + crate::backend::vm::VMValue::Bool(b) => { + Some(crate::value::NyashValue::Bool(b)) + } + crate::backend::vm::VMValue::String(s) => { + Some(crate::value::NyashValue::String(s)) + } crate::backend::vm::VMValue::BoxRef(_) => None, _ => None, }; - if let Some(nv) = nv_opt { let _ = inst.set_field_unified(field, nv); } + if let Some(nv) = nv_opt { + let _ = inst.set_field_unified(field, nv); + } let buf = tlv_encode_one(&crate::backend::vm::VMValue::Bool(true)); return encode_out(out_ptr, out_len, &buf); } } else if selector_id == 3 { // has(name) if argv.len() >= 1 { - let field = match &argv[0] { crate::backend::vm::VMValue::String(s) => s.clone(), v => v.to_string() }; + let field = match &argv[0] { + crate::backend::vm::VMValue::String(s) => s.clone(), + v => v.to_string(), + }; let has = inst.get_field_unified(&field).is_some(); let buf = tlv_encode_one(&crate::backend::vm::VMValue::Bool(has)); return encode_out(out_ptr, out_len, &buf); @@ -279,36 +450,62 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64, } } 100 | 101 | 102 => { - if let Some(arr) = recv_arc.as_any().downcast_ref::() { + if let Some(arr) = recv_arc + .as_any() + .downcast_ref::() + { match selector_id { - 100 => { // get(index) + 100 => { + // get(index) if argv.len() >= 1 { - let idx = match argv[0].clone() { crate::backend::vm::VMValue::Integer(i) => i, v => v.to_string().parse::().unwrap_or(0) }; + let idx = match argv[0].clone() { + crate::backend::vm::VMValue::Integer(i) => i, + v => v.to_string().parse::().unwrap_or(0), + }; let out = arr.get(Box::new(crate::box_trait::IntegerBox::new(idx))); let vmv = crate::backend::vm::VMValue::from_nyash_box(out); let buf = tlv_encode_one(&vmv); return encode_out(out_ptr, out_len, &buf); } } - 101 => { // set(index, value) + 101 => { + // set(index, value) if argv.len() >= 2 { - let idx = match argv[0].clone() { crate::backend::vm::VMValue::Integer(i) => i, v => v.to_string().parse::().unwrap_or(0) }; + let idx = match argv[0].clone() { + crate::backend::vm::VMValue::Integer(i) => i, + v => v.to_string().parse::().unwrap_or(0), + }; let vb = match argv[1].clone() { - crate::backend::vm::VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)) as Box, - crate::backend::vm::VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)), - crate::backend::vm::VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)), - crate::backend::vm::VMValue::String(s) => Box::new(crate::box_trait::StringBox::new(s)), + crate::backend::vm::VMValue::Integer(i) => { + Box::new(crate::box_trait::IntegerBox::new(i)) + as Box + } + crate::backend::vm::VMValue::Float(f) => { + Box::new(crate::boxes::math_box::FloatBox::new(f)) + } + crate::backend::vm::VMValue::Bool(b) => { + Box::new(crate::box_trait::BoolBox::new(b)) + } + crate::backend::vm::VMValue::String(s) => { + Box::new(crate::box_trait::StringBox::new(s)) + } crate::backend::vm::VMValue::BoxRef(b) => b.share_box(), _ => Box::new(crate::box_trait::VoidBox::new()), }; - let _ = with_current_vm_mut(|vm| { crate::backend::gc_helpers::gc_write_barrier_site(vm.runtime_ref(), "HostAPI.Array.set"); }); + let _ = with_current_vm_mut(|vm| { + crate::backend::gc_helpers::gc_write_barrier_site( + vm.runtime_ref(), + "HostAPI.Array.set", + ); + }); let out = arr.set(Box::new(crate::box_trait::IntegerBox::new(idx)), vb); let vmv = crate::backend::vm::VMValue::from_nyash_box(out); let buf = tlv_encode_one(&vmv); return encode_out(out_ptr, out_len, &buf); } } - 102 => { // len() + 102 => { + // len() let len = arr.len(); let buf = tlv_encode_one(&crate::backend::vm::VMValue::Integer(len as i64)); return encode_out(out_ptr, out_len, &buf); @@ -318,7 +515,10 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64, } } 200 | 201 | 202 | 203 | 204 => { - if let Some(map) = recv_arc.as_any().downcast_ref::() { + if let Some(map) = recv_arc + .as_any() + .downcast_ref::() + { match selector_id { 200 | 201 => { let out = map.size(); @@ -326,16 +526,27 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64, let buf = tlv_encode_one(&vmv); return encode_out(out_ptr, out_len, &buf); } - 202 => { // has(key) + 202 => { + // has(key) if argv.len() >= 1 { let key_box: Box = match argv[0].clone() { - crate::backend::vm::VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)), - crate::backend::vm::VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)), - crate::backend::vm::VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)), - crate::backend::vm::VMValue::String(s) => Box::new(crate::box_trait::StringBox::new(s)), + crate::backend::vm::VMValue::Integer(i) => { + Box::new(crate::box_trait::IntegerBox::new(i)) + } + crate::backend::vm::VMValue::Float(f) => { + Box::new(crate::boxes::math_box::FloatBox::new(f)) + } + crate::backend::vm::VMValue::Bool(b) => { + Box::new(crate::box_trait::BoolBox::new(b)) + } + crate::backend::vm::VMValue::String(s) => { + Box::new(crate::box_trait::StringBox::new(s)) + } crate::backend::vm::VMValue::BoxRef(b) => b.share_box(), crate::backend::vm::VMValue::Future(fu) => Box::new(fu), - crate::backend::vm::VMValue::Void => Box::new(crate::box_trait::VoidBox::new()), + crate::backend::vm::VMValue::Void => { + Box::new(crate::box_trait::VoidBox::new()) + } }; let out = map.has(key_box); let vmv = crate::backend::vm::VMValue::from_nyash_box(out); @@ -343,16 +554,27 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64, return encode_out(out_ptr, out_len, &buf); } } - 203 => { // get(key) + 203 => { + // get(key) if argv.len() >= 1 { let key_box: Box = match argv[0].clone() { - crate::backend::vm::VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)), - crate::backend::vm::VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)), - crate::backend::vm::VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)), - crate::backend::vm::VMValue::String(s) => Box::new(crate::box_trait::StringBox::new(s)), + crate::backend::vm::VMValue::Integer(i) => { + Box::new(crate::box_trait::IntegerBox::new(i)) + } + crate::backend::vm::VMValue::Float(f) => { + Box::new(crate::boxes::math_box::FloatBox::new(f)) + } + crate::backend::vm::VMValue::Bool(b) => { + Box::new(crate::box_trait::BoolBox::new(b)) + } + crate::backend::vm::VMValue::String(s) => { + Box::new(crate::box_trait::StringBox::new(s)) + } crate::backend::vm::VMValue::BoxRef(b) => b.share_box(), crate::backend::vm::VMValue::Future(fu) => Box::new(fu), - crate::backend::vm::VMValue::Void => Box::new(crate::box_trait::VoidBox::new()), + crate::backend::vm::VMValue::Void => { + Box::new(crate::box_trait::VoidBox::new()) + } }; let out = map.get(key_box); let vmv = crate::backend::vm::VMValue::from_nyash_box(out); @@ -360,25 +582,46 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64, return encode_out(out_ptr, out_len, &buf); } } - 204 => { // set(key, value) + 204 => { + // set(key, value) if argv.len() >= 2 { let key_box: Box = match argv[0].clone() { - crate::backend::vm::VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)), - crate::backend::vm::VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)), - crate::backend::vm::VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)), - crate::backend::vm::VMValue::String(s) => Box::new(crate::box_trait::StringBox::new(s)), + crate::backend::vm::VMValue::Integer(i) => { + Box::new(crate::box_trait::IntegerBox::new(i)) + } + crate::backend::vm::VMValue::Float(f) => { + Box::new(crate::boxes::math_box::FloatBox::new(f)) + } + crate::backend::vm::VMValue::Bool(b) => { + Box::new(crate::box_trait::BoolBox::new(b)) + } + crate::backend::vm::VMValue::String(s) => { + Box::new(crate::box_trait::StringBox::new(s)) + } crate::backend::vm::VMValue::BoxRef(b) => b.share_box(), crate::backend::vm::VMValue::Future(fu) => Box::new(fu), - crate::backend::vm::VMValue::Void => Box::new(crate::box_trait::VoidBox::new()), + crate::backend::vm::VMValue::Void => { + Box::new(crate::box_trait::VoidBox::new()) + } }; let val_box: Box = match argv[1].clone() { - crate::backend::vm::VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)), - crate::backend::vm::VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)), - crate::backend::vm::VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)), - crate::backend::vm::VMValue::String(s) => Box::new(crate::box_trait::StringBox::new(s)), + crate::backend::vm::VMValue::Integer(i) => { + Box::new(crate::box_trait::IntegerBox::new(i)) + } + crate::backend::vm::VMValue::Float(f) => { + Box::new(crate::boxes::math_box::FloatBox::new(f)) + } + crate::backend::vm::VMValue::Bool(b) => { + Box::new(crate::box_trait::BoolBox::new(b)) + } + crate::backend::vm::VMValue::String(s) => { + Box::new(crate::box_trait::StringBox::new(s)) + } crate::backend::vm::VMValue::BoxRef(b) => b.share_box(), crate::backend::vm::VMValue::Future(fu) => Box::new(fu), - crate::backend::vm::VMValue::Void => Box::new(crate::box_trait::VoidBox::new()), + crate::backend::vm::VMValue::Void => { + Box::new(crate::box_trait::VoidBox::new()) + } }; let out = map.set(key_box, val_box); let vmv = crate::backend::vm::VMValue::from_nyash_box(out); @@ -391,7 +634,10 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64, } } 300 => { - if let Some(sb) = recv_arc.as_any().downcast_ref::() { + if let Some(sb) = recv_arc + .as_any() + .downcast_ref::() + { let out = crate::backend::vm::VMValue::Integer(sb.value.len() as i64); let buf = tlv_encode_one(&out); return encode_out(out_ptr, out_len, &buf); diff --git a/src/runtime/host_handles.rs b/src/runtime/host_handles.rs index 0a96105f..b26e5983 100644 --- a/src/runtime/host_handles.rs +++ b/src/runtime/host_handles.rs @@ -8,7 +8,10 @@ use once_cell::sync::OnceCell; use std::collections::HashMap; -use std::sync::{Arc, RwLock, atomic::{AtomicU64, Ordering}}; +use std::sync::{ + atomic::{AtomicU64, Ordering}, + Arc, RwLock, +}; use crate::box_trait::NyashBox; @@ -18,26 +21,44 @@ struct Registry { } impl Registry { - fn new() -> Self { Self { next: AtomicU64::new(1), map: RwLock::new(HashMap::new()) } } + fn new() -> Self { + Self { + next: AtomicU64::new(1), + map: RwLock::new(HashMap::new()), + } + } fn alloc(&self, obj: Arc) -> u64 { let h = self.next.fetch_add(1, Ordering::Relaxed); - if let Ok(mut m) = self.map.write() { m.insert(h, obj); } + if let Ok(mut m) = self.map.write() { + m.insert(h, obj); + } h } fn get(&self, h: u64) -> Option> { self.map.read().ok().and_then(|m| m.get(&h).cloned()) } #[allow(dead_code)] - fn drop_handle(&self, h: u64) { if let Ok(mut m) = self.map.write() { m.remove(&h); } } + fn drop_handle(&self, h: u64) { + if let Ok(mut m) = self.map.write() { + m.remove(&h); + } + } } static REG: OnceCell = OnceCell::new(); -fn reg() -> &'static Registry { REG.get_or_init(Registry::new) } +fn reg() -> &'static Registry { + REG.get_or_init(Registry::new) +} /// Box → HostHandle (u64) -pub fn to_handle_box(bx: Box) -> u64 { reg().alloc(Arc::from(bx)) } +pub fn to_handle_box(bx: Box) -> u64 { + reg().alloc(Arc::from(bx)) +} /// Arc → HostHandle (u64) -pub fn to_handle_arc(arc: Arc) -> u64 { reg().alloc(arc) } +pub fn to_handle_arc(arc: Arc) -> u64 { + reg().alloc(arc) +} /// HostHandle(u64) → Arc -pub fn get(h: u64) -> Option> { reg().get(h) } - +pub fn get(h: u64) -> Option> { + reg().get(h) +} diff --git a/src/runtime/leak_tracker.rs b/src/runtime/leak_tracker.rs index bd330395..7dd59f58 100644 --- a/src/runtime/leak_tracker.rs +++ b/src/runtime/leak_tracker.rs @@ -2,33 +2,53 @@ use once_cell::sync::Lazy; use std::collections::HashMap; use std::sync::Mutex; -static ENABLED: Lazy = Lazy::new(|| std::env::var("NYASH_LEAK_LOG").unwrap_or_default() == "1"); -static LEAKS: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); +static ENABLED: Lazy = + Lazy::new(|| std::env::var("NYASH_LEAK_LOG").unwrap_or_default() == "1"); +static LEAKS: Lazy>> = + Lazy::new(|| Mutex::new(HashMap::new())); -pub fn init() { let _ = &*REPORTER; } +pub fn init() { + let _ = &*REPORTER; +} pub fn register_plugin(box_type: &str, instance_id: u32) { - if !*ENABLED { return; } - LEAKS.lock().unwrap().insert((box_type.to_string(), instance_id), "plugin"); + if !*ENABLED { + return; + } + LEAKS + .lock() + .unwrap() + .insert((box_type.to_string(), instance_id), "plugin"); } pub fn finalize_plugin(box_type: &str, instance_id: u32) { - if !*ENABLED { return; } - LEAKS.lock().unwrap().remove(&(box_type.to_string(), instance_id)); + if !*ENABLED { + return; + } + LEAKS + .lock() + .unwrap() + .remove(&(box_type.to_string(), instance_id)); } struct Reporter; impl Drop for Reporter { fn drop(&mut self) { - if !*ENABLED { return; } + if !*ENABLED { + return; + } let m = LEAKS.lock().unwrap(); - if m.is_empty() { return; } + if m.is_empty() { + return; + } eprintln!("[leak] Detected {} non-finalized plugin boxes:", m.len()); for ((ty, id), _) in m.iter() { - eprintln!(" - {}(id={}) not finalized (missing fini or scope)", ty, id); + eprintln!( + " - {}(id={}) not finalized (missing fini or scope)", + ty, id + ); } } } static REPORTER: Lazy = Lazy::new(|| Reporter); - diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index a5090a2e..ff7d6d16 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1,41 +1,46 @@ //! Nyashランタイムモジュール -//! +//! //! プラグインシステムとBox管理の中核 -pub mod plugin_config; pub mod box_registry; -pub mod plugin_loader_v2; -pub mod plugin_loader_unified; -pub mod plugin_ffi_common; -pub mod leak_tracker; -pub mod unified_registry; -pub mod nyash_runtime; pub mod gc; -pub mod scheduler; pub mod global_hooks; +pub mod leak_tracker; +pub mod nyash_runtime; +pub mod plugin_config; +pub mod plugin_ffi_common; +pub mod plugin_loader_unified; +pub mod plugin_loader_v2; +pub mod scheduler; pub mod semantics; +pub mod unified_registry; // pub mod plugin_box; // legacy - 古いPluginBox // pub mod plugin_loader; // legacy - Host VTable使用 -pub mod type_meta; -pub mod type_box_abi; // Phase 12: Nyash ABI (vtable) 雛形 -pub mod type_registry; // Phase 12: TypeId→TypeBox 解決(雛形) -pub mod host_handles; // C ABI(TLV) 向け HostHandle レジストリ(ユーザー/内蔵Box受け渡し) -pub mod host_api; // C ABI: plugins -> host 逆呼び出しAPI(TLSでVMに橋渡し) pub mod extern_registry; // ExternCall (env.*) 登録・診断用レジストリ -pub mod modules_registry; // env.modules minimal registry +pub mod host_api; // C ABI: plugins -> host 逆呼び出しAPI(TLSでVMに橋渡し) +pub mod host_handles; // C ABI(TLV) 向け HostHandle レジストリ(ユーザー/内蔵Box受け渡し) +pub mod modules_registry; +pub mod type_box_abi; // Phase 12: Nyash ABI (vtable) 雛形 +pub mod type_meta; +pub mod type_registry; // Phase 12: TypeId→TypeBox 解決(雛形) // env.modules minimal registry #[cfg(test)] mod tests; +pub use box_registry::{get_global_registry, BoxFactoryRegistry, BoxProvider}; pub use plugin_config::PluginConfig; -pub use box_registry::{BoxFactoryRegistry, BoxProvider, get_global_registry}; -pub use plugin_loader_v2::{PluginLoaderV2, get_global_loader_v2, init_global_loader_v2}; -pub use plugin_loader_unified::{PluginHost, get_global_plugin_host, init_global_plugin_host, PluginLibraryHandle, PluginBoxType, MethodHandle}; +pub use plugin_loader_unified::{ + get_global_plugin_host, init_global_plugin_host, MethodHandle, PluginBoxType, PluginHost, + PluginLibraryHandle, +}; +pub use plugin_loader_v2::{get_global_loader_v2, init_global_loader_v2, PluginLoaderV2}; pub mod cache_versions; -pub use unified_registry::{get_global_unified_registry, init_global_unified_registry, register_user_defined_factory}; +pub use gc::{BarrierKind, GcHooks}; pub use nyash_runtime::{NyashRuntime, NyashRuntimeBuilder}; -pub use gc::{GcHooks, BarrierKind}; pub use scheduler::{Scheduler, SingleThreadScheduler}; +pub use unified_registry::{ + get_global_unified_registry, init_global_unified_registry, register_user_defined_factory, +}; // pub use plugin_box::PluginBox; // legacy // Use unified plugin loader (formerly v2) // pub use plugin_loader::{PluginLoaderV2 as PluginLoader, get_global_loader_v2 as get_global_loader}; // legacy diff --git a/src/runtime/modules_registry.rs b/src/runtime/modules_registry.rs index b7842f00..d10c3f07 100644 --- a/src/runtime/modules_registry.rs +++ b/src/runtime/modules_registry.rs @@ -1,12 +1,13 @@ //! Minimal global registry for env.modules (Phase 15 P0b) +use once_cell::sync::Lazy; use std::collections::HashMap; use std::sync::Mutex; -use once_cell::sync::Lazy; use crate::box_trait::NyashBox; -static REGISTRY: Lazy>>> = Lazy::new(|| Mutex::new(HashMap::new())); +static REGISTRY: Lazy>>> = + Lazy::new(|| Mutex::new(HashMap::new())); pub fn set(name: String, value: Box) { if let Ok(mut map) = REGISTRY.lock() { @@ -23,4 +24,3 @@ pub fn get(name: &str) -> Option> { } None } - diff --git a/src/runtime/nyash_runtime.rs b/src/runtime/nyash_runtime.rs index 612bf679..2416969a 100644 --- a/src/runtime/nyash_runtime.rs +++ b/src/runtime/nyash_runtime.rs @@ -6,11 +6,11 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex, RwLock}; -use crate::core::model::BoxDeclaration; -use crate::box_factory::{UnifiedBoxRegistry, BoxFactory}; use crate::box_factory::builtin::BuiltinBoxFactory; #[cfg(feature = "plugins")] use crate::box_factory::plugin::PluginBoxFactory; +use crate::box_factory::{BoxFactory, UnifiedBoxRegistry}; +use crate::core::model::BoxDeclaration; /// Core runtime container for executing Nyash programs pub struct NyashRuntime { @@ -21,7 +21,7 @@ pub struct NyashRuntime { /// GC hooks (switchable runtime). Default is no-op. pub gc: Arc, /// Optional scheduler (single-thread by default is fine) - pub scheduler: Option>, + pub scheduler: Option>, } impl NyashRuntime { @@ -31,7 +31,9 @@ impl NyashRuntime { box_registry: create_default_registry(), box_declarations: Arc::new(RwLock::new(HashMap::new())), gc: Arc::new(crate::runtime::gc::NullGc), - scheduler: Some(Arc::new(crate::runtime::scheduler::SingleThreadScheduler::new())), + scheduler: Some(Arc::new( + crate::runtime::scheduler::SingleThreadScheduler::new(), + )), } } } @@ -40,18 +42,26 @@ impl NyashRuntime { pub struct NyashRuntimeBuilder { box_registry: Option>>, box_declarations: Option>>>, - gc: Option>, + gc: Option>, scheduler: Option>, } impl NyashRuntimeBuilder { pub fn new() -> Self { - Self { box_registry: None, box_declarations: None, gc: None, scheduler: None } + Self { + box_registry: None, + box_declarations: None, + gc: None, + scheduler: None, + } } /// Inject a BoxFactory implementation directly into a private registry pub fn with_factory(mut self, factory: Arc) -> Self { - let registry = self.box_registry.take().unwrap_or_else(|| create_default_registry()); + let registry = self + .box_registry + .take() + .unwrap_or_else(|| create_default_registry()); if let Ok(mut reg) = registry.lock() { reg.register(factory); } @@ -68,13 +78,21 @@ impl NyashRuntimeBuilder { } pub fn build(self) -> NyashRuntime { - let registry = self.box_registry.unwrap_or_else(|| create_default_registry()); + let registry = self + .box_registry + .unwrap_or_else(|| create_default_registry()); NyashRuntime { box_registry: registry, - box_declarations: self.box_declarations.unwrap_or_else(|| Arc::new(RwLock::new(HashMap::new()))), - gc: self.gc.unwrap_or_else(|| Arc::new(crate::runtime::gc::NullGc)), - scheduler: Some(self.scheduler.unwrap_or_else(|| Arc::new(crate::runtime::scheduler::SingleThreadScheduler::new()))), + box_declarations: self + .box_declarations + .unwrap_or_else(|| Arc::new(RwLock::new(HashMap::new()))), + gc: self + .gc + .unwrap_or_else(|| Arc::new(crate::runtime::gc::NullGc)), + scheduler: Some(self.scheduler.unwrap_or_else(|| { + Arc::new(crate::runtime::scheduler::SingleThreadScheduler::new()) + })), } } } @@ -115,7 +133,9 @@ impl NyashRuntimeBuilder { /// Convenience: use SingleThreadScheduler pub fn with_single_thread_scheduler(mut self) -> Self { - self.scheduler = Some(Arc::new(crate::runtime::scheduler::SingleThreadScheduler::new())); + self.scheduler = Some(Arc::new( + crate::runtime::scheduler::SingleThreadScheduler::new(), + )); self } } diff --git a/src/runtime/plugin_config.rs b/src/runtime/plugin_config.rs index e4b2e5d1..d7907024 100644 --- a/src/runtime/plugin_config.rs +++ b/src/runtime/plugin_config.rs @@ -1,5 +1,5 @@ //! プラグイン設定(nyash.toml)の読み込み -//! +//! //! シンプルな実装から始める - 必要最小限の機能のみ use std::collections::HashMap; @@ -17,14 +17,14 @@ pub struct PluginConfig { impl PluginConfig { /// nyash.tomlを読み込む pub fn load_from_file(path: impl AsRef) -> Result { - let content = fs::read_to_string(path) - .map_err(|e| format!("Failed to read nyash.toml: {}", e))?; - + let content = + fs::read_to_string(path).map_err(|e| format!("Failed to read nyash.toml: {}", e))?; + Self::parse(&content) } - + /// 設定文字列をパース(シンプル版) - /// + /// /// 対応フォーマット: /// ```toml /// [plugins] @@ -34,15 +34,15 @@ impl PluginConfig { pub fn parse(content: &str) -> Result { let mut config = PluginConfig::default(); let mut in_plugins_section = false; - + for line in content.lines() { let line = line.trim(); - + // 空行やコメントはスキップ if line.is_empty() || line.starts_with('#') { continue; } - + // セクション検出 if line == "[plugins]" { in_plugins_section = true; @@ -51,7 +51,7 @@ impl PluginConfig { in_plugins_section = false; continue; } - + // plugins セクション内の設定を読む if in_plugins_section { if let Some((key, value)) = line.split_once('=') { @@ -61,7 +61,7 @@ impl PluginConfig { } } } - + Ok(config) } } @@ -69,7 +69,7 @@ impl PluginConfig { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_parse_simple_config() { let toml = r#" @@ -80,20 +80,23 @@ StringBox = "mystring" [other] something = "else" "#; - + let config = PluginConfig::parse(toml).unwrap(); assert_eq!(config.plugins.get("FileBox"), Some(&"filebox".to_string())); - assert_eq!(config.plugins.get("StringBox"), Some(&"mystring".to_string())); + assert_eq!( + config.plugins.get("StringBox"), + Some(&"mystring".to_string()) + ); assert_eq!(config.plugins.len(), 2); } - + #[test] fn test_parse_empty_config() { let toml = ""; let config = PluginConfig::parse(toml).unwrap(); assert!(config.plugins.is_empty()); } - + #[test] fn test_parse_with_comments() { let toml = r#" @@ -103,10 +106,10 @@ something = "else" FileBox = "filebox" # StringBox = "disabled" # This is commented out "#; - + let config = PluginConfig::parse(toml).unwrap(); assert_eq!(config.plugins.get("FileBox"), Some(&"filebox".to_string())); assert_eq!(config.plugins.get("StringBox"), None); assert_eq!(config.plugins.len(), 1); } -} \ No newline at end of file +} diff --git a/src/runtime/plugin_ffi_common.rs b/src/runtime/plugin_ffi_common.rs index 5ee806dd..43c4b530 100644 --- a/src/runtime/plugin_ffi_common.rs +++ b/src/runtime/plugin_ffi_common.rs @@ -4,7 +4,9 @@ use crate::box_trait::NyashBox; /// Encode empty TLV arguments: version=1, argc=0 -pub fn encode_empty_args() -> Vec { vec![1u8, 0, 0, 0] } +pub fn encode_empty_args() -> Vec { + vec![1u8, 0, 0, 0] +} /// Encode TLV header with argc (no payload entries encoded here) pub fn encode_tlv_header(argc: u16) -> Vec { @@ -34,39 +36,56 @@ pub fn encode_args(args: &[Box]) -> Vec { pub mod decode { /// Try to parse a u32 instance id from an output buffer (little-endian). pub fn instance_id(buf: &[u8]) -> Option { - if buf.len() < 4 { return None; } + if buf.len() < 4 { + return None; + } Some(u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]])) } /// Parse TLV header from buffer; returns (tag, size, payload_slice) pub fn tlv_first(buf: &[u8]) -> Option<(u8, usize, &[u8])> { - if buf.len() < 8 { return None; } + if buf.len() < 8 { + return None; + } let tag = buf[4]; let size = u16::from_le_bytes([buf[6], buf[7]]) as usize; - if buf.len() < 8 + size { return None; } + if buf.len() < 8 + size { + return None; + } Some((tag, size, &buf[8..8 + size])) } /// Decode u64 payload (size must be 8) pub fn u64(payload: &[u8]) -> Option { - if payload.len() != 8 { return None; } - let mut b = [0u8;8]; b.copy_from_slice(payload); + if payload.len() != 8 { + return None; + } + let mut b = [0u8; 8]; + b.copy_from_slice(payload); Some(u64::from_le_bytes(b)) } /// Decode bool payload (size must be 1; nonzero => true) pub fn bool(payload: &[u8]) -> Option { - if payload.len() != 1 { return None; } + if payload.len() != 1 { + return None; + } Some(payload[0] != 0) } /// Decode i32 payload (size must be 4) pub fn i32(payload: &[u8]) -> Option { - if payload.len() != 4 { return None; } - let mut b = [0u8;4]; b.copy_from_slice(payload); + if payload.len() != 4 { + return None; + } + let mut b = [0u8; 4]; + b.copy_from_slice(payload); Some(i32::from_le_bytes(b)) } /// Decode f64 payload (size must be 8) pub fn f64(payload: &[u8]) -> Option { - if payload.len() != 8 { return None; } - let mut b = [0u8;8]; b.copy_from_slice(payload); + if payload.len() != 8 { + return None; + } + let mut b = [0u8; 8]; + b.copy_from_slice(payload); Some(f64::from_le_bytes(b)) } /// Decode UTF-8 string/bytes @@ -76,9 +95,11 @@ pub mod decode { /// Decode plugin handle payload (type_id:u32 + instance_id:u32) pub fn plugin_handle(payload: &[u8]) -> Option<(u32, u32)> { - if payload.len() != 8 { return None; } - let mut a = [0u8;4]; - let mut b = [0u8;4]; + if payload.len() != 8 { + return None; + } + let mut a = [0u8; 4]; + let mut b = [0u8; 4]; a.copy_from_slice(&payload[0..4]); b.copy_from_slice(&payload[4..8]); Some((u32::from_le_bytes(a), u32::from_le_bytes(b))) @@ -86,16 +107,22 @@ pub mod decode { /// Get nth TLV entry from a buffer with header pub fn tlv_nth(buf: &[u8], n: usize) -> Option<(u8, usize, &[u8])> { - if buf.len() < 4 { return None; } + if buf.len() < 4 { + return None; + } let mut off = 4usize; for i in 0..=n { - if buf.len() < off + 4 { return None; } + if buf.len() < off + 4 { + return None; + } let tag = buf[off]; - let _rsv = buf[off+1]; - let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize; - if buf.len() < off + 4 + size { return None; } + let _rsv = buf[off + 1]; + let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize; + if buf.len() < off + 4 + size { + return None; + } if i == n { - return Some((tag, size, &buf[off+4..off+4+size])); + return Some((tag, size, &buf[off + 4..off + 4 + size])); } off += 4 + size; } @@ -197,7 +224,7 @@ pub mod encode { #[cfg(test)] mod tests { - use super::{encode, decode}; + use super::{decode, encode}; #[test] fn test_encode_decode_bool() { @@ -234,7 +261,7 @@ mod tests { fn test_encode_decode_string_and_bytes() { let mut buf = super::encode_tlv_header(2); encode::string(&mut buf, "hello"); - encode::bytes(&mut buf, &[1,2,3,4]); + encode::bytes(&mut buf, &[1, 2, 3, 4]); // First entry string let (tag, _sz, payload) = decode::tlv_nth(&buf, 0).unwrap(); assert_eq!(tag, 6); @@ -243,6 +270,6 @@ mod tests { let (tag2, sz2, payload2) = decode::tlv_nth(&buf, 1).unwrap(); assert_eq!(tag2, 7); assert_eq!(sz2, 4); - assert_eq!(payload2, &[1,2,3,4]); + assert_eq!(payload2, &[1, 2, 3, 4]); } } diff --git a/src/runtime/plugin_loader_unified.rs b/src/runtime/plugin_loader_unified.rs index 53bad736..8deab0f5 100644 --- a/src/runtime/plugin_loader_unified.rs +++ b/src/runtime/plugin_loader_unified.rs @@ -3,9 +3,9 @@ //! Thin wrapper over v2 loader to provide a stable facade //! with minimal, friendly API for runtime/runner and future transports. -use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; use std::cell::Cell; +use std::sync::{Arc, RwLock}; use crate::bid::{BidError, BidResult}; use crate::config::nyash_toml_v2::NyashConfigV2; @@ -44,7 +44,11 @@ pub struct PluginHost { impl PluginHost { pub fn new(loader: Arc>) -> Self { - Self { loader, config: None, config_path: None } + Self { + loader, + config: None, + config_path: None, + } } /// Load config and dynamic libraries, keeping a local config cache. @@ -58,7 +62,8 @@ impl PluginHost { let canonical = std::fs::canonicalize(config_path) .map(|p| p.to_string_lossy().to_string()) .unwrap_or_else(|_| config_path.to_string()); - self.config = Some(NyashConfigV2::from_file(&canonical).map_err(|_| BidError::PluginError)?); + self.config = + Some(NyashConfigV2::from_file(&canonical).map_err(|_| BidError::PluginError)?); self.config_path = Some(canonical); // Delegate actual library loads + pre-birth singletons to v2 @@ -67,20 +72,32 @@ impl PluginHost { } /// Register built-ins or user-defined boxes if needed (no-op for now). - pub fn register_boxes(&self) -> BidResult<()> { Ok(()) } + pub fn register_boxes(&self) -> BidResult<()> { + Ok(()) + } /// Expose read-only view of loaded config for callers migrating from v2 paths. - pub fn config_ref(&self) -> Option<&NyashConfigV2> { self.config.as_ref() } + pub fn config_ref(&self) -> Option<&NyashConfigV2> { + self.config.as_ref() + } /// Resolve a method handle for a given plugin box type and method name. pub fn resolve_method(&self, box_type: &str, method_name: &str) -> BidResult { let cfg = self.config.as_ref().ok_or(BidError::PluginError)?; - let (lib_name, _lib_def) = cfg.find_library_for_box(box_type).ok_or(BidError::InvalidType)?; + let (lib_name, _lib_def) = cfg + .find_library_for_box(box_type) + .ok_or(BidError::InvalidType)?; let cfg_path = self.config_path.as_deref().unwrap_or("nyash.toml"); let toml_content = std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?; - let toml_value: toml::Value = toml::from_str(&toml_content).map_err(|_| BidError::PluginError)?; - let box_conf = cfg.get_box_config(lib_name, box_type, &toml_value).ok_or(BidError::InvalidType)?; - let m = box_conf.methods.get(method_name).ok_or(BidError::InvalidMethod)?; + let toml_value: toml::Value = + toml::from_str(&toml_content).map_err(|_| BidError::PluginError)?; + let box_conf = cfg + .get_box_config(lib_name, box_type, &toml_value) + .ok_or(BidError::InvalidType)?; + let m = box_conf + .methods + .get(method_name) + .ok_or(BidError::InvalidMethod)?; Ok(MethodHandle { lib: lib_name.to_string(), box_type: box_type.to_string(), @@ -91,7 +108,11 @@ impl PluginHost { } // --- v2 adapter layer: allow gradual migration of callers --- - pub fn create_box(&self, box_type: &str, args: &[Box]) -> BidResult> { + pub fn create_box( + &self, + box_type: &str, + args: &[Box], + ) -> BidResult> { let l = self.loader.read().unwrap(); l.create_box(box_type, args) } @@ -137,7 +158,10 @@ impl PluginHost { if iface_name == "env.future" && method_name == "await" { use crate::boxes::result::NyashResultBox; if let Some(arg0) = args.get(0) { - if let Some(fut) = arg0.as_any().downcast_ref::() { + if let Some(fut) = arg0 + .as_any() + .downcast_ref::() + { let max_ms: u64 = crate::config::env::await_max_ms(); let start = std::time::Instant::now(); let mut spins = 0usize; @@ -145,18 +169,24 @@ impl PluginHost { crate::runtime::global_hooks::safepoint_and_poll(); std::thread::yield_now(); 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) { let err = crate::box_trait::StringBox::new("Timeout"); return Ok(Some(Box::new(NyashResultBox::new_err(Box::new(err))))); } } - return Ok(fut.wait_and_get().ok().map(|v| Box::new(NyashResultBox::new_ok(v)) as Box)); + return Ok(fut.wait_and_get().ok().map(|v| { + Box::new(NyashResultBox::new_ok(v)) as Box + })); } else { return Ok(Some(Box::new(NyashResultBox::new_ok(arg0.clone_box())))); } } - return Ok(Some(Box::new(NyashResultBox::new_err(Box::new(crate::box_trait::StringBox::new("InvalidArgs")))))); + return Ok(Some(Box::new(NyashResultBox::new_err(Box::new( + crate::box_trait::StringBox::new("InvalidArgs"), + ))))); } let l = self.loader.read().unwrap(); l.extern_call(iface_name, method_name, args) @@ -169,7 +199,9 @@ static GLOBAL_HOST: Lazy>> = Lazy::new(|| { Arc::new(RwLock::new(PluginHost::new(loader))) }); -pub fn get_global_plugin_host() -> Arc> { GLOBAL_HOST.clone() } +pub fn get_global_plugin_host() -> Arc> { + GLOBAL_HOST.clone() +} pub fn init_global_plugin_host(config_path: &str) -> BidResult<()> { let host = get_global_plugin_host(); diff --git a/src/runtime/plugin_loader_v2/enabled/errors.rs b/src/runtime/plugin_loader_v2/enabled/errors.rs index d77fc306..4441689c 100644 --- a/src/runtime/plugin_loader_v2/enabled/errors.rs +++ b/src/runtime/plugin_loader_v2/enabled/errors.rs @@ -1,4 +1,4 @@ -use crate::bid::{BidResult, BidError}; +use crate::bid::{BidError, BidResult}; // Minimal helpers to keep loader.rs lean and consistent #[inline] @@ -12,5 +12,6 @@ pub fn from_toml(_r: Result) -> BidResult { } #[inline] -pub fn or_plugin_err(opt: Option) -> BidResult { opt.ok_or(BidError::PluginError) } - +pub fn or_plugin_err(opt: Option) -> BidResult { + opt.ok_or(BidError::PluginError) +} diff --git a/src/runtime/plugin_loader_v2/enabled/globals.rs b/src/runtime/plugin_loader_v2/enabled/globals.rs index 1fd56bc1..5573d983 100644 --- a/src/runtime/plugin_loader_v2/enabled/globals.rs +++ b/src/runtime/plugin_loader_v2/enabled/globals.rs @@ -1,12 +1,14 @@ use super::loader::PluginLoaderV2; -use crate::bid::{BidResult}; +use crate::bid::BidResult; use once_cell::sync::Lazy; use std::sync::{Arc, RwLock}; static GLOBAL_LOADER_V2: Lazy>> = Lazy::new(|| Arc::new(RwLock::new(PluginLoaderV2::new()))); -pub fn get_global_loader_v2() -> Arc> { GLOBAL_LOADER_V2.clone() } +pub fn get_global_loader_v2() -> Arc> { + GLOBAL_LOADER_V2.clone() +} pub fn init_global_loader_v2(config_path: &str) -> BidResult<()> { let loader = get_global_loader_v2(); @@ -24,4 +26,3 @@ pub fn shutdown_plugins_v2() -> BidResult<()> { loader.shutdown_singletons(); Ok(()) } - diff --git a/src/runtime/plugin_loader_v2/enabled/host_bridge.rs b/src/runtime/plugin_loader_v2/enabled/host_bridge.rs index ec99dae1..033c87bd 100644 --- a/src/runtime/plugin_loader_v2/enabled/host_bridge.rs +++ b/src/runtime/plugin_loader_v2/enabled/host_bridge.rs @@ -1,6 +1,7 @@ // Host bridge helpers for TypeBox invoke (minimal, v2) -pub type InvokeFn = unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32; +pub type InvokeFn = + unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32; // Call invoke_id with a temporary output buffer; returns (code, bytes_written, buffer) pub fn invoke_alloc( @@ -25,4 +26,3 @@ pub fn invoke_alloc( }; (code, out_len, out) } - diff --git a/src/runtime/plugin_loader_v2/enabled/loader.rs b/src/runtime/plugin_loader_v2/enabled/loader.rs index ef292098..0fc636c3 100644 --- a/src/runtime/plugin_loader_v2/enabled/loader.rs +++ b/src/runtime/plugin_loader_v2/enabled/loader.rs @@ -1,14 +1,16 @@ -use super::types::{PluginBoxMetadata, PluginBoxV2, PluginHandleInner, LoadedPluginV2}; -use crate::bid::{BidResult, BidError}; +use super::types::{LoadedPluginV2, PluginBoxMetadata, PluginBoxV2, PluginHandleInner}; +use crate::bid::{BidError, BidResult}; use crate::box_trait::NyashBox; -use crate::config::nyash_toml_v2::{NyashConfigV2, LibraryDefinition}; +use crate::config::nyash_toml_v2::{LibraryDefinition, NyashConfigV2}; use std::collections::HashMap; -use std::sync::{Arc, RwLock}; use std::path::{Path, PathBuf}; +use std::sync::{Arc, RwLock}; use libloading::{Library, Symbol}; -fn dbg_on() -> bool { std::env::var("NYASH_DEBUG_PLUGIN").unwrap_or_default() == "1" } +fn dbg_on() -> bool { + std::env::var("NYASH_DEBUG_PLUGIN").unwrap_or_default() == "1" +} #[derive(Debug, Clone, Default)] struct LoadedBoxSpec { @@ -17,14 +19,17 @@ struct LoadedBoxSpec { fini_method_id: Option, } #[derive(Debug, Clone, Copy)] -struct MethodSpec { method_id: u32, returns_result: bool } +struct MethodSpec { + method_id: u32, + returns_result: bool, +} pub struct PluginLoaderV2 { pub(super) plugins: RwLock>>, pub config: Option, pub(super) config_path: Option, - pub(super) singletons: RwLock>>, - pub(super) box_specs: RwLock>, + pub(super) singletons: RwLock>>, + pub(super) box_specs: RwLock>, } impl PluginLoaderV2 { @@ -39,12 +44,19 @@ impl PluginLoaderV2 { } pub fn load_config(&mut self, config_path: &str) -> BidResult<()> { - let canonical = std::fs::canonicalize(config_path).map(|p| p.to_string_lossy().to_string()).unwrap_or_else(|_| config_path.to_string()); + let canonical = std::fs::canonicalize(config_path) + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_else(|_| config_path.to_string()); self.config_path = Some(canonical.clone()); - self.config = Some(NyashConfigV2::from_file(&canonical).map_err(|_| BidError::PluginError)?); + self.config = + Some(NyashConfigV2::from_file(&canonical).map_err(|_| BidError::PluginError)?); if let Some(cfg) = self.config.as_ref() { let mut labels: Vec = Vec::new(); - for (_lib, def) in &cfg.libraries { for bt in &def.boxes { labels.push(format!("BoxRef:{}", bt)); } } + for (_lib, def) in &cfg.libraries { + for bt in &def.boxes { + labels.push(format!("BoxRef:{}", bt)); + } + } crate::runtime::cache_versions::bump_many(&labels); } Ok(()) @@ -52,8 +64,12 @@ impl PluginLoaderV2 { pub fn load_all_plugins(&self) -> BidResult<()> { let config = self.config.as_ref().ok_or(BidError::PluginError)?; - for (lib_name, lib_def) in &config.libraries { let _ = self.load_plugin(lib_name, lib_def); } - for (plugin_name, root) in &config.plugins { let _ = self.load_plugin_from_root(plugin_name, root); } + for (lib_name, lib_def) in &config.libraries { + let _ = self.load_plugin(lib_name, lib_def); + } + for (plugin_name, root) in &config.plugins { + let _ = self.load_plugin_from_root(plugin_name, root); + } self.prebirth_singletons()?; Ok(()) } @@ -88,24 +104,38 @@ impl PluginLoaderV2 { if let Some(fname) = c.file_name().and_then(|s| s.to_str()) { if let Some(resolved) = cfg.resolve_plugin_path(fname) { let pb = PathBuf::from(resolved); - if pb.exists() { lib_path = Some(pb); break; } + if pb.exists() { + lib_path = Some(pb); + break; + } } } } } } let lib_path = lib_path.unwrap_or_else(|| base.to_path_buf()); - if dbg_on() { eprintln!("[PluginLoaderV2] load_plugin: lib='{}' path='{}'", lib_name, lib_path.display()); } + if dbg_on() { + eprintln!( + "[PluginLoaderV2] load_plugin: lib='{}' path='{}'", + lib_name, + lib_path.display() + ); + } let lib = unsafe { Library::new(&lib_path) }.map_err(|_| BidError::PluginError)?; let lib_arc = Arc::new(lib); // Resolve required invoke symbol (TypeBox v2: nyash_plugin_invoke) unsafe { - let invoke_sym: Symbol i32> = - lib_arc.get(b"nyash_plugin_invoke\0").map_err(|_| BidError::PluginError)?; + let invoke_sym: Symbol< + unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32, + > = lib_arc + .get(b"nyash_plugin_invoke\0") + .map_err(|_| BidError::PluginError)?; // Optional init (best-effort) - if let Ok(init_sym) = lib_arc.get:: i32>>(b"nyash_plugin_init\0") { + if let Ok(init_sym) = + lib_arc.get:: i32>>(b"nyash_plugin_init\0") + { let _ = init_sym(); } @@ -116,13 +146,18 @@ impl PluginLoaderV2 { init_fn: None, invoke_fn: *invoke_sym, }; - self.plugins.write().map_err(|_| BidError::PluginError)?.insert(lib_name.to_string(), Arc::new(loaded)); + self.plugins + .write() + .map_err(|_| BidError::PluginError)? + .insert(lib_name.to_string(), Arc::new(loaded)); } Ok(()) } - fn load_plugin_from_root(&self, _plugin_name: &str, _root: &str) -> BidResult<()> { Ok(()) } + fn load_plugin_from_root(&self, _plugin_name: &str, _root: &str) -> BidResult<()> { + Ok(()) + } fn prebirth_singletons(&self) -> BidResult<()> { let config = self.config.as_ref().ok_or(BidError::PluginError)?; @@ -131,16 +166,29 @@ impl PluginLoaderV2 { let toml_value: toml::Value = super::errors::from_toml(toml::from_str(&toml_content))?; for (lib_name, lib_def) in &config.libraries { for box_name in &lib_def.boxes { - if let Some(bc) = config.get_box_config(lib_name, box_name, &toml_value) { if bc.singleton { let _ = self.ensure_singleton_handle(lib_name, box_name); } } + if let Some(bc) = config.get_box_config(lib_name, box_name, &toml_value) { + if bc.singleton { + let _ = self.ensure_singleton_handle(lib_name, box_name); + } + } } } Ok(()) } - fn find_box_by_type_id<'a>(&'a self, config: &'a NyashConfigV2, toml_value: &'a toml::Value, type_id: u32) -> Option<(&'a str, &'a str)> { + fn find_box_by_type_id<'a>( + &'a self, + config: &'a NyashConfigV2, + toml_value: &'a toml::Value, + type_id: u32, + ) -> Option<(&'a str, &'a str)> { for (lib_name, lib_def) in &config.libraries { for box_name in &lib_def.boxes { - if let Some(box_conf) = config.get_box_config(lib_name, box_name, toml_value) { if box_conf.type_id == type_id { return Some((lib_name.as_str(), box_name.as_str())); } } + if let Some(box_conf) = config.get_box_config(lib_name, box_name, toml_value) { + if box_conf.type_id == type_id { + return Some((lib_name.as_str(), box_name.as_str())); + } + } } } None @@ -160,12 +208,18 @@ impl PluginLoaderV2 { let mut resolved_type = type_id; let mut fini_method = None; if let Some(spec) = self.box_specs.read().ok()?.get(&spec_key).cloned() { - if let Some(tid) = spec.type_id { resolved_type = tid; } - if let Some(fini) = spec.fini_method_id { fini_method = Some(fini); } + if let Some(tid) = spec.type_id { + resolved_type = tid; + } + if let Some(fini) = spec.fini_method_id { + fini_method = Some(fini); + } } if resolved_type == type_id || fini_method.is_none() { if let Some(cfg) = config.get_box_config(lib_name, box_type, &toml_value) { - if resolved_type == type_id { resolved_type = cfg.type_id; } + if resolved_type == type_id { + resolved_type = cfg.type_id; + } if fini_method.is_none() { fini_method = cfg.methods.get("fini").map(|m| m.method_id); } @@ -180,7 +234,11 @@ impl PluginLoaderV2 { }) } - pub fn construct_existing_instance(&self, type_id: u32, instance_id: u32) -> Option> { + pub fn construct_existing_instance( + &self, + type_id: u32, + instance_id: u32, + ) -> Option> { let config = self.config.as_ref()?; let cfg_path = self.config_path.as_ref()?; let toml_str = std::fs::read_to_string(cfg_path).ok()?; @@ -188,42 +246,122 @@ impl PluginLoaderV2 { let (lib_name, box_type) = self.find_box_by_type_id(config, &toml_value, type_id)?; let plugins = self.plugins.read().ok()?; let plugin = plugins.get(lib_name)?.clone(); - let fini_method_id = if let Some(spec) = self.box_specs.read().ok()?.get(&(lib_name.to_string(), box_type.to_string())) { spec.fini_method_id } else { let box_conf = config.get_box_config(lib_name, box_type, &toml_value)?; box_conf.methods.get("fini").map(|m| m.method_id) }; - let bx = super::types::construct_plugin_box(box_type.to_string(), type_id, plugin.invoke_fn, instance_id, fini_method_id); + let fini_method_id = if let Some(spec) = self + .box_specs + .read() + .ok()? + .get(&(lib_name.to_string(), box_type.to_string())) + { + spec.fini_method_id + } else { + let box_conf = config.get_box_config(lib_name, box_type, &toml_value)?; + box_conf.methods.get("fini").map(|m| m.method_id) + }; + let bx = super::types::construct_plugin_box( + box_type.to_string(), + type_id, + plugin.invoke_fn, + instance_id, + fini_method_id, + ); Some(Box::new(bx)) } fn find_lib_name_for_box(&self, box_type: &str) -> Option { - if let Some(cfg) = &self.config { if let Some((name, _)) = cfg.find_library_for_box(box_type) { return Some(name.to_string()); } } - for ((lib, b), _) in self.box_specs.read().unwrap().iter() { if b == box_type { return Some(lib.clone()); } } + if let Some(cfg) = &self.config { + if let Some((name, _)) = cfg.find_library_for_box(box_type) { + return Some(name.to_string()); + } + } + for ((lib, b), _) in self.box_specs.read().unwrap().iter() { + if b == box_type { + return Some(lib.clone()); + } + } None } fn ensure_singleton_handle(&self, lib_name: &str, box_type: &str) -> BidResult<()> { - if self.singletons.read().unwrap().contains_key(&(lib_name.to_string(), box_type.to_string())) { return Ok(()); } + if self + .singletons + .read() + .unwrap() + .contains_key(&(lib_name.to_string(), box_type.to_string())) + { + return Ok(()); + } let cfg_path = self.config_path.as_deref().unwrap_or("nyash.toml"); - let toml_value: toml::Value = toml::from_str(&std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?).map_err(|_| BidError::PluginError)?; + let toml_value: toml::Value = + toml::from_str(&std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?) + .map_err(|_| BidError::PluginError)?; let config = self.config.as_ref().ok_or(BidError::PluginError)?; let plugins = self.plugins.read().unwrap(); let plugin = plugins.get(lib_name).ok_or(BidError::PluginError)?; - let type_id = if let Some(spec) = self.box_specs.read().unwrap().get(&(lib_name.to_string(), box_type.to_string())) { spec.type_id.unwrap_or_else(|| config.box_types.get(box_type).copied().unwrap_or(0)) } else { let box_conf = config.get_box_config(lib_name, box_type, &toml_value).ok_or(BidError::InvalidType)?; box_conf.type_id }; + let type_id = if let Some(spec) = self + .box_specs + .read() + .unwrap() + .get(&(lib_name.to_string(), box_type.to_string())) + { + spec.type_id + .unwrap_or_else(|| config.box_types.get(box_type).copied().unwrap_or(0)) + } else { + let box_conf = config + .get_box_config(lib_name, box_type, &toml_value) + .ok_or(BidError::InvalidType)?; + box_conf.type_id + }; let out = vec![0u8; 1024]; let out_len = out.len(); let tlv_args = crate::runtime::plugin_ffi_common::encode_empty_args(); - let (birth_result, _len, out_vec) = super::host_bridge::invoke_alloc(plugin.invoke_fn, type_id, 0, 0, &tlv_args); + let (birth_result, _len, out_vec) = + super::host_bridge::invoke_alloc(plugin.invoke_fn, type_id, 0, 0, &tlv_args); let out = out_vec; - if birth_result != 0 || out_len < 4 { return Err(BidError::PluginError); } + if birth_result != 0 || out_len < 4 { + return Err(BidError::PluginError); + } let instance_id = u32::from_le_bytes([out[0], out[1], out[2], out[3]]); - let fini_id = if let Some(spec) = self.box_specs.read().unwrap().get(&(lib_name.to_string(), box_type.to_string())) { spec.fini_method_id } else { let box_conf = config.get_box_config(lib_name, box_type, &toml_value).ok_or(BidError::InvalidType)?; box_conf.methods.get("fini").map(|m| m.method_id) }; - let handle = Arc::new(PluginHandleInner { type_id, invoke_fn: plugin.invoke_fn, instance_id, fini_method_id: fini_id, finalized: std::sync::atomic::AtomicBool::new(false) }); - self.singletons.write().unwrap().insert((lib_name.to_string(), box_type.to_string()), handle); + let fini_id = if let Some(spec) = self + .box_specs + .read() + .unwrap() + .get(&(lib_name.to_string(), box_type.to_string())) + { + spec.fini_method_id + } else { + let box_conf = config + .get_box_config(lib_name, box_type, &toml_value) + .ok_or(BidError::InvalidType)?; + box_conf.methods.get("fini").map(|m| m.method_id) + }; + let handle = Arc::new(PluginHandleInner { + type_id, + invoke_fn: plugin.invoke_fn, + instance_id, + fini_method_id: fini_id, + finalized: std::sync::atomic::AtomicBool::new(false), + }); + self.singletons + .write() + .unwrap() + .insert((lib_name.to_string(), box_type.to_string()), handle); crate::runtime::cache_versions::bump_version(&format!("BoxRef:{}", box_type)); Ok(()) } - pub fn extern_call(&self, iface_name: &str, method_name: &str, args: &[Box]) -> BidResult>> { + pub fn extern_call( + &self, + iface_name: &str, + method_name: &str, + args: &[Box], + ) -> BidResult>> { match (iface_name, method_name) { - ("env.console", "log") => { for a in args { println!("{}", a.to_string_box().value); } Ok(None) } + ("env.console", "log") => { + for a in args { + println!("{}", a.to_string_box().value); + } + Ok(None) + } ("env.modules", "set") => { if args.len() >= 2 { let key = args[0].to_string_box().value; @@ -235,27 +373,110 @@ impl PluginLoaderV2 { ("env.modules", "get") => { if let Some(k) = args.get(0) { let key = k.to_string_box().value; - if let Some(v) = crate::runtime::modules_registry::get(&key) { return Ok(Some(v)); } + if let Some(v) = crate::runtime::modules_registry::get(&key) { + return Ok(Some(v)); + } } Ok(Some(Box::new(crate::box_trait::VoidBox::new()))) } - ("env.task", "cancelCurrent") => { let tok = crate::runtime::global_hooks::current_group_token(); tok.cancel(); Ok(None) } - ("env.task", "currentToken") => { let tok = crate::runtime::global_hooks::current_group_token(); let tb = crate::boxes::token_box::TokenBox::from_token(tok); Ok(Some(Box::new(tb))) } - ("env.debug", "trace") => { if std::env::var("NYASH_DEBUG_TRACE").ok().as_deref() == Some("1") { for a in args { eprintln!("[debug.trace] {}", a.to_string_box().value); } } Ok(None) } - ("env.runtime", "checkpoint") => { if crate::config::env::runtime_checkpoint_trace() { eprintln!("[runtime.checkpoint] reached"); } crate::runtime::global_hooks::safepoint_and_poll(); Ok(None) } - ("env.future", "new") | ("env.future", "birth") => { let fut = crate::boxes::future::FutureBox::new(); if let Some(v) = args.get(0) { fut.set_result(v.clone_box()); } Ok(Some(Box::new(fut))) } - ("env.future", "set") => { if args.len() >= 2 { if let Some(fut) = args[0].as_any().downcast_ref::() { fut.set_result(args[1].clone_box()); } } Ok(None) } - ("env.future", "await") => { use crate::boxes::result::NyashResultBox; if let Some(arg) = args.get(0) { if let Some(fut) = arg.as_any().downcast_ref::() { let max_ms: u64 = crate::config::env::await_max_ms(); let start = std::time::Instant::now(); let mut spins = 0usize; while !fut.ready() { crate::runtime::global_hooks::safepoint_and_poll(); std::thread::yield_now(); spins += 1; if spins % 1024 == 0 { std::thread::sleep(std::time::Duration::from_millis(1)); } if start.elapsed() >= std::time::Duration::from_millis(max_ms) { let err = crate::box_trait::StringBox::new("Timeout"); return Ok(Some(Box::new(NyashResultBox::new_err(Box::new(err))))); } } return match fut.wait_and_get() { Ok(v) => Ok(Some(Box::new(NyashResultBox::new_ok(v)))), Err(e) => { let err = crate::box_trait::StringBox::new(format!("Error: {}", e)); Ok(Some(Box::new(NyashResultBox::new_err(Box::new(err))))) } }; } else { return Ok(Some(Box::new(NyashResultBox::new_ok(arg.clone_box())))); } } Ok(Some(Box::new(crate::boxes::result::NyashResultBox::new_err(Box::new(crate::box_trait::StringBox::new("InvalidArgs")))))) } - _ => Err(BidError::PluginError) + ("env.task", "cancelCurrent") => { + let tok = crate::runtime::global_hooks::current_group_token(); + tok.cancel(); + Ok(None) + } + ("env.task", "currentToken") => { + let tok = crate::runtime::global_hooks::current_group_token(); + let tb = crate::boxes::token_box::TokenBox::from_token(tok); + Ok(Some(Box::new(tb))) + } + ("env.debug", "trace") => { + if std::env::var("NYASH_DEBUG_TRACE").ok().as_deref() == Some("1") { + for a in args { + eprintln!("[debug.trace] {}", a.to_string_box().value); + } + } + Ok(None) + } + ("env.runtime", "checkpoint") => { + if crate::config::env::runtime_checkpoint_trace() { + eprintln!("[runtime.checkpoint] reached"); + } + crate::runtime::global_hooks::safepoint_and_poll(); + Ok(None) + } + ("env.future", "new") | ("env.future", "birth") => { + let fut = crate::boxes::future::FutureBox::new(); + if let Some(v) = args.get(0) { + fut.set_result(v.clone_box()); + } + Ok(Some(Box::new(fut))) + } + ("env.future", "set") => { + if args.len() >= 2 { + if let Some(fut) = args[0] + .as_any() + .downcast_ref::() + { + fut.set_result(args[1].clone_box()); + } + } + Ok(None) + } + ("env.future", "await") => { + use crate::boxes::result::NyashResultBox; + if let Some(arg) = args.get(0) { + if let Some(fut) = arg + .as_any() + .downcast_ref::() + { + let max_ms: u64 = crate::config::env::await_max_ms(); + let start = std::time::Instant::now(); + let mut spins = 0usize; + while !fut.ready() { + crate::runtime::global_hooks::safepoint_and_poll(); + std::thread::yield_now(); + spins += 1; + if spins % 1024 == 0 { + std::thread::sleep(std::time::Duration::from_millis(1)); + } + if start.elapsed() >= std::time::Duration::from_millis(max_ms) { + let err = crate::box_trait::StringBox::new("Timeout"); + return Ok(Some(Box::new(NyashResultBox::new_err(Box::new(err))))); + } + } + return match fut.wait_and_get() { + Ok(v) => Ok(Some(Box::new(NyashResultBox::new_ok(v)))), + Err(e) => { + let err = crate::box_trait::StringBox::new(format!("Error: {}", e)); + Ok(Some(Box::new(NyashResultBox::new_err(Box::new(err))))) + } + }; + } else { + return Ok(Some(Box::new(NyashResultBox::new_ok(arg.clone_box())))); + } + } + Ok(Some(Box::new( + crate::boxes::result::NyashResultBox::new_err(Box::new( + crate::box_trait::StringBox::new("InvalidArgs"), + )), + ))) + } + _ => Err(BidError::PluginError), } } fn resolve_method_id_from_file(&self, box_type: &str, method_name: &str) -> BidResult { let cfg = self.config.as_ref().ok_or(BidError::PluginError)?; let cfg_path = self.config_path.as_deref().unwrap_or("nyash.toml"); - let toml_value: toml::Value = super::errors::from_toml(toml::from_str(&std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?))?; + let toml_value: toml::Value = super::errors::from_toml(toml::from_str( + &std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?, + ))?; if let Some((lib_name, _)) = cfg.find_library_for_box(box_type) { - if let Some(bc) = cfg.get_box_config(&lib_name, box_type, &toml_value) { if let Some(m) = bc.methods.get(method_name) { return Ok(m.method_id); } } + if let Some(bc) = cfg.get_box_config(&lib_name, box_type, &toml_value) { + if let Some(m) = bc.methods.get(method_name) { + return Ok(m.method_id); + } + } } Err(BidError::InvalidMethod) } @@ -264,8 +485,16 @@ impl PluginLoaderV2 { if let Some(cfg) = &self.config { if let Some((lib_name, _)) = cfg.find_library_for_box(box_type) { if let Some(cfg_path) = self.config_path.as_deref() { - if let Ok(toml_value) = toml::from_str::(&std::fs::read_to_string(cfg_path).unwrap_or_default()) { - if let Some(bc) = cfg.get_box_config(&lib_name, box_type, &toml_value) { return bc.methods.get(method_name).map(|m| m.returns_result).unwrap_or(false); } + if let Ok(toml_value) = toml::from_str::( + &std::fs::read_to_string(cfg_path).unwrap_or_default(), + ) { + if let Some(bc) = cfg.get_box_config(&lib_name, box_type, &toml_value) { + return bc + .methods + .get(method_name) + .map(|m| m.returns_result) + .unwrap_or(false); + } } } } @@ -274,41 +503,83 @@ impl PluginLoaderV2 { } /// Resolve (type_id, method_id, returns_result) for a box_type.method - pub fn resolve_method_handle(&self, box_type: &str, method_name: &str) -> BidResult<(u32, u32, bool)> { + pub fn resolve_method_handle( + &self, + box_type: &str, + method_name: &str, + ) -> BidResult<(u32, u32, bool)> { let cfg = self.config.as_ref().ok_or(BidError::PluginError)?; let cfg_path = self.config_path.as_deref().unwrap_or("nyash.toml"); - let toml_value: toml::Value = toml::from_str(&std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?).map_err(|_| BidError::PluginError)?; - let (lib_name, _) = cfg.find_library_for_box(box_type).ok_or(BidError::InvalidType)?; - let bc = cfg.get_box_config(lib_name, box_type, &toml_value).ok_or(BidError::InvalidType)?; + let toml_value: toml::Value = + toml::from_str(&std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?) + .map_err(|_| BidError::PluginError)?; + let (lib_name, _) = cfg + .find_library_for_box(box_type) + .ok_or(BidError::InvalidType)?; + let bc = cfg + .get_box_config(lib_name, box_type, &toml_value) + .ok_or(BidError::InvalidType)?; let m = bc.methods.get(method_name).ok_or(BidError::InvalidMethod)?; Ok((bc.type_id, m.method_id, m.returns_result)) } - pub fn invoke_instance_method(&self, box_type: &str, method_name: &str, instance_id: u32, args: &[Box]) -> BidResult>> { + pub fn invoke_instance_method( + &self, + box_type: &str, + method_name: &str, + instance_id: u32, + args: &[Box], + ) -> BidResult>> { // Non-recursive direct bridge for minimal methods used by semantics and basic VM paths // Resolve library/type/method ids from cached config let cfg = self.config.as_ref().ok_or(BidError::PluginError)?; let cfg_path = self.config_path.as_deref().unwrap_or("nyash.toml"); - let toml_value: toml::Value = toml::from_str(&std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?).map_err(|_| BidError::PluginError)?; - let (lib_name, _lib_def) = cfg.find_library_for_box(box_type).ok_or(BidError::InvalidType)?; - let box_conf = cfg.get_box_config(lib_name, box_type, &toml_value).ok_or(BidError::InvalidType)?; + let toml_value: toml::Value = + toml::from_str(&std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?) + .map_err(|_| BidError::PluginError)?; + let (lib_name, _lib_def) = cfg + .find_library_for_box(box_type) + .ok_or(BidError::InvalidType)?; + let box_conf = cfg + .get_box_config(lib_name, box_type, &toml_value) + .ok_or(BidError::InvalidType)?; let type_id = box_conf.type_id; - let method = box_conf.methods.get(method_name).ok_or(BidError::InvalidMethod)?; + let method = box_conf + .methods + .get(method_name) + .ok_or(BidError::InvalidMethod)?; // Get plugin handle let plugins = self.plugins.read().map_err(|_| BidError::PluginError)?; let plugin = plugins.get(lib_name).ok_or(BidError::PluginError)?; // Encode TLV args via shared helper (numeric→string→toString) let tlv = crate::runtime::plugin_ffi_common::encode_args(args); - let (_code, out_len, out) = super::host_bridge::invoke_alloc(plugin.invoke_fn, type_id, method.method_id, instance_id, &tlv); + let (_code, out_len, out) = super::host_bridge::invoke_alloc( + plugin.invoke_fn, + type_id, + method.method_id, + instance_id, + &tlv, + ); // Decode TLV (first entry) generically - if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) { + if let Some((tag, _sz, payload)) = + crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) + { let bx: Box = match tag { - 1 => Box::new(crate::box_trait::BoolBox::new(crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false))), - 2 => Box::new(crate::box_trait::IntegerBox::new(crate::runtime::plugin_ffi_common::decode::i32(payload).unwrap_or(0) as i64)), + 1 => Box::new(crate::box_trait::BoolBox::new( + crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false), + )), + 2 => Box::new(crate::box_trait::IntegerBox::new( + crate::runtime::plugin_ffi_common::decode::i32(payload).unwrap_or(0) as i64, + )), 3 => { // i64 payload - if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); Box::new(crate::box_trait::IntegerBox::new(i64::from_le_bytes(b))) } - else { Box::new(crate::box_trait::IntegerBox::new(0)) } + if payload.len() == 8 { + let mut b = [0u8; 8]; + b.copy_from_slice(payload); + Box::new(crate::box_trait::IntegerBox::new(i64::from_le_bytes(b))) + } else { + Box::new(crate::box_trait::IntegerBox::new(0)) + } } 5 => { let x = crate::runtime::plugin_ffi_common::decode::f64(payload).unwrap_or(0.0); @@ -320,7 +591,9 @@ impl PluginLoaderV2 { } 8 => { // Plugin handle (type_id, instance_id) → wrap into PluginBoxV2 - if let Some((ret_type, inst)) = crate::runtime::plugin_ffi_common::decode::plugin_handle(payload) { + if let Some((ret_type, inst)) = + crate::runtime::plugin_ffi_common::decode::plugin_handle(payload) + { let handle = Arc::new(super::types::PluginHandleInner { type_id: ret_type, invoke_fn: plugin.invoke_fn, @@ -328,13 +601,20 @@ impl PluginLoaderV2 { fini_method_id: None, finalized: std::sync::atomic::AtomicBool::new(false), }); - Box::new(super::types::PluginBoxV2 { box_type: box_type.to_string(), inner: handle }) - } else { Box::new(crate::box_trait::VoidBox::new()) } + Box::new(super::types::PluginBoxV2 { + box_type: box_type.to_string(), + inner: handle, + }) + } else { + Box::new(crate::box_trait::VoidBox::new()) + } } 9 => { // Host handle (u64) → try to map back to BoxRef, else void if let Some(u) = crate::runtime::plugin_ffi_common::decode::u64(payload) { - if let Some(arc) = crate::runtime::host_handles::get(u) { return Ok(Some(arc.share_box())); } + if let Some(arc) = crate::runtime::host_handles::get(u) { + return Ok(Some(arc.share_box())); + } } Box::new(crate::box_trait::VoidBox::new()) } @@ -345,17 +625,31 @@ impl PluginLoaderV2 { Ok(Some(Box::new(crate::box_trait::VoidBox::new()))) } - pub fn create_box(&self, box_type: &str, _args: &[Box]) -> BidResult> { + pub fn create_box( + &self, + box_type: &str, + _args: &[Box], + ) -> BidResult> { // Non-recursive: directly call plugin 'birth' and construct PluginBoxV2 let cfg = self.config.as_ref().ok_or(BidError::PluginError)?; let cfg_path = self.config_path.as_deref().unwrap_or("nyash.toml"); - let toml_value: toml::Value = toml::from_str(&std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?).map_err(|_| BidError::PluginError)?; - let (lib_name, _) = cfg.find_library_for_box(box_type).ok_or(BidError::InvalidType)?; + let toml_value: toml::Value = + toml::from_str(&std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?) + .map_err(|_| BidError::PluginError)?; + let (lib_name, _) = cfg + .find_library_for_box(box_type) + .ok_or(BidError::InvalidType)?; // Resolve type_id and method ids - let box_conf = cfg.get_box_config(lib_name, box_type, &toml_value).ok_or(BidError::InvalidType)?; + let box_conf = cfg + .get_box_config(lib_name, box_type, &toml_value) + .ok_or(BidError::InvalidType)?; let type_id = box_conf.type_id; - let birth_id = box_conf.methods.get("birth").map(|m| m.method_id).ok_or(BidError::InvalidMethod)?; + let birth_id = box_conf + .methods + .get("birth") + .map(|m| m.method_id) + .ok_or(BidError::InvalidMethod)?; let fini_id = box_conf.methods.get("fini").map(|m| m.method_id); // Get loaded plugin invoke @@ -364,15 +658,26 @@ impl PluginLoaderV2 { // Call birth (no args TLV) and read returned instance id (little-endian u32 in bytes 0..4) if dbg_on() { - eprintln!("[PluginLoaderV2] invoking birth: box_type={} type_id={} birth_id={}", box_type, type_id, birth_id); + eprintln!( + "[PluginLoaderV2] invoking birth: box_type={} type_id={} birth_id={}", + box_type, type_id, birth_id + ); } let tlv = crate::runtime::plugin_ffi_common::encode_empty_args(); - let (code, out_len, out_buf) = super::host_bridge::invoke_alloc(plugin.invoke_fn, type_id, birth_id, 0, &tlv); + let (code, out_len, out_buf) = + super::host_bridge::invoke_alloc(plugin.invoke_fn, type_id, birth_id, 0, &tlv); if dbg_on() { eprintln!("[PluginLoaderV2] create_box: box_type={} type_id={} birth_id={} code={} out_len={}", box_type, type_id, birth_id, code, out_len); - if out_len > 0 { eprintln!("[PluginLoaderV2] create_box: out[0..min(8)]={:02x?}", &out_buf[..out_len.min(8)]); } + if out_len > 0 { + eprintln!( + "[PluginLoaderV2] create_box: out[0..min(8)]={:02x?}", + &out_buf[..out_len.min(8)] + ); + } + } + if code != 0 || out_len < 4 { + return Err(BidError::PluginError); } - if code != 0 || out_len < 4 { return Err(BidError::PluginError); } let instance_id = u32::from_le_bytes([out_buf[0], out_buf[1], out_buf[2], out_buf[3]]); let bx = PluginBoxV2 { @@ -391,6 +696,8 @@ impl PluginLoaderV2 { /// Shutdown singletons: finalize and clear all singleton handles pub fn shutdown_singletons(&self) { let mut map = self.singletons.write().unwrap(); - for (_, handle) in map.drain() { handle.finalize_now(); } + for (_, handle) in map.drain() { + handle.finalize_now(); + } } } diff --git a/src/runtime/plugin_loader_v2/enabled/mod.rs b/src/runtime/plugin_loader_v2/enabled/mod.rs index fa2dc694..08b90d99 100644 --- a/src/runtime/plugin_loader_v2/enabled/mod.rs +++ b/src/runtime/plugin_loader_v2/enabled/mod.rs @@ -1,12 +1,15 @@ -mod types; -mod loader; -mod globals; mod errors; +mod globals; mod host_bridge; +mod loader; +mod types; -pub use types::{PluginBoxMetadata, PluginBoxV2, PluginHandleInner, NyashTypeBoxFfi, make_plugin_box_v2, construct_plugin_box}; -pub use loader::PluginLoaderV2; pub use globals::{get_global_loader_v2, init_global_loader_v2, shutdown_plugins_v2}; +pub use loader::PluginLoaderV2; +pub use types::{ + construct_plugin_box, make_plugin_box_v2, NyashTypeBoxFfi, PluginBoxMetadata, PluginBoxV2, + PluginHandleInner, +}; pub fn metadata_for_type_id(type_id: u32) -> Option { let loader = get_global_loader_v2(); @@ -14,4 +17,6 @@ pub fn metadata_for_type_id(type_id: u32) -> Option { guard.metadata_for_type_id(type_id) } -pub fn backend_kind() -> &'static str { "enabled" } +pub fn backend_kind() -> &'static str { + "enabled" +} diff --git a/src/runtime/plugin_loader_v2/enabled/types.rs b/src/runtime/plugin_loader_v2/enabled/types.rs index 8f5c8868..1a0d58d2 100644 --- a/src/runtime/plugin_loader_v2/enabled/types.rs +++ b/src/runtime/plugin_loader_v2/enabled/types.rs @@ -1,9 +1,11 @@ -use crate::box_trait::{BoxCore, NyashBox, StringBox}; use super::host_bridge::InvokeFn; +use crate::box_trait::{BoxCore, NyashBox, StringBox}; use std::any::Any; use std::sync::Arc; -fn dbg_on() -> bool { std::env::var("NYASH_DEBUG_PLUGIN").unwrap_or_default() == "1" } +fn dbg_on() -> bool { + std::env::var("NYASH_DEBUG_PLUGIN").unwrap_or_default() == "1" +} /// Loaded plugin information (library handle + exported addresses) pub struct LoadedPluginV2 { @@ -36,9 +38,18 @@ pub struct PluginHandleInner { impl Drop for PluginHandleInner { fn drop(&mut self) { if let Some(fini_id) = self.fini_method_id { - if !self.finalized.swap(true, std::sync::atomic::Ordering::SeqCst) { + if !self + .finalized + .swap(true, std::sync::atomic::Ordering::SeqCst) + { let tlv_args: [u8; 4] = [1, 0, 0, 0]; - let _ = super::host_bridge::invoke_alloc(self.invoke_fn, self.type_id, fini_id, self.instance_id, &tlv_args); + let _ = super::host_bridge::invoke_alloc( + self.invoke_fn, + self.type_id, + fini_id, + self.instance_id, + &tlv_args, + ); } } } @@ -47,10 +58,19 @@ impl Drop for PluginHandleInner { impl PluginHandleInner { pub fn finalize_now(&self) { if let Some(fini_id) = self.fini_method_id { - if !self.finalized.swap(true, std::sync::atomic::Ordering::SeqCst) { + if !self + .finalized + .swap(true, std::sync::atomic::Ordering::SeqCst) + { crate::runtime::leak_tracker::finalize_plugin("PluginBox", self.instance_id); let tlv_args: [u8; 4] = [1, 0, 0, 0]; - let _ = super::host_bridge::invoke_alloc(self.invoke_fn, self.type_id, fini_id, self.instance_id, &tlv_args); + let _ = super::host_bridge::invoke_alloc( + self.invoke_fn, + self.type_id, + fini_id, + self.instance_id, + &tlv_args, + ); } } } @@ -76,46 +96,113 @@ pub struct PluginBoxV2 { } impl BoxCore for PluginBoxV2 { - fn box_id(&self) -> u64 { self.inner.instance_id as u64 } - fn parent_type_id(&self) -> Option { None } - fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}({})", self.box_type, self.inner.instance_id) } - fn as_any(&self) -> &dyn Any { self } - fn as_any_mut(&mut self) -> &mut dyn Any { self } + fn box_id(&self) -> u64 { + self.inner.instance_id as u64 + } + fn parent_type_id(&self) -> Option { + None + } + fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}({})", self.box_type, self.inner.instance_id) + } + fn as_any(&self) -> &dyn Any { + self + } + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } } impl NyashBox for PluginBoxV2 { - fn is_identity(&self) -> bool { true } - fn type_name(&self) -> &'static str { - match self.box_type.as_str() { "FileBox" => "FileBox", _ => "PluginBoxV2" } + fn is_identity(&self) -> bool { + true } - fn clone_box(&self) -> Box { - if dbg_on() { eprintln!("[PluginBoxV2] clone_box {}({})", self.box_type, self.inner.instance_id); } - let tlv_args = [1u8, 0, 0, 0]; - let (result, out_len, out_buf) = super::host_bridge::invoke_alloc(self.inner.invoke_fn, self.inner.type_id, 0, 0, &tlv_args); - if result == 0 && out_len >= 4 { - let new_instance_id = u32::from_le_bytes([out_buf[0], out_buf[1], out_buf[2], out_buf[3]]); - Box::new(PluginBoxV2 { - box_type: self.box_type.clone(), - inner: Arc::new(PluginHandleInner { type_id: self.inner.type_id, invoke_fn: self.inner.invoke_fn, instance_id: new_instance_id, fini_method_id: self.inner.fini_method_id, finalized: std::sync::atomic::AtomicBool::new(false) }), - }) - } else { - Box::new(StringBox::new(format!("Clone failed for {}", self.box_type))) + fn type_name(&self) -> &'static str { + match self.box_type.as_str() { + "FileBox" => "FileBox", + _ => "PluginBoxV2", } } - fn to_string_box(&self) -> StringBox { StringBox::new(format!("{}({})", self.box_type, self.inner.instance_id)) } - fn equals(&self, _other: &dyn NyashBox) -> crate::box_trait::BoolBox { crate::box_trait::BoolBox::new(false) } - fn share_box(&self) -> Box { Box::new(PluginBoxV2 { box_type: self.box_type.clone(), inner: self.inner.clone() }) } + fn clone_box(&self) -> Box { + if dbg_on() { + eprintln!( + "[PluginBoxV2] clone_box {}({})", + self.box_type, self.inner.instance_id + ); + } + let tlv_args = [1u8, 0, 0, 0]; + let (result, out_len, out_buf) = super::host_bridge::invoke_alloc( + self.inner.invoke_fn, + self.inner.type_id, + 0, + 0, + &tlv_args, + ); + if result == 0 && out_len >= 4 { + let new_instance_id = + u32::from_le_bytes([out_buf[0], out_buf[1], out_buf[2], out_buf[3]]); + Box::new(PluginBoxV2 { + box_type: self.box_type.clone(), + inner: Arc::new(PluginHandleInner { + type_id: self.inner.type_id, + invoke_fn: self.inner.invoke_fn, + instance_id: new_instance_id, + fini_method_id: self.inner.fini_method_id, + finalized: std::sync::atomic::AtomicBool::new(false), + }), + }) + } else { + Box::new(StringBox::new(format!( + "Clone failed for {}", + self.box_type + ))) + } + } + fn to_string_box(&self) -> StringBox { + StringBox::new(format!("{}({})", self.box_type, self.inner.instance_id)) + } + fn equals(&self, _other: &dyn NyashBox) -> crate::box_trait::BoolBox { + crate::box_trait::BoolBox::new(false) + } + fn share_box(&self) -> Box { + Box::new(PluginBoxV2 { + box_type: self.box_type.clone(), + inner: self.inner.clone(), + }) + } } impl PluginBoxV2 { - pub fn instance_id(&self) -> u32 { self.inner.instance_id } - pub fn finalize_now(&self) { self.inner.finalize_now() } - pub fn is_finalized(&self) -> bool { self.inner.finalized.load(std::sync::atomic::Ordering::SeqCst) } + pub fn instance_id(&self) -> u32 { + self.inner.instance_id + } + pub fn finalize_now(&self) { + self.inner.finalize_now() + } + pub fn is_finalized(&self) -> bool { + self.inner + .finalized + .load(std::sync::atomic::Ordering::SeqCst) + } } /// Helper to construct a PluginBoxV2 from raw ids and invoke pointer safely -pub fn make_plugin_box_v2(box_type: String, type_id: u32, instance_id: u32, invoke_fn: InvokeFn) -> PluginBoxV2 { - PluginBoxV2 { box_type, inner: Arc::new(PluginHandleInner { type_id, invoke_fn, instance_id, fini_method_id: None, finalized: std::sync::atomic::AtomicBool::new(false) }) } +pub fn make_plugin_box_v2( + box_type: String, + type_id: u32, + instance_id: u32, + invoke_fn: InvokeFn, +) -> PluginBoxV2 { + PluginBoxV2 { + box_type, + inner: Arc::new(PluginHandleInner { + type_id, + invoke_fn, + instance_id, + fini_method_id: None, + finalized: std::sync::atomic::AtomicBool::new(false), + }), + } } /// Public helper to construct a PluginBoxV2 from raw parts (for VM/JIT integration) @@ -126,5 +213,14 @@ pub fn construct_plugin_box( instance_id: u32, fini_method_id: Option, ) -> PluginBoxV2 { - PluginBoxV2 { box_type, inner: Arc::new(PluginHandleInner { type_id, invoke_fn, instance_id, fini_method_id, finalized: std::sync::atomic::AtomicBool::new(false) }) } + PluginBoxV2 { + box_type, + inner: Arc::new(PluginHandleInner { + type_id, + invoke_fn, + instance_id, + fini_method_id, + finalized: std::sync::atomic::AtomicBool::new(false), + }), + } } diff --git a/src/runtime/plugin_loader_v2/mod.rs b/src/runtime/plugin_loader_v2/mod.rs index f4c97c98..14ecc035 100644 --- a/src/runtime/plugin_loader_v2/mod.rs +++ b/src/runtime/plugin_loader_v2/mod.rs @@ -9,4 +9,3 @@ mod stub; pub use enabled::*; #[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))] pub use stub::*; - diff --git a/src/runtime/plugin_loader_v2/stub.rs b/src/runtime/plugin_loader_v2/stub.rs index 65a684db..fb59fa6e 100644 --- a/src/runtime/plugin_loader_v2/stub.rs +++ b/src/runtime/plugin_loader_v2/stub.rs @@ -1,9 +1,10 @@ -use crate::bid::{BidResult, BidError}; +use crate::bid::{BidError, BidResult}; use crate::box_trait::NyashBox; use once_cell::sync::Lazy; use std::sync::{Arc, RwLock}; -pub type InvokeFn = unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32; +pub type InvokeFn = + unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32; #[derive(Debug, Clone)] pub struct PluginBoxV2 { @@ -28,32 +29,82 @@ pub struct PluginHandleInner { pub fini_method_id: Option, } -pub struct PluginLoaderV2 { pub config: Option<()> } -impl PluginLoaderV2 { pub fn new() -> Self { Self { config: None } } } +pub struct PluginLoaderV2 { + pub config: Option<()>, +} +impl PluginLoaderV2 { + pub fn new() -> Self { + Self { config: None } + } +} impl PluginLoaderV2 { - pub fn load_config(&mut self, _p: &str) -> BidResult<()> { Ok(()) } - pub fn load_all_plugins(&self) -> BidResult<()> { Ok(()) } - pub fn create_box(&self, _t: &str, _a: &[Box]) -> BidResult> { Err(BidError::PluginError) } - pub fn extern_call(&self, _iface_name: &str, _method_name: &str, _args: &[Box]) -> BidResult>> { Err(BidError::PluginError) } - pub fn invoke_instance_method(&self, _box_type: &str, _method_name: &str, _instance_id: u32, _args: &[Box]) -> BidResult>> { Err(BidError::PluginError) } - pub fn metadata_for_type_id(&self, _type_id: u32) -> Option { None } + pub fn load_config(&mut self, _p: &str) -> BidResult<()> { + Ok(()) + } + pub fn load_all_plugins(&self) -> BidResult<()> { + Ok(()) + } + pub fn create_box(&self, _t: &str, _a: &[Box]) -> BidResult> { + Err(BidError::PluginError) + } + pub fn extern_call( + &self, + _iface_name: &str, + _method_name: &str, + _args: &[Box], + ) -> BidResult>> { + Err(BidError::PluginError) + } + pub fn invoke_instance_method( + &self, + _box_type: &str, + _method_name: &str, + _instance_id: u32, + _args: &[Box], + ) -> BidResult>> { + Err(BidError::PluginError) + } + pub fn metadata_for_type_id(&self, _type_id: u32) -> Option { + None + } pub fn shutdown_singletons(&self) {} } -static GLOBAL_LOADER_V2: Lazy>> = Lazy::new(|| Arc::new(RwLock::new(PluginLoaderV2::new()))); -pub fn get_global_loader_v2() -> Arc> { GLOBAL_LOADER_V2.clone() } -pub fn init_global_loader_v2(_config_path: &str) -> BidResult<()> { Ok(()) } -pub fn shutdown_plugins_v2() -> BidResult<()> { Ok(()) } +static GLOBAL_LOADER_V2: Lazy>> = + Lazy::new(|| Arc::new(RwLock::new(PluginLoaderV2::new()))); +pub fn get_global_loader_v2() -> Arc> { + GLOBAL_LOADER_V2.clone() +} +pub fn init_global_loader_v2(_config_path: &str) -> BidResult<()> { + Ok(()) +} +pub fn shutdown_plugins_v2() -> BidResult<()> { + Ok(()) +} -pub fn backend_kind() -> &'static str { "stub" } +pub fn backend_kind() -> &'static str { + "stub" +} -pub fn metadata_for_type_id(_type_id: u32) -> Option { None } +pub fn metadata_for_type_id(_type_id: u32) -> Option { + None +} -pub fn make_plugin_box_v2(box_type: String, type_id: u32, instance_id: u32, invoke_fn: InvokeFn) -> PluginBoxV2 { +pub fn make_plugin_box_v2( + box_type: String, + type_id: u32, + instance_id: u32, + invoke_fn: InvokeFn, +) -> PluginBoxV2 { PluginBoxV2 { box_type, - inner: Arc::new(PluginHandleInner { type_id, invoke_fn, instance_id, fini_method_id: None }), + inner: Arc::new(PluginHandleInner { + type_id, + invoke_fn, + instance_id, + fini_method_id: None, + }), } } @@ -66,6 +117,11 @@ pub fn construct_plugin_box( ) -> PluginBoxV2 { PluginBoxV2 { box_type, - inner: Arc::new(PluginHandleInner { type_id, invoke_fn, instance_id, fini_method_id }), + inner: Arc::new(PluginHandleInner { + type_id, + invoke_fn, + instance_id, + fini_method_id, + }), } } diff --git a/src/runtime/scheduler.rs b/src/runtime/scheduler.rs index 1a15d55e..60722cff 100644 --- a/src/runtime/scheduler.rs +++ b/src/runtime/scheduler.rs @@ -10,10 +10,15 @@ pub trait Scheduler: Send + Sync { /// Poll scheduler: run due tasks and a limited number of queued tasks. fn poll(&self) {} /// Cooperative yield point (no-op for single-thread). - fn yield_now(&self) { } + fn yield_now(&self) {} /// Optional: spawn with a cancellation token. Default delegates to spawn. - fn spawn_with_token(&self, name: &str, _token: CancellationToken, f: Box) { + fn spawn_with_token( + &self, + name: &str, + _token: CancellationToken, + f: Box, + ) { self.spawn(name, f) } } @@ -30,17 +35,24 @@ pub struct SingleThreadScheduler { impl SingleThreadScheduler { pub fn new() -> Self { - Self { queue: Arc::new(Mutex::new(VecDeque::new())), delayed: Arc::new(Mutex::new(Vec::new())) } + Self { + queue: Arc::new(Mutex::new(VecDeque::new())), + delayed: Arc::new(Mutex::new(Vec::new())), + } } } impl Scheduler for SingleThreadScheduler { fn spawn(&self, _name: &str, f: Box) { - if let Ok(mut q) = self.queue.lock() { q.push_back(f); } + if let Ok(mut q) = self.queue.lock() { + q.push_back(f); + } } fn spawn_after(&self, delay_ms: u64, _name: &str, f: Box) { let when = Instant::now() + Duration::from_millis(delay_ms); - if let Ok(mut d) = self.delayed.lock() { d.push((when, f)); } + if let Ok(mut d) = self.delayed.lock() { + d.push((when, f)); + } } fn poll(&self) { // Move due delayed tasks to queue @@ -52,20 +64,36 @@ impl Scheduler for SingleThreadScheduler { while i < d.len() { if d[i].0 <= now { let (_when, task) = d.remove(i); - if let Ok(mut q) = self.queue.lock() { q.push_back(task); } + if let Ok(mut q) = self.queue.lock() { + q.push_back(task); + } moved += 1; - } else { i += 1; } + } else { + i += 1; + } } } // Run up to budget queued tasks let budget: usize = std::env::var("NYASH_SCHED_POLL_BUDGET") - .ok().and_then(|s| s.parse().ok()).filter(|&n: &usize| n > 0).unwrap_or(1); + .ok() + .and_then(|s| s.parse().ok()) + .filter(|&n: &usize| n > 0) + .unwrap_or(1); let mut ran = 0usize; while ran < budget { let task_opt = { - if let Ok(mut q) = self.queue.lock() { q.pop_front() } else { None } + if let Ok(mut q) = self.queue.lock() { + q.pop_front() + } else { + None + } }; - if let Some(task) = task_opt { task(); ran += 1; } else { break; } + if let Some(task) = task_opt { + task(); + ran += 1; + } else { + break; + } } if trace { eprintln!("[SCHED] poll moved={} ran={} budget={}", moved, ran, budget); @@ -80,7 +108,13 @@ use std::sync::atomic::{AtomicBool, Ordering}; pub struct CancellationToken(Arc); impl CancellationToken { - pub fn new() -> Self { Self(Arc::new(AtomicBool::new(false))) } - pub fn cancel(&self) { self.0.store(true, Ordering::SeqCst); } - pub fn is_cancelled(&self) -> bool { self.0.load(Ordering::SeqCst) } + pub fn new() -> Self { + Self(Arc::new(AtomicBool::new(false))) + } + pub fn cancel(&self) { + self.0.store(true, Ordering::SeqCst); + } + pub fn is_cancelled(&self) -> bool { + self.0.load(Ordering::SeqCst) + } } diff --git a/src/runtime/semantics.rs b/src/runtime/semantics.rs index 011ab3c7..2f19e7c8 100644 --- a/src/runtime/semantics.rs +++ b/src/runtime/semantics.rs @@ -5,20 +5,27 @@ * string/number coercions and common operation ordering. */ -use crate::box_trait::{NyashBox, StringBox, IntegerBox}; +use crate::box_trait::{IntegerBox, NyashBox, StringBox}; /// Try to unwrap InstanceBox and return inner if present fn maybe_unwrap_instance(b: &dyn NyashBox) -> &dyn NyashBox { if let Some(inst) = b.as_any().downcast_ref::() { - if let Some(ref inner) = inst.inner_content { return inner.as_ref(); } + if let Some(ref inner) = inst.inner_content { + return inner.as_ref(); + } } b } /// Result.Ok(inner) → recurse helper fn maybe_unwrap_result_ok(b: &dyn NyashBox) -> &dyn NyashBox { - if let Some(res) = b.as_any().downcast_ref::() { - if let crate::boxes::result::NyashResultBox::Ok(inner) = res { return inner.as_ref(); } + if let Some(res) = b + .as_any() + .downcast_ref::() + { + if let crate::boxes::result::NyashResultBox::Ok(inner) = res { + return inner.as_ref(); + } } b } @@ -27,23 +34,34 @@ fn maybe_unwrap_result_ok(b: &dyn NyashBox) -> &dyn NyashBox { pub fn coerce_to_string(b: &dyn NyashBox) -> Option { let b = maybe_unwrap_instance(b); // Internal StringBox - if let Some(s) = b.as_any().downcast_ref::() { return Some(s.value.clone()); } + if let Some(s) = b.as_any().downcast_ref::() { + return Some(s.value.clone()); + } // Result.Ok recursion let b2 = maybe_unwrap_result_ok(b); if !std::ptr::eq(b2 as *const _, b as *const _) { - if let Some(s) = coerce_to_string(b2) { return Some(s); } + if let Some(s) = coerce_to_string(b2) { + return Some(s); + } } // Plugin StringBox: prefer toUtf8; fallback to toString - if let Some(pb) = b.as_any().downcast_ref::() { + if let Some(pb) = b + .as_any() + .downcast_ref::() + { // StringBox.toUtf8 if pb.box_type == "StringBox" { let host = crate::runtime::get_global_plugin_host(); let read_res = host.read(); if let Ok(ro) = read_res { - if let Ok(ret) = ro.invoke_instance_method("StringBox", "toUtf8", pb.instance_id(), &[]) { + if let Ok(ret) = + ro.invoke_instance_method("StringBox", "toUtf8", pb.instance_id(), &[]) + { if let Some(vb) = ret { - if let Some(sb2) = vb.as_any().downcast_ref::() { return Some(sb2.value.clone()); } + if let Some(sb2) = vb.as_any().downcast_ref::() { + return Some(sb2.value.clone()); + } } } } @@ -52,8 +70,14 @@ pub fn coerce_to_string(b: &dyn NyashBox) -> Option { let host = crate::runtime::get_global_plugin_host(); let read_res = host.read(); if let Ok(ro) = read_res { - if let Ok(ret) = ro.invoke_instance_method(&pb.box_type, "toString", pb.instance_id(), &[]) { - if let Some(vb) = ret { if let Some(s) = coerce_to_string(vb.as_ref()) { return Some(s); } } + if let Ok(ret) = + ro.invoke_instance_method(&pb.box_type, "toString", pb.instance_id(), &[]) + { + if let Some(vb) = ret { + if let Some(s) = coerce_to_string(vb.as_ref()) { + return Some(s); + } + } } } } @@ -63,17 +87,28 @@ pub fn coerce_to_string(b: &dyn NyashBox) -> Option { /// Best-effort integer coercion used by all backends. pub fn coerce_to_i64(b: &dyn NyashBox) -> Option { let b = maybe_unwrap_instance(b); - if let Some(i) = b.as_any().downcast_ref::() { return Some(i.value); } + if let Some(i) = b.as_any().downcast_ref::() { + return Some(i.value); + } // Plugin numeric getters - if let Some(pb) = b.as_any().downcast_ref::() { + if let Some(pb) = b + .as_any() + .downcast_ref::() + { // IntegerBox.get -> IntegerBox if pb.box_type == "IntegerBox" { let host = crate::runtime::get_global_plugin_host(); let read_res = host.read(); if let Ok(ro) = read_res { - if let Ok(ret) = ro.invoke_instance_method("IntegerBox", "get", pb.instance_id(), &[]) { - if let Some(vb) = ret { if let Some(ii) = vb.as_any().downcast_ref::() { return Some(ii.value); } } + if let Ok(ret) = + ro.invoke_instance_method("IntegerBox", "get", pb.instance_id(), &[]) + { + if let Some(vb) = ret { + if let Some(ii) = vb.as_any().downcast_ref::() { + return Some(ii.value); + } + } } } } @@ -82,8 +117,14 @@ pub fn coerce_to_i64(b: &dyn NyashBox) -> Option { let host = crate::runtime::get_global_plugin_host(); let read_res = host.read(); if let Ok(ro) = read_res { - if let Ok(ret) = ro.invoke_instance_method("FloatBox", "toDouble", pb.instance_id(), &[]) { - if let Some(vb) = ret { if let Some(fb) = vb.as_any().downcast_ref::() { return Some(fb.value as i64); } } + if let Ok(ret) = + ro.invoke_instance_method("FloatBox", "toDouble", pb.instance_id(), &[]) + { + if let Some(vb) = ret { + if let Some(fb) = vb.as_any().downcast_ref::() { + return Some(fb.value as i64); + } + } } } } diff --git a/src/runtime/tests.rs b/src/runtime/tests.rs index 2c916aee..86114aa2 100644 --- a/src/runtime/tests.rs +++ b/src/runtime/tests.rs @@ -1,23 +1,26 @@ //! プラグインシステム統合テスト -//! +//! //! プラグインBoxの透過的切り替えをテスト #[cfg(test)] mod tests { - use super::super::{PluginConfig, BoxFactoryRegistry}; - use crate::runtime::box_registry::BoxProvider; - use crate::box_trait::{NyashBox, StringBox}; + use super::super::{BoxFactoryRegistry, PluginConfig}; use crate::bid::{BidHandle, BoxTypeId}; - + use crate::box_trait::{NyashBox, StringBox}; + use crate::runtime::box_registry::BoxProvider; + fn dummy_filebox_constructor(args: &[Box]) -> Result, String> { // ダミーFileBox作成(ビルトイン版シミュレーション) if args.is_empty() { Ok(Box::new(StringBox::new("DummyFileBox"))) } else { - Ok(Box::new(StringBox::new(&format!("DummyFileBox({})", args[0].to_string_box().value)))) + Ok(Box::new(StringBox::new(&format!( + "DummyFileBox({})", + args[0].to_string_box().value + )))) } } - + #[test] fn test_plugin_config_parsing() { let toml = r#" @@ -25,29 +28,34 @@ mod tests { FileBox = "filebox" StringBox = "custom_string" "#; - + let config = PluginConfig::parse(toml).unwrap(); assert_eq!(config.plugins.get("FileBox"), Some(&"filebox".to_string())); - assert_eq!(config.plugins.get("StringBox"), Some(&"custom_string".to_string())); + assert_eq!( + config.plugins.get("StringBox"), + Some(&"custom_string".to_string()) + ); } - + #[test] fn test_box_registry_builtin() { let registry = BoxFactoryRegistry::new(); registry.register_builtin("FileBox", dummy_filebox_constructor); - + let result = registry.create_box("FileBox", &[]).unwrap(); assert_eq!(result.to_string_box().value, "DummyFileBox"); } - + #[test] fn test_box_registry_plugin_override() { let registry = BoxFactoryRegistry::new(); registry.register_builtin("FileBox", dummy_filebox_constructor); - + // プラグイン設定でビルトインを上書き let mut config = PluginConfig::default(); - config.plugins.insert("FileBox".to_string(), "filebox".to_string()); + config + .plugins + .insert("FileBox".to_string(), "filebox".to_string()); registry.apply_plugin_config(&config); // 生成までは行わず、プロバイダーがプラグインに切り替わったことを確認 @@ -56,55 +64,69 @@ StringBox = "custom_string" _ => panic!("Expected plugin provider for FileBox"), } } - + // TODO: PluginBox型が削除されたためこのテストはコメントアウト // #[test] // fn test_plugin_box_creation() { // let handle = BidHandle::new(BoxTypeId::FileBox as u32, 123); // let plugin_box = PluginBox::new("filebox".to_string(), handle); - // + // // assert_eq!(plugin_box.plugin_name(), "filebox"); // assert_eq!(plugin_box.handle().type_id, BoxTypeId::FileBox as u32); // assert_eq!(plugin_box.handle().instance_id, 123); // } - + // 旧PluginBox直接生成テストは削除(v2統合により不要) - + #[test] fn test_transparent_box_switching() { let registry = BoxFactoryRegistry::new(); - + // 1. ビルトイン版を登録 registry.register_builtin("FileBox", dummy_filebox_constructor); - + // 2. 現在のプロバイダーはビルトイン match registry.get_provider("FileBox").unwrap() { BoxProvider::Builtin(_) => {} _ => panic!("Expected builtin provider before plugin override"), } - + // 3. プラグイン設定を適用 let mut config = PluginConfig::default(); - config.plugins.insert("FileBox".to_string(), "filebox".to_string()); + config + .plugins + .insert("FileBox".to_string(), "filebox".to_string()); registry.apply_plugin_config(&config); - + // 4. プロバイダーがプラグインに切り替わっている match registry.get_provider("FileBox").unwrap() { BoxProvider::Plugin(name) => assert_eq!(name, "filebox"), _ => panic!("Expected plugin provider after override"), } } - + #[test] fn test_multiple_plugin_types() { let mut config = PluginConfig::default(); - config.plugins.insert("FileBox".to_string(), "filebox".to_string()); - config.plugins.insert("StringBox".to_string(), "custom_string".to_string()); - config.plugins.insert("MathBox".to_string(), "advanced_math".to_string()); - + config + .plugins + .insert("FileBox".to_string(), "filebox".to_string()); + config + .plugins + .insert("StringBox".to_string(), "custom_string".to_string()); + config + .plugins + .insert("MathBox".to_string(), "advanced_math".to_string()); + assert_eq!(config.plugins.len(), 3); assert_eq!(config.plugins.get("FileBox"), Some(&"filebox".to_string())); - assert_eq!(config.plugins.get("StringBox"), Some(&"custom_string".to_string())); - assert_eq!(config.plugins.get("MathBox"), Some(&"advanced_math".to_string())); + assert_eq!( + config.plugins.get("StringBox"), + Some(&"custom_string".to_string()) + ); + assert_eq!( + config.plugins.get("MathBox"), + Some(&"advanced_math".to_string()) + ); } } diff --git a/src/runtime/type_box_abi.rs b/src/runtime/type_box_abi.rs index 5cc7141f..9cc76949 100644 --- a/src/runtime/type_box_abi.rs +++ b/src/runtime/type_box_abi.rs @@ -25,8 +25,18 @@ pub struct NyrtValue { } impl NyrtValue { - pub fn void() -> Self { Self { tag: NyrtTag::Void, i64_: 0 } } - pub fn i64(v: i64) -> Self { Self { tag: NyrtTag::I64, i64_: v } } + pub fn void() -> Self { + Self { + tag: NyrtTag::Void, + i64_: 0, + } + } + pub fn i64(v: i64) -> Self { + Self { + tag: NyrtTag::I64, + i64_: v, + } + } } /// Nyash ABI のメソッド関数ポインタ(雛形) @@ -34,7 +44,11 @@ pub type NyrtMethodFn = fn(instance: u64, argc: usize, argv: *const NyrtValue) - /// スロット定義(雛形) #[derive(Clone, Copy)] -pub struct MethodEntry { pub name: &'static str, pub arity: u8, pub slot: u16 } +pub struct MethodEntry { + pub name: &'static str, + pub arity: u8, + pub slot: u16, +} /// TypeBox(雛形): 各型の静的メタデータ(スロット一覧付き) pub struct TypeBox { @@ -43,6 +57,13 @@ pub struct TypeBox { } impl TypeBox { - pub const fn new(type_name: &'static str) -> Self { Self { type_name, methods: &[] } } - pub const fn new_with(type_name: &'static str, methods: &'static [MethodEntry]) -> Self { Self { type_name, methods } } + pub const fn new(type_name: &'static str) -> Self { + Self { + type_name, + methods: &[], + } + } + pub const fn new_with(type_name: &'static str, methods: &'static [MethodEntry]) -> Self { + Self { type_name, methods } + } } diff --git a/src/runtime/type_meta.rs b/src/runtime/type_meta.rs index 9595e6ad..931dc6d0 100644 --- a/src/runtime/type_meta.rs +++ b/src/runtime/type_meta.rs @@ -31,10 +31,25 @@ pub struct MethodThunk { } impl MethodThunk { - pub fn new() -> Self { Self { target: RwLock::new(None), _flags: RwLock::new(0) } } - pub fn get_target(&self) -> Option { self.target.read().ok().and_then(|g| g.clone()) } - pub fn set_mir_target(&self, name: String) { if let Ok(mut g) = self.target.write() { *g = Some(ThunkTarget::MirFunction(name)); } } - pub fn set_plugin_invoke(&self, method_id: u16) { if let Ok(mut g) = self.target.write() { *g = Some(ThunkTarget::PluginInvoke { method_id }); } } + pub fn new() -> Self { + Self { + target: RwLock::new(None), + _flags: RwLock::new(0), + } + } + pub fn get_target(&self) -> Option { + self.target.read().ok().and_then(|g| g.clone()) + } + pub fn set_mir_target(&self, name: String) { + if let Ok(mut g) = self.target.write() { + *g = Some(ThunkTarget::MirFunction(name)); + } + } + pub fn set_plugin_invoke(&self, method_id: u16) { + if let Ok(mut g) = self.target.write() { + *g = Some(ThunkTarget::PluginInvoke { method_id }); + } + } } /// Per-type metadata including thunk table @@ -46,7 +61,10 @@ pub struct TypeMeta { impl TypeMeta { fn new(class_name: String) -> Self { - Self { class_name, thunks: RwLock::new(Vec::new()) } + Self { + class_name, + thunks: RwLock::new(Vec::new()), + } } /// Ensure that the thunk table length is at least `len`. @@ -54,7 +72,9 @@ impl TypeMeta { if let Ok(mut tbl) = self.thunks.write() { if tbl.len() < len { let to_add = len - tbl.len(); - for _ in 0..to_add { tbl.push(Arc::new(MethodThunk::new())); } + for _ in 0..to_add { + tbl.push(Arc::new(MethodThunk::new())); + } } } } @@ -68,12 +88,16 @@ impl TypeMeta { /// Set thunk target name for slot pub fn set_thunk_mir_target(&self, slot: usize, target_name: String) { self.ensure_len(slot + 1); - if let Some(th) = self.get_thunk(slot) { th.set_mir_target(target_name); } + if let Some(th) = self.get_thunk(slot) { + th.set_mir_target(target_name); + } } pub fn set_thunk_plugin_invoke(&self, slot: usize, method_id: u16) { self.ensure_len(slot + 1); - if let Some(th) = self.get_thunk(slot) { th.set_plugin_invoke(method_id); } + if let Some(th) = self.get_thunk(slot) { + th.set_plugin_invoke(method_id); + } } pub fn set_thunk_builtin(&self, slot: usize, method: String) { @@ -91,12 +115,15 @@ impl TypeMeta { } } -static TYPE_META_REGISTRY: Lazy>>> = Lazy::new(|| Mutex::new(HashMap::new())); +static TYPE_META_REGISTRY: Lazy>>> = + Lazy::new(|| Mutex::new(HashMap::new())); /// Get or create TypeMeta for a given class name pub fn get_or_create_type_meta(class_name: &str) -> Arc { let mut map = TYPE_META_REGISTRY.lock().unwrap(); - if let Some(m) = map.get(class_name) { return m.clone(); } + if let Some(m) = map.get(class_name) { + return m.clone(); + } let m = Arc::new(TypeMeta::new(class_name.to_string())); map.insert(class_name.to_string(), m.clone()); m diff --git a/src/runtime/type_registry.rs b/src/runtime/type_registry.rs index 430ebe4d..1dbacac8 100644 --- a/src/runtime/type_registry.rs +++ b/src/runtime/type_registry.rs @@ -19,67 +19,207 @@ * - 400..: Console 系(log/warn/error/clear) */ -use super::type_box_abi::{TypeBox, MethodEntry}; +use super::type_box_abi::{MethodEntry, TypeBox}; // 最小サンプル: MapBox の TypeBox を事前登録(Tier-1 PoC 用) // --- ArrayBox --- const ARRAY_METHODS: &[MethodEntry] = &[ - MethodEntry { name: "get", arity: 1, slot: 100 }, - MethodEntry { name: "set", arity: 2, slot: 101 }, - MethodEntry { name: "len", arity: 0, slot: 102 }, - MethodEntry { name: "length", arity: 0, slot: 102 }, + MethodEntry { + name: "get", + arity: 1, + slot: 100, + }, + MethodEntry { + name: "set", + arity: 2, + slot: 101, + }, + MethodEntry { + name: "len", + arity: 0, + slot: 102, + }, + MethodEntry { + name: "length", + arity: 0, + slot: 102, + }, // P0: vtable coverage extension - MethodEntry { name: "push", arity: 1, slot: 103 }, - MethodEntry { name: "pop", arity: 0, slot: 104 }, - MethodEntry { name: "clear", arity: 0, slot: 105 }, + MethodEntry { + name: "push", + arity: 1, + slot: 103, + }, + MethodEntry { + name: "pop", + arity: 0, + slot: 104, + }, + MethodEntry { + name: "clear", + arity: 0, + slot: 105, + }, // P1: contains/indexOf/join - MethodEntry { name: "contains", arity: 1, slot: 106 }, - MethodEntry { name: "indexOf", arity: 1, slot: 107 }, - MethodEntry { name: "join", arity: 1, slot: 108 }, + MethodEntry { + name: "contains", + arity: 1, + slot: 106, + }, + MethodEntry { + name: "indexOf", + arity: 1, + slot: 107, + }, + MethodEntry { + name: "join", + arity: 1, + slot: 108, + }, // P2: sort/reverse/slice - MethodEntry { name: "sort", arity: 0, slot: 109 }, - MethodEntry { name: "reverse", arity: 0, slot: 110 }, - MethodEntry { name: "slice", arity: 2, slot: 111 }, + MethodEntry { + name: "sort", + arity: 0, + slot: 109, + }, + MethodEntry { + name: "reverse", + arity: 0, + slot: 110, + }, + MethodEntry { + name: "slice", + arity: 2, + slot: 111, + }, ]; static ARRAYBOX_TB: TypeBox = TypeBox::new_with("ArrayBox", ARRAY_METHODS); // --- MapBox --- const MAP_METHODS: &[MethodEntry] = &[ - MethodEntry { name: "size", arity: 0, slot: 200 }, - MethodEntry { name: "len", arity: 0, slot: 201 }, - MethodEntry { name: "has", arity: 1, slot: 202 }, - MethodEntry { name: "get", arity: 1, slot: 203 }, - MethodEntry { name: "set", arity: 2, slot: 204 }, + MethodEntry { + name: "size", + arity: 0, + slot: 200, + }, + MethodEntry { + name: "len", + arity: 0, + slot: 201, + }, + MethodEntry { + name: "has", + arity: 1, + slot: 202, + }, + MethodEntry { + name: "get", + arity: 1, + slot: 203, + }, + MethodEntry { + name: "set", + arity: 2, + slot: 204, + }, // Extended - MethodEntry { name: "delete", arity: 1, slot: 205 }, // alias: remove (同一スロット) - MethodEntry { name: "remove", arity: 1, slot: 205 }, - MethodEntry { name: "keys", arity: 0, slot: 206 }, - MethodEntry { name: "values", arity: 0, slot: 207 }, - MethodEntry { name: "clear", arity: 0, slot: 208 }, + MethodEntry { + name: "delete", + arity: 1, + slot: 205, + }, // alias: remove (同一スロット) + MethodEntry { + name: "remove", + arity: 1, + slot: 205, + }, + MethodEntry { + name: "keys", + arity: 0, + slot: 206, + }, + MethodEntry { + name: "values", + arity: 0, + slot: 207, + }, + MethodEntry { + name: "clear", + arity: 0, + slot: 208, + }, ]; static MAPBOX_TB: TypeBox = TypeBox::new_with("MapBox", MAP_METHODS); // --- StringBox --- const STRING_METHODS: &[MethodEntry] = &[ - MethodEntry { name: "len", arity: 0, slot: 300 }, + MethodEntry { + name: "len", + arity: 0, + slot: 300, + }, // P1: extend String vtable - MethodEntry { name: "substring", arity: 2, slot: 301 }, - MethodEntry { name: "concat", arity: 1, slot: 302 }, - MethodEntry { name: "indexOf", arity: 1, slot: 303 }, - MethodEntry { name: "replace", arity: 2, slot: 304 }, - MethodEntry { name: "trim", arity: 0, slot: 305 }, - MethodEntry { name: "toUpper", arity: 0, slot: 306 }, - MethodEntry { name: "toLower", arity: 0, slot: 307 }, + MethodEntry { + name: "substring", + arity: 2, + slot: 301, + }, + MethodEntry { + name: "concat", + arity: 1, + slot: 302, + }, + MethodEntry { + name: "indexOf", + arity: 1, + slot: 303, + }, + MethodEntry { + name: "replace", + arity: 2, + slot: 304, + }, + MethodEntry { + name: "trim", + arity: 0, + slot: 305, + }, + MethodEntry { + name: "toUpper", + arity: 0, + slot: 306, + }, + MethodEntry { + name: "toLower", + arity: 0, + slot: 307, + }, ]; static STRINGBOX_TB: TypeBox = TypeBox::new_with("StringBox", STRING_METHODS); // --- ConsoleBox --- (WASM v2 unified dispatch 用の雛形) // 400: log(..), 401: warn(..), 402: error(..), 403: clear() const CONSOLE_METHODS: &[MethodEntry] = &[ - MethodEntry { name: "log", arity: 1, slot: 400 }, - MethodEntry { name: "warn", arity: 1, slot: 401 }, - MethodEntry { name: "error", arity: 1, slot: 402 }, - MethodEntry { name: "clear", arity: 0, slot: 403 }, + MethodEntry { + name: "log", + arity: 1, + slot: 400, + }, + MethodEntry { + name: "warn", + arity: 1, + slot: 401, + }, + MethodEntry { + name: "error", + arity: 1, + slot: 402, + }, + MethodEntry { + name: "clear", + arity: 0, + slot: 403, + }, ]; static CONSOLEBOX_TB: TypeBox = TypeBox::new_with("ConsoleBox", CONSOLE_METHODS); @@ -90,10 +230,26 @@ static CONSOLEBOX_TB: TypeBox = TypeBox::new_with("ConsoleBox", CONSOLE_METHODS) // 3: has(name) // 4: size() const INSTANCE_METHODS: &[MethodEntry] = &[ - MethodEntry { name: "getField", arity: 1, slot: 1 }, - MethodEntry { name: "setField", arity: 2, slot: 2 }, - MethodEntry { name: "has", arity: 1, slot: 3 }, - MethodEntry { name: "size", arity: 0, slot: 4 }, + MethodEntry { + name: "getField", + arity: 1, + slot: 1, + }, + MethodEntry { + name: "setField", + arity: 2, + slot: 2, + }, + MethodEntry { + name: "has", + arity: 1, + slot: 3, + }, + MethodEntry { + name: "size", + arity: 0, + slot: 4, + }, ]; static INSTANCEBOX_TB: TypeBox = TypeBox::new_with("InstanceBox", INSTANCE_METHODS); @@ -114,7 +270,9 @@ pub fn resolve_slot_by_name(type_name: &str, method: &str, arity: usize) -> Opti let tb = resolve_typebox_by_name(type_name)?; let ar = arity as u8; for m in tb.methods { - if m.name == method && m.arity == ar { return Some(m.slot); } + if m.name == method && m.arity == ar { + return Some(m.slot); + } } None } diff --git a/src/runtime/unified_registry.rs b/src/runtime/unified_registry.rs index 06e65dde..f4d8dff7 100644 --- a/src/runtime/unified_registry.rs +++ b/src/runtime/unified_registry.rs @@ -1,14 +1,14 @@ /*! * Global Unified Box Registry - * + * * Manages the global instance of UnifiedBoxRegistry * Integrates all Box creation sources (builtin, user-defined, plugin) */ -use crate::box_factory::UnifiedBoxRegistry; use crate::box_factory::builtin::BuiltinBoxFactory; #[cfg(feature = "plugins")] use crate::box_factory::plugin::PluginBoxFactory; +use crate::box_factory::UnifiedBoxRegistry; use std::sync::{Arc, Mutex, OnceLock}; /// Global registry instance @@ -23,15 +23,15 @@ pub fn init_global_unified_registry() { { registry.register(std::sync::Arc::new(BuiltinBoxFactory::new())); } - + // Register plugin Box factory (primary) #[cfg(feature = "plugins")] { registry.register(Arc::new(PluginBoxFactory::new())); } - + // TODO: User-defined Box factory will be registered by interpreter - + Arc::new(Mutex::new(registry)) }); } @@ -46,7 +46,7 @@ pub fn get_global_unified_registry() -> Arc> { pub fn register_user_defined_factory(factory: Arc) { let registry = get_global_unified_registry(); let mut registry_lock = registry.lock().unwrap(); - + // Insert at position 1 (after builtin, before plugin) // This maintains priority: builtin > user > plugin if registry_lock.factories.len() >= 2 { diff --git a/src/scope_tracker.rs b/src/scope_tracker.rs index 95153ce5..741371e1 100644 --- a/src/scope_tracker.rs +++ b/src/scope_tracker.rs @@ -1,14 +1,14 @@ /*! * ScopeTracker - Track Box instances for proper lifecycle management - * + * * Phase 9.78a: Unified Box lifecycle management for VM */ -use std::sync::Arc; use crate::box_trait::NyashBox; use crate::instance_v2::InstanceBox; #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] use crate::runtime::plugin_loader_v2::PluginBoxV2; +use std::sync::Arc; /// Tracks Box instances created in different scopes for proper fini calls pub struct ScopeTracker { @@ -26,12 +26,12 @@ impl ScopeTracker { roots: vec![Vec::new()], // Start with one root region } } - + /// Enter a new scope pub fn push_scope(&mut self) { self.scopes.push(Vec::new()); } - + /// Exit current scope and call fini on all Boxes created in it pub fn pop_scope(&mut self) { if let Some(scope) = self.scopes.pop() { @@ -51,25 +51,27 @@ impl ScopeTracker { // Builtin and others: no-op for now } } - + // Ensure we always have at least one scope - if self.scopes.is_empty() { self.scopes.push(Vec::new()); } + if self.scopes.is_empty() { + self.scopes.push(Vec::new()); + } } - + /// Register a Box in the current scope pub fn register_box(&mut self, nyash_box: Arc) { if let Some(current_scope) = self.scopes.last_mut() { current_scope.push(nyash_box); } } - + /// Clear all scopes (used when resetting VM state) pub fn clear(&mut self) { // Pop all scopes and call fini while self.scopes.len() > 1 { self.pop_scope(); } - + // Clear the root scope if let Some(root_scope) = self.scopes.first_mut() { root_scope.clear(); @@ -96,7 +98,9 @@ impl ScopeTracker { eprintln!("[GC] roots: leave"); } } - if self.roots.is_empty() { self.roots.push(Vec::new()); } + if self.roots.is_empty() { + self.roots.push(Vec::new()); + } } /// Pin a VMValue into the current root region (cheap clone) @@ -110,15 +114,21 @@ impl ScopeTracker { } /// Total number of pinned roots across all regions (for GC PoC diagnostics) - pub fn root_count_total(&self) -> usize { self.roots.iter().map(|r| r.len()).sum() } + pub fn root_count_total(&self) -> usize { + self.roots.iter().map(|r| r.len()).sum() + } /// Number of active root regions - pub fn root_regions(&self) -> usize { self.roots.len() } + pub fn root_regions(&self) -> usize { + self.roots.len() + } /// Snapshot a flat vector of current roots (cloned) for diagnostics pub fn roots_snapshot(&self) -> Vec { let mut out = Vec::new(); - for region in &self.roots { out.extend(region.iter().cloned()); } + for region in &self.roots { + out.extend(region.iter().cloned()); + } out } } diff --git a/src/semantics/clif_adapter.rs b/src/semantics/clif_adapter.rs index 8e89546b..46eed230 100644 --- a/src/semantics/clif_adapter.rs +++ b/src/semantics/clif_adapter.rs @@ -1,6 +1,6 @@ use super::Semantics; -use crate::jit::lower::builder::{IRBuilder, BinOpKind, CmpKind}; -use crate::mir::{ValueId, BasicBlockId}; +use crate::jit::lower::builder::{BinOpKind, CmpKind, IRBuilder}; +use crate::mir::{BasicBlockId, ValueId}; /// Adapter that translates Semantics operations into IRBuilder calls (Cranelift path) pub struct ClifSemanticsAdapter<'a> { @@ -8,7 +8,9 @@ pub struct ClifSemanticsAdapter<'a> { } impl<'a> ClifSemanticsAdapter<'a> { - pub fn new(builder: &'a mut dyn IRBuilder) -> Self { Self { builder } } + pub fn new(builder: &'a mut dyn IRBuilder) -> Self { + Self { builder } + } } impl<'a> Semantics for ClifSemanticsAdapter<'a> { @@ -16,40 +18,107 @@ impl<'a> Semantics for ClifSemanticsAdapter<'a> { type Ptr = ValueId; type BB = BasicBlockId; - fn const_i64(&mut self, v: i64) -> Self::Val { self.builder.emit_const_i64(v); } - fn const_f64(&mut self, v: f64) -> Self::Val { self.builder.emit_const_f64(v); } - fn const_bool(&mut self, v: bool) -> Self::Val { self.builder.emit_const_i64(if v {1} else {0}); } - fn const_null(&mut self) -> Self::Val { self.builder.emit_const_i64(0); } - fn const_str(&mut self, _s: &str) -> Self::Val { self.builder.emit_const_i64(0); } + fn const_i64(&mut self, v: i64) -> Self::Val { + self.builder.emit_const_i64(v); + } + fn const_f64(&mut self, v: f64) -> Self::Val { + self.builder.emit_const_f64(v); + } + fn const_bool(&mut self, v: bool) -> Self::Val { + self.builder.emit_const_i64(if v { 1 } else { 0 }); + } + fn const_null(&mut self) -> Self::Val { + self.builder.emit_const_i64(0); + } + fn const_str(&mut self, _s: &str) -> Self::Val { + self.builder.emit_const_i64(0); + } - fn neg(&mut self, _x: Self::Val) -> Self::Val { self.builder.emit_binop(BinOpKind::Sub); } - fn not(&mut self, _x: Self::Val) -> Self::Val { /* handled via compare/select in LowerCore */ } - fn bit_not(&mut self, _x: Self::Val) -> Self::Val { /* not used here */ } - fn add(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { self.builder.emit_binop(BinOpKind::Add); } - fn sub(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { self.builder.emit_binop(BinOpKind::Sub); } - fn mul(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { self.builder.emit_binop(BinOpKind::Mul); } - fn div(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { self.builder.emit_binop(BinOpKind::Div); } - fn modulo(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { self.builder.emit_binop(BinOpKind::Mod); } - fn cmp_eq(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { self.builder.emit_compare(CmpKind::Eq); } - fn cmp_ne(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { self.builder.emit_compare(CmpKind::Ne); } - fn cmp_lt(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { self.builder.emit_compare(CmpKind::Lt); } - fn cmp_le(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { self.builder.emit_compare(CmpKind::Le); } - fn cmp_gt(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { self.builder.emit_compare(CmpKind::Gt); } - fn cmp_ge(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { self.builder.emit_compare(CmpKind::Ge); } + fn neg(&mut self, _x: Self::Val) -> Self::Val { + self.builder.emit_binop(BinOpKind::Sub); + } + fn not(&mut self, _x: Self::Val) -> Self::Val { /* handled via compare/select in LowerCore */ + } + fn bit_not(&mut self, _x: Self::Val) -> Self::Val { /* not used here */ + } + fn add(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { + self.builder.emit_binop(BinOpKind::Add); + } + fn sub(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { + self.builder.emit_binop(BinOpKind::Sub); + } + fn mul(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { + self.builder.emit_binop(BinOpKind::Mul); + } + fn div(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { + self.builder.emit_binop(BinOpKind::Div); + } + fn modulo(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { + self.builder.emit_binop(BinOpKind::Mod); + } + fn cmp_eq(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { + self.builder.emit_compare(CmpKind::Eq); + } + fn cmp_ne(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { + self.builder.emit_compare(CmpKind::Ne); + } + fn cmp_lt(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { + self.builder.emit_compare(CmpKind::Lt); + } + fn cmp_le(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { + self.builder.emit_compare(CmpKind::Le); + } + fn cmp_gt(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { + self.builder.emit_compare(CmpKind::Gt); + } + fn cmp_ge(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val { + self.builder.emit_compare(CmpKind::Ge); + } - fn alloca_ptr(&mut self, _vid: ValueId) -> Self::Ptr { _vid } - fn load(&mut self, _ptr: &Self::Ptr) -> Self::Val { self.builder.load_local_i64(_ptr.as_u32() as usize); } - fn store(&mut self, _ptr: &Self::Ptr, _v: Self::Val) { self.builder.store_local_i64(_ptr.as_u32() as usize); } - fn jump(&mut self, _target: BasicBlockId) { /* handled by LowerCore */ } - fn branch(&mut self, _cond: Self::Val, _then_bb: BasicBlockId, _else_bb: BasicBlockId) { /* handled by LowerCore */ } - fn phi_select(&mut self, _incoming: &[(BasicBlockId, Self::Val)]) -> Self::Val { () } - fn ret(&mut self, _v: Option) { self.builder.emit_return(); } + fn alloca_ptr(&mut self, _vid: ValueId) -> Self::Ptr { + _vid + } + fn load(&mut self, _ptr: &Self::Ptr) -> Self::Val { + self.builder.load_local_i64(_ptr.as_u32() as usize); + } + fn store(&mut self, _ptr: &Self::Ptr, _v: Self::Val) { + self.builder.store_local_i64(_ptr.as_u32() as usize); + } + fn jump(&mut self, _target: BasicBlockId) { /* handled by LowerCore */ + } + fn branch(&mut self, _cond: Self::Val, _then_bb: BasicBlockId, _else_bb: BasicBlockId) { + /* handled by LowerCore */ + } + fn phi_select(&mut self, _incoming: &[(BasicBlockId, Self::Val)]) -> Self::Val { + () + } + fn ret(&mut self, _v: Option) { + self.builder.emit_return(); + } - fn new_box(&mut self, _type_id: i64, _args: &[Self::Val]) -> Self::Val { () } - fn box_call_tagged(&mut self, _type_id: i64, _method_id: i64, _recv: Self::Val, _argv: &[Self::Val], _tags: &[i64]) -> Self::Val { () } - fn extern_call(&mut self, _iface: &str, _method: &str, _args: &[Self::Val]) -> Self::Val { () } + fn new_box(&mut self, _type_id: i64, _args: &[Self::Val]) -> Self::Val { + () + } + fn box_call_tagged( + &mut self, + _type_id: i64, + _method_id: i64, + _recv: Self::Val, + _argv: &[Self::Val], + _tags: &[i64], + ) -> Self::Val { + () + } + fn extern_call(&mut self, _iface: &str, _method: &str, _args: &[Self::Val]) -> Self::Val { + () + } - fn barrier_read(&mut self, v: Self::Val) -> Self::Val { v } - fn barrier_write(&mut self, _ptr: &Self::Ptr, v: Self::Val) -> Self::Val { v } - fn safepoint(&mut self) { /* Lowered via explicit hostcall in LowerCore path */ } + fn barrier_read(&mut self, v: Self::Val) -> Self::Val { + v + } + fn barrier_write(&mut self, _ptr: &Self::Ptr, v: Self::Val) -> Self::Val { + v + } + fn safepoint(&mut self) { /* Lowered via explicit hostcall in LowerCore path */ + } } diff --git a/src/semantics/mod.rs b/src/semantics/mod.rs index 9f1bbb1e..34b9632d 100644 --- a/src/semantics/mod.rs +++ b/src/semantics/mod.rs @@ -9,7 +9,7 @@ #![allow(dead_code)] -use crate::mir::{ValueId, BasicBlockId}; +use crate::mir::{BasicBlockId, ValueId}; /// The unified semantics interface for MIR evaluation/lowering. pub trait Semantics { @@ -55,21 +55,34 @@ pub trait Semantics { // Host/Box calls fn new_box(&mut self, type_id: i64, args: &[Self::Val]) -> Self::Val; - fn box_call_tagged(&mut self, type_id: i64, method_id: i64, recv: Self::Val, argv: &[Self::Val], tags: &[i64]) -> Self::Val; + fn box_call_tagged( + &mut self, + type_id: i64, + method_id: i64, + recv: Self::Val, + argv: &[Self::Val], + tags: &[i64], + ) -> Self::Val; fn extern_call(&mut self, iface: &str, method: &str, args: &[Self::Val]) -> Self::Val; // GC hooks - fn barrier_read(&mut self, v: Self::Val) -> Self::Val { v } - fn barrier_write(&mut self, _ptr: &Self::Ptr, v: Self::Val) -> Self::Val { v } + fn barrier_read(&mut self, v: Self::Val) -> Self::Val { + v + } + fn barrier_write(&mut self, _ptr: &Self::Ptr, v: Self::Val) -> Self::Val { + v + } fn safepoint(&mut self) {} } /// Optional helpers extension — default blanket impl with conveniences. pub trait SemanticsExt: Semantics { - fn to_bool_hint(&mut self, v: Self::Val) -> Self::Val { v } + fn to_bool_hint(&mut self, v: Self::Val) -> Self::Val { + v + } } impl SemanticsExt for T {} -pub mod vm_impl; pub mod clif_adapter; +pub mod vm_impl; diff --git a/src/semantics/vm_impl.rs b/src/semantics/vm_impl.rs index 58c63a38..2c04fcdb 100644 --- a/src/semantics/vm_impl.rs +++ b/src/semantics/vm_impl.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; -use crate::mir::{ValueId, BasicBlockId}; use crate::backend::vm::VMValue; +use crate::mir::{BasicBlockId, ValueId}; use super::Semantics; @@ -13,7 +13,12 @@ pub struct VmSemantics { } impl VmSemantics { - pub fn new() -> Self { Self { vals: HashMap::new(), last_ret: None } } + pub fn new() -> Self { + Self { + vals: HashMap::new(), + last_ret: None, + } + } } impl Semantics for VmSemantics { @@ -21,61 +26,146 @@ impl Semantics for VmSemantics { type Ptr = ValueId; // address by MIR value id (local slot semantics) type BB = BasicBlockId; - fn const_i64(&mut self, v: i64) -> Self::Val { VMValue::Integer(v) } - fn const_f64(&mut self, v: f64) -> Self::Val { VMValue::Float(v) } - fn const_bool(&mut self, v: bool) -> Self::Val { VMValue::Bool(v) } - fn const_null(&mut self) -> Self::Val { VMValue::Void } - fn const_str(&mut self, s: &str) -> Self::Val { VMValue::String(s.to_string()) } + fn const_i64(&mut self, v: i64) -> Self::Val { + VMValue::Integer(v) + } + fn const_f64(&mut self, v: f64) -> Self::Val { + VMValue::Float(v) + } + fn const_bool(&mut self, v: bool) -> Self::Val { + VMValue::Bool(v) + } + fn const_null(&mut self) -> Self::Val { + VMValue::Void + } + fn const_str(&mut self, s: &str) -> Self::Val { + VMValue::String(s.to_string()) + } fn neg(&mut self, x: Self::Val) -> Self::Val { - match x { VMValue::Integer(i) => VMValue::Integer(-i), VMValue::Float(f) => VMValue::Float(-f), _ => VMValue::Integer(0) } + match x { + VMValue::Integer(i) => VMValue::Integer(-i), + VMValue::Float(f) => VMValue::Float(-f), + _ => VMValue::Integer(0), + } } fn not(&mut self, x: Self::Val) -> Self::Val { - match x { VMValue::Bool(b) => VMValue::Bool(!b), VMValue::Integer(i) => VMValue::Bool(i==0), _ => VMValue::Bool(false) } + match x { + VMValue::Bool(b) => VMValue::Bool(!b), + VMValue::Integer(i) => VMValue::Bool(i == 0), + _ => VMValue::Bool(false), + } } fn bit_not(&mut self, x: Self::Val) -> Self::Val { - match x { VMValue::Integer(i) => VMValue::Integer(!i), _ => VMValue::Integer(0) } + match x { + VMValue::Integer(i) => VMValue::Integer(!i), + _ => VMValue::Integer(0), + } } fn add(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { - match (a,b) { (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Integer(x+y), (VMValue::Float(x), VMValue::Float(y)) => VMValue::Float(x+y), _ => VMValue::Integer(0) } + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Integer(x + y), + (VMValue::Float(x), VMValue::Float(y)) => VMValue::Float(x + y), + _ => VMValue::Integer(0), + } } fn sub(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { - match (a,b) { (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Integer(x-y), (VMValue::Float(x), VMValue::Float(y)) => VMValue::Float(x-y), _ => VMValue::Integer(0) } + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Integer(x - y), + (VMValue::Float(x), VMValue::Float(y)) => VMValue::Float(x - y), + _ => VMValue::Integer(0), + } } fn mul(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { - match (a,b) { (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Integer(x*y), (VMValue::Float(x), VMValue::Float(y)) => VMValue::Float(x*y), _ => VMValue::Integer(0) } + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Integer(x * y), + (VMValue::Float(x), VMValue::Float(y)) => VMValue::Float(x * y), + _ => VMValue::Integer(0), + } } fn div(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { - match (a,b) { (VMValue::Integer(x), VMValue::Integer(y)) if y!=0 => VMValue::Integer(x/y), (VMValue::Float(x), VMValue::Float(y)) => VMValue::Float(x/y), _ => VMValue::Integer(0) } + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) if y != 0 => VMValue::Integer(x / y), + (VMValue::Float(x), VMValue::Float(y)) => VMValue::Float(x / y), + _ => VMValue::Integer(0), + } } fn modulo(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { - match (a,b) { (VMValue::Integer(x), VMValue::Integer(y)) if y!=0 => VMValue::Integer(x%y), _ => VMValue::Integer(0) } + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) if y != 0 => VMValue::Integer(x % y), + _ => VMValue::Integer(0), + } + } + fn cmp_eq(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { + VMValue::Bool(a == b) + } + fn cmp_ne(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { + VMValue::Bool(a != b) } - fn cmp_eq(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { VMValue::Bool(a==b) } - fn cmp_ne(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { VMValue::Bool(a!=b) } fn cmp_lt(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { - match (a,b) { (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x VMValue::Bool(x VMValue::Bool(false) } + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x < y), + (VMValue::Float(x), VMValue::Float(y)) => VMValue::Bool(x < y), + _ => VMValue::Bool(false), + } } fn cmp_le(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { - match (a,b) { (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x<=y), (VMValue::Float(x), VMValue::Float(y)) => VMValue::Bool(x<=y), _ => VMValue::Bool(false) } + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x <= y), + (VMValue::Float(x), VMValue::Float(y)) => VMValue::Bool(x <= y), + _ => VMValue::Bool(false), + } } fn cmp_gt(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { - match (a,b) { (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x>y), (VMValue::Float(x), VMValue::Float(y)) => VMValue::Bool(x>y), _ => VMValue::Bool(false) } + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x > y), + (VMValue::Float(x), VMValue::Float(y)) => VMValue::Bool(x > y), + _ => VMValue::Bool(false), + } } fn cmp_ge(&mut self, a: Self::Val, b: Self::Val) -> Self::Val { - match (a,b) { (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x>=y), (VMValue::Float(x), VMValue::Float(y)) => VMValue::Bool(x>=y), _ => VMValue::Bool(false) } + match (a, b) { + (VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x >= y), + (VMValue::Float(x), VMValue::Float(y)) => VMValue::Bool(x >= y), + _ => VMValue::Bool(false), + } } - fn alloca_ptr(&mut self, vid: ValueId) -> Self::Ptr { vid } - fn load(&mut self, ptr: &Self::Ptr) -> Self::Val { self.vals.get(ptr).cloned().unwrap_or(VMValue::Integer(0)) } - fn store(&mut self, ptr: &Self::Ptr, v: Self::Val) { self.vals.insert(*ptr, v); } + fn alloca_ptr(&mut self, vid: ValueId) -> Self::Ptr { + vid + } + fn load(&mut self, ptr: &Self::Ptr) -> Self::Val { + self.vals.get(ptr).cloned().unwrap_or(VMValue::Integer(0)) + } + fn store(&mut self, ptr: &Self::Ptr, v: Self::Val) { + self.vals.insert(*ptr, v); + } fn jump(&mut self, _target: BasicBlockId) {} fn branch(&mut self, _cond: Self::Val, _then_bb: BasicBlockId, _else_bb: BasicBlockId) {} - fn phi_select(&mut self, incoming: &[(BasicBlockId, Self::Val)]) -> Self::Val { incoming.last().map(|(_,v)| v.clone()).unwrap_or(VMValue::Integer(0)) } - fn ret(&mut self, v: Option) { self.last_ret = v; } + fn phi_select(&mut self, incoming: &[(BasicBlockId, Self::Val)]) -> Self::Val { + incoming + .last() + .map(|(_, v)| v.clone()) + .unwrap_or(VMValue::Integer(0)) + } + fn ret(&mut self, v: Option) { + self.last_ret = v; + } - fn new_box(&mut self, _type_id: i64, _args: &[Self::Val]) -> Self::Val { VMValue::Integer(0) } - fn box_call_tagged(&mut self, _type_id: i64, _method_id: i64, _recv: Self::Val, _argv: &[Self::Val], _tags: &[i64]) -> Self::Val { VMValue::Integer(0) } - fn extern_call(&mut self, _iface: &str, _method: &str, _args: &[Self::Val]) -> Self::Val { VMValue::Integer(0) } + fn new_box(&mut self, _type_id: i64, _args: &[Self::Val]) -> Self::Val { + VMValue::Integer(0) + } + fn box_call_tagged( + &mut self, + _type_id: i64, + _method_id: i64, + _recv: Self::Val, + _argv: &[Self::Val], + _tags: &[i64], + ) -> Self::Val { + VMValue::Integer(0) + } + fn extern_call(&mut self, _iface: &str, _method: &str, _args: &[Self::Val]) -> Self::Val { + VMValue::Integer(0) + } } - diff --git a/src/stdlib/mod.rs b/src/stdlib/mod.rs index 83d55f37..c1e1a183 100644 --- a/src/stdlib/mod.rs +++ b/src/stdlib/mod.rs @@ -1,11 +1,11 @@ /*! * Nyash Built-in Standard Library - * + * * 超簡単実装:ハードコード組み込み型標準ライブラリ * nyash.linkなしで動作する基本的な標準関数群 */ -use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox}; +use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox}; use crate::boxes::ArrayBox; use crate::interpreter::RuntimeError; use std::collections::HashMap; @@ -36,190 +36,212 @@ impl BuiltinStdlib { let mut stdlib = BuiltinStdlib { namespaces: HashMap::new(), }; - + // nyashstd名前空間登録 stdlib.register_nyashstd(); - + stdlib } - + /// nyashstd名前空間を登録 fn register_nyashstd(&mut self) { let mut nyashstd = BuiltinNamespace { name: "nyashstd".to_string(), static_boxes: HashMap::new(), }; - + // string static box - nyashstd.static_boxes.insert("string".to_string(), Self::create_string_box()); - + nyashstd + .static_boxes + .insert("string".to_string(), Self::create_string_box()); + // integer static box - nyashstd.static_boxes.insert("integer".to_string(), Self::create_integer_box()); - + nyashstd + .static_boxes + .insert("integer".to_string(), Self::create_integer_box()); + // bool static box - nyashstd.static_boxes.insert("bool".to_string(), Self::create_bool_box()); - + nyashstd + .static_boxes + .insert("bool".to_string(), Self::create_bool_box()); + // array static box - nyashstd.static_boxes.insert("array".to_string(), Self::create_array_box()); - + nyashstd + .static_boxes + .insert("array".to_string(), Self::create_array_box()); + // console static box - nyashstd.static_boxes.insert("console".to_string(), Self::create_console_box()); - + nyashstd + .static_boxes + .insert("console".to_string(), Self::create_console_box()); + self.namespaces.insert("nyashstd".to_string(), nyashstd); } - + /// string static boxを作成 fn create_string_box() -> BuiltinStaticBox { let mut string_box = BuiltinStaticBox { name: "string".to_string(), methods: HashMap::new(), }; - + // string.create(text) -> StringBox string_box.methods.insert("create".to_string(), |args| { if args.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: "string.create() takes exactly 1 argument".to_string() + message: "string.create() takes exactly 1 argument".to_string(), }); } - + // StringBoxにダウンキャスト if let Some(string_arg) = args[0].as_any().downcast_ref::() { let result = StringBox::new(&string_arg.value); Ok(Box::new(result)) } else { Err(RuntimeError::TypeError { - message: format!("string.create() expects string argument, got {:?}", args[0].type_name()) + message: format!( + "string.create() expects string argument, got {:?}", + args[0].type_name() + ), }) } }); - + // string.upper(str) -> String string_box.methods.insert("upper".to_string(), |args| { if args.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: "string.upper() takes exactly 1 argument".to_string() + message: "string.upper() takes exactly 1 argument".to_string(), }); } - + // StringBoxにダウンキャスト if let Some(string_arg) = args[0].as_any().downcast_ref::() { let result = StringBox::new(&string_arg.value.to_uppercase()); Ok(Box::new(result)) } else { Err(RuntimeError::TypeError { - message: format!("string.upper() expects string argument, got {:?}", args[0].type_name()) + message: format!( + "string.upper() expects string argument, got {:?}", + args[0].type_name() + ), }) } }); - + string_box } - + /// integer static boxを作成 fn create_integer_box() -> BuiltinStaticBox { let mut integer_box = BuiltinStaticBox { name: "integer".to_string(), methods: HashMap::new(), }; - + // integer.create(value) -> IntegerBox integer_box.methods.insert("create".to_string(), |args| { if args.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: "integer.create() takes exactly 1 argument".to_string() + message: "integer.create() takes exactly 1 argument".to_string(), }); } - + // IntegerBoxにダウンキャスト if let Some(int_arg) = args[0].as_any().downcast_ref::() { let result = IntegerBox::new(int_arg.value); Ok(Box::new(result)) } else { Err(RuntimeError::TypeError { - message: format!("integer.create() expects integer argument, got {:?}", args[0].type_name()) + message: format!( + "integer.create() expects integer argument, got {:?}", + args[0].type_name() + ), }) } }); - + integer_box } - + /// bool static boxを作成 fn create_bool_box() -> BuiltinStaticBox { let mut bool_box = BuiltinStaticBox { name: "bool".to_string(), methods: HashMap::new(), }; - + // bool.create(value) -> BoolBox bool_box.methods.insert("create".to_string(), |args| { if args.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: "bool.create() takes exactly 1 argument".to_string() + message: "bool.create() takes exactly 1 argument".to_string(), }); } - + // BoolBoxにダウンキャスト if let Some(bool_arg) = args[0].as_any().downcast_ref::() { let result = BoolBox::new(bool_arg.value); Ok(Box::new(result)) } else { Err(RuntimeError::TypeError { - message: format!("bool.create() expects bool argument, got {:?}", args[0].type_name()) + message: format!( + "bool.create() expects bool argument, got {:?}", + args[0].type_name() + ), }) } }); - + bool_box } - + /// array static boxを作成 fn create_array_box() -> BuiltinStaticBox { let mut array_box = BuiltinStaticBox { name: "array".to_string(), methods: HashMap::new(), }; - + // array.create() -> ArrayBox (引数なしで空配列作成) array_box.methods.insert("create".to_string(), |args| { if !args.is_empty() { return Err(RuntimeError::InvalidOperation { - message: "array.create() takes no arguments".to_string() + message: "array.create() takes no arguments".to_string(), }); } - + let result = ArrayBox::new(); Ok(Box::new(result)) }); - + array_box } - + /// console static boxを作成 fn create_console_box() -> BuiltinStaticBox { let mut console_box = BuiltinStaticBox { name: "console".to_string(), methods: HashMap::new(), }; - + // console.log(message) -> void console_box.methods.insert("log".to_string(), |args| { if args.len() != 1 { return Err(RuntimeError::InvalidOperation { - message: "console.log() takes exactly 1 argument".to_string() + message: "console.log() takes exactly 1 argument".to_string(), }); } - + // 任意のBoxを文字列として出力 let message = args[0].to_string_box().value; println!("{}", message); - + // VoidBoxを返す use crate::box_trait::VoidBox; Ok(Box::new(VoidBox::new())) }); - + console_box } -} \ No newline at end of file +} diff --git a/src/syntax/sugar_config.rs b/src/syntax/sugar_config.rs index 41faa081..d6dd33e4 100644 --- a/src/syntax/sugar_config.rs +++ b/src/syntax/sugar_config.rs @@ -1,20 +1,32 @@ use std::{env, fs, path::Path}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum SugarLevel { None, Basic, Full } +pub enum SugarLevel { + None, + Basic, + Full, +} #[derive(Debug, Clone, PartialEq, Eq)] -pub struct SugarConfig { pub level: SugarLevel } +pub struct SugarConfig { + pub level: SugarLevel, +} impl Default for SugarConfig { - fn default() -> Self { Self { level: SugarLevel::None } } + fn default() -> Self { + Self { + level: SugarLevel::None, + } + } } impl SugarConfig { pub fn from_env_or_toml(path: impl AsRef) -> Self { // 1) env override if let Ok(s) = env::var("NYASH_SYNTAX_SUGAR_LEVEL") { - return Self { level: parse_level(&s) }; + return Self { + level: parse_level(&s), + }; } // 2) toml [syntax].sugar_level let path = path.as_ref(); @@ -22,7 +34,9 @@ impl SugarConfig { if let Ok(val) = toml::from_str::(&content) { if let Some(table) = val.get("syntax").and_then(|v| v.as_table()) { if let Some(level_str) = table.get("sugar_level").and_then(|v| v.as_str()) { - return Self { level: parse_level(level_str) }; + return Self { + level: parse_level(level_str), + }; } } } diff --git a/src/tests/aot_plan_import.rs b/src/tests/aot_plan_import.rs index 050153db..2c28e6c9 100644 --- a/src/tests/aot_plan_import.rs +++ b/src/tests/aot_plan_import.rs @@ -10,4 +10,3 @@ fn import_plan_v1_min_and_run_vm() { let out = vm.execute_module(&module).expect("vm exec"); assert_eq!(out.to_string_box().value, "42"); } - diff --git a/src/tests/box_tests.rs b/src/tests/box_tests.rs index 4c2edf1c..52dbb880 100644 --- a/src/tests/box_tests.rs +++ b/src/tests/box_tests.rs @@ -1,6 +1,6 @@ //! Tests for NyashBox trait implementations -use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoxCore}; -use crate::boxes::{ArrayBox, BufferBox, JSONBox, NyashFutureBox, NyashStreamBox, NyashResultBox}; +use crate::box_trait::{BoxCore, IntegerBox, NyashBox, StringBox}; +use crate::boxes::{ArrayBox, BufferBox, JSONBox, NyashFutureBox, NyashResultBox, NyashStreamBox}; #[cfg(test)] mod tests { @@ -11,17 +11,17 @@ mod tests { let mut array = ArrayBox::new(); let str_box = Box::new(StringBox::new("test")) as Box; let int_box = Box::new(IntegerBox::new(42)) as Box; - + array.push(str_box); array.push(int_box); - + assert_eq!(array.type_name(), "ArrayBox"); assert_eq!(array.len(), 2); - + let string_repr = array.to_string_box(); assert!(string_repr.value.contains("test")); assert!(string_repr.value.contains("42")); - + // Test cloning let cloned = array.clone_box(); assert_eq!(cloned.type_name(), "ArrayBox"); @@ -30,17 +30,17 @@ mod tests { #[test] fn test_buffer_box_nyash_trait() { let buffer = BufferBox::from_vec(vec![1, 2, 3, 4, 5]); - + assert_eq!(buffer.type_name(), "BufferBox"); assert_eq!(buffer.len(), 5); - + let string_repr = buffer.to_string_box(); assert!(string_repr.value.contains("BufferBox(5 bytes)")); - + // Test cloning let cloned = buffer.clone_box(); assert_eq!(cloned.type_name(), "BufferBox"); - + // Test equality let other_buffer = BufferBox::from_vec(vec![1, 2, 3, 4, 5]); assert!(buffer.equals(&other_buffer).value); @@ -50,36 +50,36 @@ mod tests { fn test_json_box_nyash_trait() { let json_str = r#"{"name": "test", "value": 42}"#; let json_box = JSONBox::from_str(json_str).expect("Valid JSON"); - + assert_eq!(json_box.type_name(), "JSONBox"); - + let string_repr = json_box.to_string_box(); assert!(string_repr.value.contains("test")); assert!(string_repr.value.contains("42")); - + // Test cloning let cloned = json_box.clone_box(); assert_eq!(cloned.type_name(), "JSONBox"); - + // Test equality let other_json = JSONBox::from_str(json_str).expect("Valid JSON"); assert!(json_box.equals(&other_json).value); } - #[test] + #[test] fn test_future_box_nyash_trait() { let future = NyashFutureBox::new(); - + assert_eq!(future.type_name(), "NyashFutureBox"); assert!(!future.ready()); - + let string_repr = future.to_string_box(); assert!(string_repr.value.contains("Future(pending)")); - + // Test setting result let result_box = Box::new(StringBox::new("completed")) as Box; future.set_result(result_box); - + assert!(future.ready()); let result = future.get(); assert_eq!(result.to_string_box().value, "completed"); @@ -88,19 +88,19 @@ mod tests { #[test] fn test_stream_box_nyash_trait() { let mut stream = NyashStreamBox::from_data(vec![72, 101, 108, 108, 111]); // "Hello" - + assert_eq!(stream.type_name(), "NyashStreamBox"); assert_eq!(stream.len(), 5); - + let string_repr = stream.to_string_box(); assert!(string_repr.value.contains("NyashStreamBox(5 bytes")); - + // Test reading let mut buffer = [0u8; 3]; let bytes_read = stream.read(&mut buffer).expect("Read should succeed"); assert_eq!(bytes_read, 3); assert_eq!(&buffer, &[72, 101, 108]); // "Hel" - + // Test writing stream.write(&[33, 33]).expect("Write should succeed"); // "!!" assert_eq!(stream.len(), 7); @@ -109,19 +109,19 @@ mod tests { #[test] fn test_result_box_nyash_trait() { let success_result = NyashResultBox::new_ok(Box::new(StringBox::new("success"))); - + assert_eq!(success_result.type_name(), "NyashResultBox"); assert!(success_result.is_ok_bool()); assert!(!success_result.is_err()); - + let string_repr = success_result.to_string_box(); assert!(string_repr.value.contains("Ok(success)")); - + // Test error case let error_result = NyashResultBox::new_err(Box::new(StringBox::new("error"))); assert!(!error_result.is_ok_bool()); assert!(error_result.is_err()); - + let error_string = error_result.to_string_box(); assert!(error_string.value.contains("Err(error)")); } @@ -130,10 +130,10 @@ mod tests { fn test_box_id_uniqueness() { let box1 = ArrayBox::new(); let box2 = ArrayBox::new(); - + // Different instances should have different IDs assert_ne!(box1.box_id(), box2.box_id()); - + // Same instance should have same ID let cloned = box1.clone_box(); // Note: Clone creates new instance so ID will be different diff --git a/src/tests/host_reverse_slot.rs b/src/tests/host_reverse_slot.rs index 8a24c8ee..9ad8cc4a 100644 --- a/src/tests/host_reverse_slot.rs +++ b/src/tests/host_reverse_slot.rs @@ -1,12 +1,13 @@ #[cfg(test)] mod tests { - use crate::runtime::host_handles; use crate::runtime::host_api; + use crate::runtime::host_handles; #[test] fn host_reverse_call_map_slots() { // Build a MapBox and turn it into a HostHandle - let map = std::sync::Arc::new(crate::boxes::map_box::MapBox::new()) as std::sync::Arc; + let map = std::sync::Arc::new(crate::boxes::map_box::MapBox::new()) + as std::sync::Arc; let h = host_handles::to_handle_arc(map); // TLV args: key="k", val=42 @@ -17,15 +18,35 @@ mod tests { // set: slot 204 let mut out = vec![0u8; 256]; let mut out_len = out.len(); - let code = unsafe { host_api::nyrt_host_call_slot(h, 204, tlv.as_ptr(), tlv.len(), out.as_mut_ptr(), &mut out_len) }; + let code = unsafe { + host_api::nyrt_host_call_slot( + h, + 204, + tlv.as_ptr(), + tlv.len(), + out.as_mut_ptr(), + &mut out_len, + ) + }; assert_eq!(code, 0); // size: slot 200 let mut out2 = vec![0u8; 256]; let mut out2_len = out2.len(); - let code2 = unsafe { host_api::nyrt_host_call_slot(h, 200, std::ptr::null(), 0, out2.as_mut_ptr(), &mut out2_len) }; + let code2 = unsafe { + host_api::nyrt_host_call_slot( + h, + 200, + std::ptr::null(), + 0, + out2.as_mut_ptr(), + &mut out2_len, + ) + }; assert_eq!(code2, 0); - if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out2[..out2_len]) { + if let Some((tag, _sz, payload)) = + crate::runtime::plugin_ffi_common::decode::tlv_first(&out2[..out2_len]) + { assert_eq!(tag, 3, "size returns i64 tag (3)"); let n = crate::runtime::plugin_ffi_common::decode::u64(payload).unwrap_or(0); assert_eq!(n, 1, "after set, size should be 1"); @@ -34,4 +55,3 @@ mod tests { } } } - diff --git a/src/tests/identical_exec.rs b/src/tests/identical_exec.rs index 57eb85b6..cf3499be 100644 --- a/src/tests/identical_exec.rs +++ b/src/tests/identical_exec.rs @@ -1,8 +1,8 @@ #[cfg(all(test, not(feature = "jit-direct-only")))] mod tests { - use crate::mir::{MirModule, MirFunction, FunctionSignature}; - use crate::mir::{BasicBlockId, MirInstruction, ConstValue, EffectMask, MirType, BinaryOp}; use crate::backend::VM; + use crate::mir::{BasicBlockId, BinaryOp, ConstValue, EffectMask, MirInstruction, MirType}; + use crate::mir::{FunctionSignature, MirFunction, MirModule}; fn make_add_main(a: i64, b: i64) -> MirModule { let sig = FunctionSignature { @@ -14,12 +14,31 @@ mod tests { let mut func = MirFunction::new(sig, BasicBlockId::new(0)); let bb = func.entry_block; let v0 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::Integer(a) }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v0, + value: ConstValue::Integer(a), + }); let v1 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v1, value: ConstValue::Integer(b) }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v1, + value: ConstValue::Integer(b), + }); let v2 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: v2, op: BinaryOp::Add, lhs: v0, rhs: v1 }); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(v2) }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BinOp { + dst: v2, + op: BinaryOp::Add, + lhs: v0, + rhs: v1, + }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(v2) }); let mut module = MirModule::new("identical".to_string()); module.add_function(func); module @@ -35,7 +54,8 @@ mod tests { let vm_s = vm_out.to_string_box().value; // Run JIT (Cranelift minimal) - let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_jit").expect("JIT exec"); + let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_jit") + .expect("JIT exec"); let jit_s = jit_out.to_string_box().value; assert_eq!(vm_s, jit_s, "VM and JIT results should match"); @@ -45,20 +65,34 @@ mod tests { #[test] fn identical_vm_and_jit_console_log_side_effect_free() { // Build: const 1; extern_call env.console.log(1); return 1 - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask}; - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: crate::mir::MirType::Integer, effects: EffectMask::PURE }; + use crate::mir::{EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule}; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: crate::mir::MirType::Integer, + effects: EffectMask::PURE, + }; let mut func = MirFunction::new(sig, crate::mir::BasicBlockId::new(0)); let bb = func.entry_block; let v0 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: crate::mir::ConstValue::Integer(1) }); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::ExternCall { - dst: None, - iface_name: "env.console".to_string(), - method_name: "log".to_string(), - args: vec![v0], - effects: EffectMask::IO, - }); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(v0) }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v0, + value: crate::mir::ConstValue::Integer(1), + }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::ExternCall { + dst: None, + iface_name: "env.console".to_string(), + method_name: "log".to_string(), + args: vec![v0], + effects: EffectMask::IO, + }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(v0) }); let mut module = MirModule::new("identical_console".into()); module.add_function(func); @@ -68,9 +102,13 @@ mod tests { let vm_s = vm_out.to_string_box().value; // Run JIT (Cranelift minimal) — ExternCallはスキップされる想定 - let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_console").expect("JIT exec"); + let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_console") + .expect("JIT exec"); let jit_s = jit_out.to_string_box().value; - assert_eq!(vm_s, jit_s, "VM and JIT results should match despite console.log side effects"); + assert_eq!( + vm_s, jit_s, + "VM and JIT results should match despite console.log side effects" + ); } } diff --git a/src/tests/identical_exec_collections.rs b/src/tests/identical_exec_collections.rs index 905a2a37..050ad9aa 100644 --- a/src/tests/identical_exec_collections.rs +++ b/src/tests/identical_exec_collections.rs @@ -1,54 +1,174 @@ #[cfg(all(test, not(feature = "jit-direct-only")))] mod tests { use crate::backend::VM; - use crate::mir::{MirModule, MirFunction, FunctionSignature, BasicBlockId, MirInstruction, ConstValue, EffectMask, MirType, ValueId}; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, ValueId, + }; // Build a MIR that exercises Array.get/set/len, Map.set/size/has/get, and String.len fn make_module() -> MirModule { let mut module = MirModule::new("identical_collections".to_string()); - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; // Build: arr = NewBox(ArrayBox); arr.set(0, "x"); len = arr.len(); let arr = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: arr, box_type: "ArrayBox".into(), args: vec![] }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: arr, + box_type: "ArrayBox".into(), + args: vec![], + }); let idx0 = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: idx0, value: ConstValue::Integer(0) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: idx0, + value: ConstValue::Integer(0), + }); let s = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: s, value: ConstValue::String("x".into()) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "set".into(), args: vec![idx0, s], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: s, + value: ConstValue::String("x".into()), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: arr, + method: "set".into(), + args: vec![idx0, s], + method_id: None, + effects: EffectMask::PURE, + }); let alen = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(alen), box_val: arr, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(alen), + box_val: arr, + method: "len".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); // Map: m = NewBox(MapBox); m.set("k", 42); size = m.size(); has = m.has("k"); get = m.get("k") let m = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: m, box_type: "MapBox".into(), args: vec![] }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: m, + box_type: "MapBox".into(), + args: vec![], + }); let k = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k, value: ConstValue::String("k".into()) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: k, + value: ConstValue::String("k".into()), + }); let v = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v, value: ConstValue::Integer(42) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m, method: "set".into(), args: vec![k, v], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v, + value: ConstValue::Integer(42), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: m, + method: "set".into(), + args: vec![k, v], + method_id: None, + effects: EffectMask::PURE, + }); let msize = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(msize), box_val: m, method: "size".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(msize), + box_val: m, + method: "size".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); let mhas = f.next_value_id(); let k2 = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k2, value: ConstValue::String("k".into()) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(mhas), box_val: m, method: "has".into(), args: vec![k2], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: k2, + value: ConstValue::String("k".into()), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(mhas), + box_val: m, + method: "has".into(), + args: vec![k2], + method_id: None, + effects: EffectMask::PURE, + }); let mget = f.next_value_id(); let k3 = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k3, value: ConstValue::String("k".into()) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(mget), box_val: m, method: "get".into(), args: vec![k3], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: k3, + value: ConstValue::String("k".into()), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(mget), + box_val: m, + method: "get".into(), + args: vec![k3], + method_id: None, + effects: EffectMask::PURE, + }); // String.len: sb = "hello"; slen = sb.len() let sb = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: sb, value: ConstValue::String("hello".into()) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: sb, + value: ConstValue::String("hello".into()), + }); let slen = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(slen), box_val: sb, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(slen), + box_val: sb, + method: "len".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); // Return: alen + msize + (mhas?1:0) + slen + (mget coerced to int or 0) // Simplify: just return alen - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(alen) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(alen) }); module.add_function(f); module @@ -66,9 +186,14 @@ mod tests { // JIT with host bridge enabled for parity std::env::set_var("NYASH_JIT_HOST_BRIDGE", "1"); - let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_collections").expect("JIT exec"); + let jit_out = + crate::backend::cranelift_compile_and_execute(&module, "identical_collections") + .expect("JIT exec"); let jit_s = jit_out.to_string_box().value; - assert_eq!(vm_s, jit_s, "VM and JIT results should match for collection ops"); + assert_eq!( + vm_s, jit_s, + "VM and JIT results should match for collection ops" + ); } } diff --git a/src/tests/identical_exec_instance.rs b/src/tests/identical_exec_instance.rs index 62c98dcc..f7182961 100644 --- a/src/tests/identical_exec_instance.rs +++ b/src/tests/identical_exec_instance.rs @@ -1,56 +1,142 @@ #[cfg(all(test, not(feature = "jit-direct-only")))] mod tests { - use std::sync::{Arc, RwLock}; use std::collections::HashMap; + use std::sync::{Arc, RwLock}; use crate::backend::VM; - use crate::mir::{MirModule, MirFunction, FunctionSignature, BasicBlockId, MirInstruction, ConstValue, EffectMask, MirType}; use crate::box_trait::NyashBox; use crate::interpreter::RuntimeError; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, + }; // Minimal Person factory: creates InstanceBox with fields [name, age] struct PersonFactory; impl crate::box_factory::BoxFactory for PersonFactory { - fn create_box(&self, name: &str, _args: &[Box]) -> Result, RuntimeError> { - if name != "Person" { return Err(RuntimeError::InvalidOperation { message: format!("Unknown Box type: {}", name) }); } + fn create_box( + &self, + name: &str, + _args: &[Box], + ) -> Result, RuntimeError> { + if name != "Person" { + return Err(RuntimeError::InvalidOperation { + message: format!("Unknown Box type: {}", name), + }); + } let fields = vec!["name".to_string(), "age".to_string()]; let methods: HashMap = HashMap::new(); - let inst = crate::instance_v2::InstanceBox::from_declaration("Person".to_string(), fields, methods); + let inst = crate::instance_v2::InstanceBox::from_declaration( + "Person".to_string(), + fields, + methods, + ); Ok(Box::new(inst)) } - fn box_types(&self) -> Vec<&str> { vec!["Person"] } - fn is_builtin_factory(&self) -> bool { true } + fn box_types(&self) -> Vec<&str> { + vec!["Person"] + } + fn is_builtin_factory(&self) -> bool { + true + } } fn build_person_module() -> MirModule { let mut module = MirModule::new("identical_person".to_string()); - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::String, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; let person = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: person, box_type: "Person".into(), args: vec![] }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: person, + box_type: "Person".into(), + args: vec![], + }); // person.setField("name", "Alice") let k_name = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k_name, value: ConstValue::String("name".into()) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: k_name, + value: ConstValue::String("name".into()), + }); let v_alice = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v_alice, value: ConstValue::String("Alice".into()) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: person, method: "setField".into(), args: vec![k_name, v_alice], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v_alice, + value: ConstValue::String("Alice".into()), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: person, + method: "setField".into(), + args: vec![k_name, v_alice], + method_id: None, + effects: EffectMask::PURE, + }); // person.setField("age", 25) let k_age = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k_age, value: ConstValue::String("age".into()) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: k_age, + value: ConstValue::String("age".into()), + }); let v_25 = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v_25, value: ConstValue::Integer(25) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: person, method: "setField".into(), args: vec![k_age, v_25], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v_25, + value: ConstValue::Integer(25), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: person, + method: "setField".into(), + args: vec![k_age, v_25], + method_id: None, + effects: EffectMask::PURE, + }); // name = person.getField("name"); return name let k_name2 = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k_name2, value: ConstValue::String("name".into()) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: k_name2, + value: ConstValue::String("name".into()), + }); let out_name = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(out_name), box_val: person, method: "getField".into(), args: vec![k_name2], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(out_name) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(out_name), + box_val: person, + method: "getField".into(), + args: vec![k_name2], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { + value: Some(out_name), + }); module.add_function(f); module @@ -79,7 +165,8 @@ mod tests { // JIT(host-bridge on) std::env::set_var("NYASH_JIT_HOST_BRIDGE", "1"); - let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_person").expect("JIT exec"); + let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_person") + .expect("JIT exec"); let jit_s = jit_out.to_string_box().value; assert_eq!(vm_s, jit_s); diff --git a/src/tests/identical_exec_string.rs b/src/tests/identical_exec_string.rs index 9892351a..3aca4afe 100644 --- a/src/tests/identical_exec_string.rs +++ b/src/tests/identical_exec_string.rs @@ -1,18 +1,42 @@ #[cfg(all(test, not(feature = "jit-direct-only")))] mod tests { use crate::backend::VM; - use crate::mir::{MirModule, MirFunction, FunctionSignature, BasicBlockId, MirInstruction, ConstValue, EffectMask, MirType}; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, + }; fn make_string_len() -> MirModule { let mut module = MirModule::new("identical_string".to_string()); - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; let s = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: s, value: ConstValue::String("hello".into()) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: s, + value: ConstValue::String("hello".into()), + }); let ln = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(ln), box_val: s, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(ln) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(ln), + box_val: s, + method: "len".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(ln) }); module.add_function(f); module } @@ -31,9 +55,13 @@ mod tests { let vm_s = vm_out.to_string_box().value; // JIT - let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_string").expect("JIT exec"); + let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_string") + .expect("JIT exec"); let jit_s = jit_out.to_string_box().value; - assert_eq!(vm_s, jit_s, "VM and JIT results should match for String.len"); + assert_eq!( + vm_s, jit_s, + "VM and JIT results should match for String.len" + ); } } diff --git a/src/tests/mir_vm_poc.rs b/src/tests/mir_vm_poc.rs index b50c9b76..cceb1ed2 100644 --- a/src/tests/mir_vm_poc.rs +++ b/src/tests/mir_vm_poc.rs @@ -3,8 +3,8 @@ #[cfg(test)] mod tests { use crate::backend::vm::VM; - use crate::mir::{MirModule, MirFunction, FunctionSignature}; - use crate::mir::{BasicBlockId, MirInstruction, ConstValue, EffectMask, Effect, MirType}; + use crate::mir::{BasicBlockId, ConstValue, Effect, EffectMask, MirInstruction, MirType}; + use crate::mir::{FunctionSignature, MirFunction, MirModule}; fn make_main() -> MirFunction { let sig = FunctionSignature { @@ -22,26 +22,57 @@ mod tests { let bb = func.entry_block; let v0 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::Integer(42) }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v0, + value: ConstValue::Integer(42), + }); let v1 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeOp { dst: v1, op: crate::mir::TypeOpKind::Check, value: v0, ty: MirType::Integer }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::TypeOp { + dst: v1, + op: crate::mir::TypeOpKind::Check, + value: v0, + ty: MirType::Integer, + }); // console.log(result) via ExternCall - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![v1], effects: EffectMask::IO }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::ExternCall { + dst: None, + iface_name: "env.console".to_string(), + method_name: "log".to_string(), + args: vec![v1], + effects: EffectMask::IO, + }); // Cast (no-op for PoC semantics) let v2 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeOp { dst: v2, op: crate::mir::TypeOpKind::Cast, value: v0, ty: MirType::Integer }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::TypeOp { + dst: v2, + op: crate::mir::TypeOpKind::Cast, + value: v0, + ty: MirType::Integer, + }); // Return void - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: None }); let mut module = MirModule::new("test".to_string()); module.add_function(func); let mut vm = VM::new(); - let _ = vm.execute_module(&module).expect("VM should execute module"); + let _ = vm + .execute_module(&module) + .expect("VM should execute module"); } #[test] @@ -50,16 +81,32 @@ mod tests { let bb = func.entry_block; let v0 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::Integer(3) }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v0, + value: ConstValue::Integer(3), + }); let v1 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeOp { dst: v1, op: crate::mir::TypeOpKind::Cast, value: v0, ty: MirType::Float }); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::TypeOp { + dst: v1, + op: crate::mir::TypeOpKind::Cast, + value: v0, + ty: MirType::Float, + }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: None }); let mut module = MirModule::new("test".to_string()); module.add_function(func); let mut vm = VM::new(); - let _ = vm.execute_module(&module).expect("int->float cast should succeed"); + let _ = vm + .execute_module(&module) + .expect("int->float cast should succeed"); } #[test] @@ -68,16 +115,32 @@ mod tests { let bb = func.entry_block; let v0 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::Float(3.7) }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v0, + value: ConstValue::Float(3.7), + }); let v1 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeOp { dst: v1, op: crate::mir::TypeOpKind::Cast, value: v0, ty: MirType::Integer }); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::TypeOp { + dst: v1, + op: crate::mir::TypeOpKind::Cast, + value: v0, + ty: MirType::Integer, + }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: None }); let mut module = MirModule::new("test".to_string()); module.add_function(func); let mut vm = VM::new(); - let _ = vm.execute_module(&module).expect("float->int cast should succeed"); + let _ = vm + .execute_module(&module) + .expect("float->int cast should succeed"); } #[test] @@ -86,11 +149,25 @@ mod tests { let bb = func.entry_block; let v0 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::String("x".to_string()) }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v0, + value: ConstValue::String("x".to_string()), + }); let v1 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeOp { dst: v1, op: crate::mir::TypeOpKind::Cast, value: v0, ty: MirType::Integer }); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::TypeOp { + dst: v1, + op: crate::mir::TypeOpKind::Cast, + value: v0, + ty: MirType::Integer, + }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: None }); let mut module = MirModule::new("test".to_string()); module.add_function(func); @@ -105,22 +182,51 @@ mod tests { let bb = func.entry_block; let v0 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::Integer(7) }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v0, + value: ConstValue::Integer(7), + }); let v1 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeCheck { dst: v1, value: v0, expected_type: "IntegerBox".to_string() }); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![v1], effects: EffectMask::IO }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::TypeCheck { + dst: v1, + value: v0, + expected_type: "IntegerBox".to_string(), + }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::ExternCall { + dst: None, + iface_name: "env.console".to_string(), + method_name: "log".to_string(), + args: vec![v1], + effects: EffectMask::IO, + }); let v2 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Cast { dst: v2, value: v0, target_type: MirType::Integer }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Cast { + dst: v2, + value: v0, + target_type: MirType::Integer, + }); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: None }); let mut module = MirModule::new("test".to_string()); module.add_function(func); let mut vm = VM::new(); - let _ = vm.execute_module(&module).expect("VM should execute module"); + let _ = vm + .execute_module(&module) + .expect("VM should execute module"); } #[test] @@ -129,27 +235,66 @@ mod tests { let bb = func.entry_block; let v0 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::Integer(1) }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v0, + value: ConstValue::Integer(1), + }); let v1 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::WeakRef { dst: v1, op: crate::mir::WeakRefOp::New, value: v0 }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::WeakRef { + dst: v1, + op: crate::mir::WeakRefOp::New, + value: v0, + }); let v2 = func.next_value_id(); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::WeakRef { dst: v2, op: crate::mir::WeakRefOp::Load, value: v1 }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::WeakRef { + dst: v2, + op: crate::mir::WeakRefOp::Load, + value: v1, + }); // Optional barriers (no-op semantics) - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Barrier { op: crate::mir::BarrierOp::Read, ptr: v2 }); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Barrier { op: crate::mir::BarrierOp::Write, ptr: v2 }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Barrier { + op: crate::mir::BarrierOp::Read, + ptr: v2, + }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Barrier { + op: crate::mir::BarrierOp::Write, + ptr: v2, + }); // Print loaded value via env.console.log - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![v2], effects: EffectMask::IO }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::ExternCall { + dst: None, + iface_name: "env.console".to_string(), + method_name: "log".to_string(), + args: vec![v2], + effects: EffectMask::IO, + }); - func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None }); + func.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: None }); let mut module = MirModule::new("test".to_string()); module.add_function(func); let mut vm = VM::new(); - let _ = vm.execute_module(&module).expect("VM should execute module"); + let _ = vm + .execute_module(&module) + .expect("VM should execute module"); } } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index e91a3aac..e6c38e6f 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,28 +1,28 @@ -pub mod box_tests; -pub mod mir_vm_poc; -pub mod identical_exec; -pub mod identical_exec_collections; -pub mod identical_exec_string; -pub mod identical_exec_instance; -pub mod vtable_array_string; -pub mod vtable_strict; -pub mod vtable_array_ext; -pub mod vtable_array_p2; -pub mod vtable_array_p1; -pub mod vtable_string; -pub mod vtable_console; -pub mod vtable_map_ext; -pub mod vtable_string_p1; -pub mod host_reverse_slot; -pub mod nyash_abi_basic; -pub mod typebox_tlv_diff; -pub mod sugar_basic_test; -pub mod sugar_pipeline_test; -pub mod sugar_comp_assign_test; -pub mod sugar_coalesce_test; -pub mod sugar_safe_access_test; -pub mod sugar_range_test; -pub mod policy_mutdeny; -pub mod plugin_hygiene; #[cfg(feature = "aot-plan-import")] pub mod aot_plan_import; +pub mod box_tests; +pub mod host_reverse_slot; +pub mod identical_exec; +pub mod identical_exec_collections; +pub mod identical_exec_instance; +pub mod identical_exec_string; +pub mod mir_vm_poc; +pub mod nyash_abi_basic; +pub mod plugin_hygiene; +pub mod policy_mutdeny; +pub mod sugar_basic_test; +pub mod sugar_coalesce_test; +pub mod sugar_comp_assign_test; +pub mod sugar_pipeline_test; +pub mod sugar_range_test; +pub mod sugar_safe_access_test; +pub mod typebox_tlv_diff; +pub mod vtable_array_ext; +pub mod vtable_array_p1; +pub mod vtable_array_p2; +pub mod vtable_array_string; +pub mod vtable_console; +pub mod vtable_map_ext; +pub mod vtable_strict; +pub mod vtable_string; +pub mod vtable_string_p1; diff --git a/src/tests/nyash_abi_basic.rs b/src/tests/nyash_abi_basic.rs index 851da602..5956b318 100644 --- a/src/tests/nyash_abi_basic.rs +++ b/src/tests/nyash_abi_basic.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod tests { - use crate::runtime::type_registry::{resolve_slot_by_name, known_methods_for}; + use crate::runtime::type_registry::{known_methods_for, resolve_slot_by_name}; #[test] fn type_registry_resolves_core_slots() { @@ -28,36 +28,99 @@ mod tests { #[ignore] fn vm_vtable_map_set_get_has() { use crate::backend::vm::VM; - use crate::mir::{MirModule, MirFunction, FunctionSignature, BasicBlockId, MirInstruction, EffectMask, ConstValue, MirType, ValueId}; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, ValueId, + }; // Enable vtable-preferred path std::env::set_var("NYASH_ABI_VTABLE", "1"); // Program: m = new MapBox(); m.set("k","v"); h = m.has("k"); g = m.get("k"); return g let mut m = MirModule::new("nyash_abi_map_get".into()); - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::String, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; let mapv = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: mapv, box_type: "MapBox".into(), args: vec![] }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: mapv, + box_type: "MapBox".into(), + args: vec![], + }); let k = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k, value: ConstValue::String("k".into()) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: k, + value: ConstValue::String("k".into()), + }); let v = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v, value: ConstValue::String("v".into()) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: mapv, method: "set".into(), args: vec![k, v], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v, + value: ConstValue::String("v".into()), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: mapv, + method: "set".into(), + args: vec![k, v], + method_id: None, + effects: EffectMask::PURE, + }); let k2 = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k2, value: ConstValue::String("k".into()) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: k2, + value: ConstValue::String("k".into()), + }); let hasv = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(hasv), box_val: mapv, method: "has".into(), args: vec![k2], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(hasv), + box_val: mapv, + method: "has".into(), + args: vec![k2], + method_id: None, + effects: EffectMask::PURE, + }); let k3 = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k3, value: ConstValue::String("k".into()) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: k3, + value: ConstValue::String("k".into()), + }); let got = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(got), box_val: mapv, method: "get".into(), args: vec![k3], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(got) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(got), + box_val: mapv, + method: "get".into(), + args: vec![k3], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(got) }); m.add_function(f); @@ -69,8 +132,8 @@ mod tests { #[test] fn mapbox_keys_values_return_arrays() { // Direct Box-level test (not via VM): keys()/values() should return ArrayBox + use crate::box_trait::{IntegerBox, NyashBox, StringBox}; use crate::boxes::map_box::MapBox; - use crate::box_trait::{NyashBox, StringBox, IntegerBox}; let map = MapBox::new(); map.set(Box::new(StringBox::new("a")), Box::new(IntegerBox::new(1))); diff --git a/src/tests/plugin_hygiene.rs b/src/tests/plugin_hygiene.rs index 0c8b9cff..088ba33e 100644 --- a/src/tests/plugin_hygiene.rs +++ b/src/tests/plugin_hygiene.rs @@ -29,6 +29,9 @@ fn plugin_invoke_hygiene_string_len_is_hostcall() { assert_eq!(symbol, c::SYM_STRING_LEN_H); assert_eq!(reason, "mapped_symbol"); } - other => panic!("expected HostCall(mapped_symbol) for String.len, got: {:?}", other), + other => panic!( + "expected HostCall(mapped_symbol) for String.len, got: {:?}", + other + ), } } diff --git a/src/tests/policy_mutdeny.rs b/src/tests/policy_mutdeny.rs index 73824764..e049eadb 100644 --- a/src/tests/policy_mutdeny.rs +++ b/src/tests/policy_mutdeny.rs @@ -2,58 +2,158 @@ #[test] #[ignore] fn jit_readonly_array_push_denied() { - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType}; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, + }; // Ensure read-only policy is on std::env::set_var("NYASH_JIT_READ_ONLY", "1"); // Build: a = new ArrayBox(); a.push(3); ret a.len() - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; let a = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: a, box_type: "ArrayBox".into(), args: vec![] }); - let three = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: three, value: ConstValue::Integer(3) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: a, + box_type: "ArrayBox".into(), + args: vec![], + }); + let three = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: three, + value: ConstValue::Integer(3), + }); // push should be denied under read-only policy, effectively no-op for length - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a, method: "push".into(), args: vec![three], method_id: None, effects: EffectMask::PURE }); - let ln = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(ln), box_val: a, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(ln) }); - let mut m = MirModule::new("jit_readonly_array_push_denied".into()); m.add_function(f); - let out = crate::backend::cranelift_compile_and_execute(&m, "jit_readonly_array_push_denied").expect("JIT exec"); - assert_eq!(out.to_string_box().value, "0", "Array.push must be denied under read-only policy"); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a, + method: "push".into(), + args: vec![three], + method_id: None, + effects: EffectMask::PURE, + }); + let ln = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(ln), + box_val: a, + method: "len".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(ln) }); + let mut m = MirModule::new("jit_readonly_array_push_denied".into()); + m.add_function(f); + let out = crate::backend::cranelift_compile_and_execute(&m, "jit_readonly_array_push_denied") + .expect("JIT exec"); + assert_eq!( + out.to_string_box().value, + "0", + "Array.push must be denied under read-only policy" + ); } #[cfg(feature = "cranelift-jit")] #[test] #[ignore] fn jit_readonly_map_set_denied() { - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType}; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, + }; // Ensure read-only policy is on std::env::set_var("NYASH_JIT_READ_ONLY", "1"); // Build: m = new MapBox(); m.set("a", 2); ret m.size() - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; let mbox = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: mbox, box_type: "MapBox".into(), args: vec![] }); - let key = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: key, value: ConstValue::String("a".into()) }); - let val = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: val, value: ConstValue::Integer(2) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: mbox, + box_type: "MapBox".into(), + args: vec![], + }); + let key = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: key, + value: ConstValue::String("a".into()), + }); + let val = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: val, + value: ConstValue::Integer(2), + }); // set should be denied under read-only policy - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: mbox, method: "set".into(), args: vec![key, val], method_id: None, effects: EffectMask::PURE }); - let sz = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(sz), box_val: mbox, method: "size".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(sz) }); - let mut module = MirModule::new("jit_readonly_map_set_denied".into()); module.add_function(f); - let out = crate::backend::cranelift_compile_and_execute(&module, "jit_readonly_map_set_denied").expect("JIT exec"); - assert_eq!(out.to_string_box().value, "0", "Map.set must be denied under read-only policy"); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: mbox, + method: "set".into(), + args: vec![key, val], + method_id: None, + effects: EffectMask::PURE, + }); + let sz = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(sz), + box_val: mbox, + method: "size".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(sz) }); + let mut module = MirModule::new("jit_readonly_map_set_denied".into()); + module.add_function(f); + let out = crate::backend::cranelift_compile_and_execute(&module, "jit_readonly_map_set_denied") + .expect("JIT exec"); + assert_eq!( + out.to_string_box().value, + "0", + "Map.set must be denied under read-only policy" + ); } // Engine-independent smoke: validate policy denial via host externs #[test] fn extern_readonly_array_push_denied() { - use std::sync::Arc; - use crate::boxes::array::ArrayBox; use crate::backend::vm::VMValue; + use crate::boxes::array::ArrayBox; use crate::jit::r#extern::collections as c; + use std::sync::Arc; std::env::set_var("NYASH_JIT_READ_ONLY", "1"); let arr = Arc::new(ArrayBox::new()); @@ -66,10 +166,10 @@ fn extern_readonly_array_push_denied() { #[test] fn extern_readonly_map_set_denied() { - use std::sync::Arc; - use crate::boxes::map_box::MapBox; use crate::backend::vm::VMValue; + use crate::boxes::map_box::MapBox; use crate::jit::r#extern::collections as c; + use std::sync::Arc; std::env::set_var("NYASH_JIT_READ_ONLY", "1"); let map = Arc::new(MapBox::new()); @@ -83,10 +183,10 @@ fn extern_readonly_map_set_denied() { #[test] fn extern_readonly_read_ops_allowed() { - use std::sync::Arc; - use crate::boxes::{array::ArrayBox, map_box::MapBox}; use crate::backend::vm::VMValue; + use crate::boxes::{array::ArrayBox, map_box::MapBox}; use crate::jit::r#extern::collections as c; + use std::sync::Arc; std::env::set_var("NYASH_JIT_READ_ONLY", "1"); // Array: len/get are read-only diff --git a/src/tests/sugar_basic_test.rs b/src/tests/sugar_basic_test.rs index 87c7e3a3..7c69f4f7 100644 --- a/src/tests/sugar_basic_test.rs +++ b/src/tests/sugar_basic_test.rs @@ -27,4 +27,3 @@ fn tokenizer_has_basic_sugar_tokens() { assert!(has(|k| matches!(k, TokenType::DivAssign))); assert!(has(|k| matches!(k, TokenType::RANGE))); } - diff --git a/src/tests/sugar_coalesce_test.rs b/src/tests/sugar_coalesce_test.rs index aad4586b..0be603c8 100644 --- a/src/tests/sugar_coalesce_test.rs +++ b/src/tests/sugar_coalesce_test.rs @@ -1,23 +1,46 @@ +use crate::ast::{ASTNode, LiteralValue}; use crate::parser::entry_sugar::parse_with_sugar_level; use crate::syntax::sugar_config::SugarLevel; -use crate::ast::{ASTNode, LiteralValue}; #[test] fn coalesce_peek_rewrite() { let code = "x = a ?? b\n"; let ast = parse_with_sugar_level(code, SugarLevel::Basic).expect("parse ok"); - let program = match ast { ASTNode::Program { statements, .. } => statements, other => panic!("expected program, got {:?}", other) }; - let assign = match &program[0] { ASTNode::Assignment { target, value, .. } => (target, value), other => panic!("expected assignment, got {:?}", other) }; - match assign.0.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "x"), _ => panic!("target not x") } + let program = match ast { + ASTNode::Program { statements, .. } => statements, + other => panic!("expected program, got {:?}", other), + }; + let assign = match &program[0] { + ASTNode::Assignment { target, value, .. } => (target, value), + other => panic!("expected assignment, got {:?}", other), + }; + match assign.0.as_ref() { + ASTNode::Variable { name, .. } => assert_eq!(name, "x"), + _ => panic!("target not x"), + } match assign.1.as_ref() { - ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => { - match scrutinee.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "a"), _ => panic!("scrutinee not a") } + ASTNode::PeekExpr { + scrutinee, + arms, + else_expr, + .. + } => { + match scrutinee.as_ref() { + ASTNode::Variable { name, .. } => assert_eq!(name, "a"), + _ => panic!("scrutinee not a"), + } assert_eq!(arms.len(), 1); assert!(matches!(arms[0].0, LiteralValue::Null)); - match &arms[0].1 { ASTNode::Variable { name, .. } => assert_eq!(name, "b"), _ => panic!("rhs not b") } - match else_expr.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "a"), _ => panic!("else not a") } + match &arms[0].1 { + ASTNode::Variable { name, .. } => assert_eq!(name, "b"), + _ => panic!("rhs not b"), + } + match else_expr.as_ref() { + ASTNode::Variable { name, .. } => assert_eq!(name, "a"), + _ => panic!("else not a"), + } } other => panic!("expected PeekExpr, got {:?}", other), } diff --git a/src/tests/sugar_comp_assign_test.rs b/src/tests/sugar_comp_assign_test.rs index 9e12cf51..7e72ff05 100644 --- a/src/tests/sugar_comp_assign_test.rs +++ b/src/tests/sugar_comp_assign_test.rs @@ -1,23 +1,43 @@ +use crate::ast::{ASTNode, BinaryOperator}; use crate::parser::entry_sugar::parse_with_sugar_level; use crate::syntax::sugar_config::SugarLevel; -use crate::ast::{ASTNode, BinaryOperator}; #[test] fn compound_assign_rewrites_to_binaryop() { let code = "x = 1\nx += 2\n"; let ast = parse_with_sugar_level(code, SugarLevel::Basic).expect("parse ok"); - let program = match ast { ASTNode::Program { statements, .. } => statements, other => panic!("expected program, got {:?}", other) }; + let program = match ast { + ASTNode::Program { statements, .. } => statements, + other => panic!("expected program, got {:?}", other), + }; assert_eq!(program.len(), 2); - let assign = match &program[1] { ASTNode::Assignment { target, value, .. } => (target, value), other => panic!("expected assignment, got {:?}", other) }; - match assign.0.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "x"), other => panic!("expected target var, got {:?}", other) } + let assign = match &program[1] { + ASTNode::Assignment { target, value, .. } => (target, value), + other => panic!("expected assignment, got {:?}", other), + }; + match assign.0.as_ref() { + ASTNode::Variable { name, .. } => assert_eq!(name, "x"), + other => panic!("expected target var, got {:?}", other), + } match assign.1.as_ref() { - ASTNode::BinaryOp { operator, left, right, .. } => { + ASTNode::BinaryOp { + operator, + left, + right, + .. + } => { assert!(matches!(operator, BinaryOperator::Add)); - match left.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "x"), other => panic!("expected left x, got {:?}", other) } - match right.as_ref() { ASTNode::Literal { .. } => {}, other => panic!("expected right literal, got {:?}", other) } + match left.as_ref() { + ASTNode::Variable { name, .. } => assert_eq!(name, "x"), + other => panic!("expected left x, got {:?}", other), + } + match right.as_ref() { + ASTNode::Literal { .. } => {} + other => panic!("expected right literal, got {:?}", other), + } } other => panic!("expected BinaryOp, got {:?}", other), } diff --git a/src/tests/sugar_pipeline_test.rs b/src/tests/sugar_pipeline_test.rs index c7cbbefc..b2aff7e9 100644 --- a/src/tests/sugar_pipeline_test.rs +++ b/src/tests/sugar_pipeline_test.rs @@ -1,6 +1,6 @@ +use crate::ast::ASTNode; use crate::parser::entry_sugar::parse_with_sugar_level; use crate::syntax::sugar_config::SugarLevel; -use crate::ast::ASTNode; #[test] fn pipeline_rewrites_function_and_method_calls() { @@ -8,17 +8,34 @@ fn pipeline_rewrites_function_and_method_calls() { let ast = parse_with_sugar_level(code, SugarLevel::Basic).expect("parse ok"); // Program with one assignment - let program = match ast { ASTNode::Program { statements, .. } => statements, other => panic!("expected program, got {:?}", other) }; + let program = match ast { + ASTNode::Program { statements, .. } => statements, + other => panic!("expected program, got {:?}", other), + }; assert_eq!(program.len(), 1); - let assign = match &program[0] { ASTNode::Assignment { target, value, .. } => (target, value), other => panic!("expected assignment, got {:?}", other) }; + let assign = match &program[0] { + ASTNode::Assignment { target, value, .. } => (target, value), + other => panic!("expected assignment, got {:?}", other), + }; // target = result - match assign.0.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "result"), other => panic!("expected target var, got {:?}", other) } + match assign.0.as_ref() { + ASTNode::Variable { name, .. } => assert_eq!(name, "result"), + other => panic!("expected target var, got {:?}", other), + } // value should be obj.m( normalize(data,1), 2 ) let (obj_name, method_name, args) = match assign.1.as_ref() { - ASTNode::MethodCall { object, method, arguments, .. } => { - let obj_name = match object.as_ref() { ASTNode::Variable { name, .. } => name.clone(), other => panic!("expected obj var, got {:?}", other) }; + ASTNode::MethodCall { + object, + method, + arguments, + .. + } => { + let obj_name = match object.as_ref() { + ASTNode::Variable { name, .. } => name.clone(), + other => panic!("expected obj var, got {:?}", other), + }; (obj_name, method.clone(), arguments.clone()) } other => panic!("expected method call, got {:?}", other), @@ -29,10 +46,15 @@ fn pipeline_rewrites_function_and_method_calls() { // first arg should be normalize(data,1) match &args[0] { - ASTNode::FunctionCall { name, arguments, .. } => { + ASTNode::FunctionCall { + name, arguments, .. + } => { assert_eq!(name, "normalize"); assert_eq!(arguments.len(), 2); - match &arguments[0] { ASTNode::Variable { name, .. } => assert_eq!(name, "data"), other => panic!("expected var data, got {:?}", other) } + match &arguments[0] { + ASTNode::Variable { name, .. } => assert_eq!(name, "data"), + other => panic!("expected var data, got {:?}", other), + } } other => panic!("expected function call, got {:?}", other), } diff --git a/src/tests/sugar_range_test.rs b/src/tests/sugar_range_test.rs index feaed77b..56667db3 100644 --- a/src/tests/sugar_range_test.rs +++ b/src/tests/sugar_range_test.rs @@ -1,16 +1,21 @@ +use crate::ast::ASTNode; use crate::parser::entry_sugar::parse_with_sugar_level; use crate::syntax::sugar_config::SugarLevel; -use crate::ast::ASTNode; #[test] fn range_rewrites_to_function_call() { let code = "r = 1 .. 5\n"; let ast = parse_with_sugar_level(code, SugarLevel::Basic).expect("parse ok"); - let program = match ast { ASTNode::Program { statements, .. } => statements, other => panic!("expected program, got {:?}", other) }; + let program = match ast { + ASTNode::Program { statements, .. } => statements, + other => panic!("expected program, got {:?}", other), + }; match &program[0] { ASTNode::Assignment { value, .. } => match value.as_ref() { - ASTNode::FunctionCall { name, arguments, .. } => { + ASTNode::FunctionCall { + name, arguments, .. + } => { assert_eq!(name, "Range"); assert_eq!(arguments.len(), 2); } diff --git a/src/tests/sugar_safe_access_test.rs b/src/tests/sugar_safe_access_test.rs index 3c0e219e..4b460122 100644 --- a/src/tests/sugar_safe_access_test.rs +++ b/src/tests/sugar_safe_access_test.rs @@ -1,41 +1,69 @@ +use crate::ast::ASTNode; use crate::parser::entry_sugar::parse_with_sugar_level; use crate::syntax::sugar_config::SugarLevel; -use crate::ast::ASTNode; #[test] fn safe_access_field_and_method() { let code = "a = user?.profile\nb = user?.m(1)\n"; let ast = parse_with_sugar_level(code, SugarLevel::Basic).expect("parse ok"); - let program = match ast { ASTNode::Program { statements, .. } => statements, other => panic!("expected program, got {:?}", other) }; + let program = match ast { + ASTNode::Program { statements, .. } => statements, + other => panic!("expected program, got {:?}", other), + }; assert_eq!(program.len(), 2); // a = user?.profile match &program[0] { ASTNode::Assignment { value, .. } => match value.as_ref() { - ASTNode::PeekExpr { scrutinee, else_expr, .. } => { - match scrutinee.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "user"), _ => panic!("scrutinee not user") } + ASTNode::PeekExpr { + scrutinee, + else_expr, + .. + } => { + match scrutinee.as_ref() { + ASTNode::Variable { name, .. } => assert_eq!(name, "user"), + _ => panic!("scrutinee not user"), + } match else_expr.as_ref() { ASTNode::FieldAccess { object, field, .. } => { - match object.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "user"), _ => panic!("object not user") } + match object.as_ref() { + ASTNode::Variable { name, .. } => assert_eq!(name, "user"), + _ => panic!("object not user"), + } assert_eq!(field, "profile"); } other => panic!("else not field access, got {:?}", other), } } other => panic!("expected PeekExpr, got {:?}", other), - } + }, other => panic!("expected assignment, got {:?}", other), } // b = user?.m(1) match &program[1] { ASTNode::Assignment { value, .. } => match value.as_ref() { - ASTNode::PeekExpr { scrutinee, else_expr, .. } => { - match scrutinee.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "user"), _ => panic!("scrutinee not user") } + ASTNode::PeekExpr { + scrutinee, + else_expr, + .. + } => { + match scrutinee.as_ref() { + ASTNode::Variable { name, .. } => assert_eq!(name, "user"), + _ => panic!("scrutinee not user"), + } match else_expr.as_ref() { - ASTNode::MethodCall { object, method, arguments, .. } => { - match object.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "user"), _ => panic!("object not user") } + ASTNode::MethodCall { + object, + method, + arguments, + .. + } => { + match object.as_ref() { + ASTNode::Variable { name, .. } => assert_eq!(name, "user"), + _ => panic!("object not user"), + } assert_eq!(method, "m"); assert_eq!(arguments.len(), 1); } @@ -43,7 +71,7 @@ fn safe_access_field_and_method() { } } other => panic!("expected PeekExpr, got {:?}", other), - } + }, other => panic!("expected assignment, got {:?}", other), } } diff --git a/src/tests/typebox_tlv_diff.rs b/src/tests/typebox_tlv_diff.rs index 65c640bc..e2edae1d 100644 --- a/src/tests/typebox_tlv_diff.rs +++ b/src/tests/typebox_tlv_diff.rs @@ -1,9 +1,9 @@ #[cfg(all(test, not(feature = "jit-direct-only")))] mod tests { - use std::env; - use crate::box_trait::{NyashBox, StringBox, IntegerBox}; - use crate::boxes::math_box::FloatBox; + use crate::box_trait::{IntegerBox, NyashBox, StringBox}; use crate::boxes::array::ArrayBox; + use crate::boxes::math_box::FloatBox; + use std::env; use std::fs; use std::path::PathBuf; @@ -16,7 +16,10 @@ mod tests { let host = host.read().unwrap(); let bx = host.create_box(box_type, &[]).expect("create_box"); // Downcast to PluginBoxV2 to get instance_id - if let Some(p) = bx.as_any().downcast_ref::() { + if let Some(p) = bx + .as_any() + .downcast_ref::() + { (box_type.to_string(), p.instance_id(), bx) } else { panic!("not a plugin box: {}", bx.type_name()); @@ -35,11 +38,24 @@ mod tests { let out_tlv = { let h = host.read().unwrap(); // set("k", 42) - let _ = h.invoke_instance_method(&bt1, "set", id1, &[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))]).expect("set tlv"); + let _ = h + .invoke_instance_method( + &bt1, + "set", + id1, + &[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))], + ) + .expect("set tlv"); // size() - let sz = h.invoke_instance_method(&bt1, "size", id1, &[]).expect("size tlv").unwrap(); + let sz = h + .invoke_instance_method(&bt1, "size", id1, &[]) + .expect("size tlv") + .unwrap(); // get("k") - let gv = h.invoke_instance_method(&bt1, "get", id1, &[Box::new(StringBox::new("k"))]).expect("get tlv").unwrap(); + let gv = h + .invoke_instance_method(&bt1, "get", id1, &[Box::new(StringBox::new("k"))]) + .expect("get tlv") + .unwrap(); (sz.to_string_box().value, gv.to_string_box().value) }; @@ -48,9 +64,22 @@ mod tests { let (bt2, id2, _hold2) = create_plugin_instance("MapBox"); let out_tb = { let h = host.read().unwrap(); - let _ = h.invoke_instance_method(&bt2, "set", id2, &[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))]).expect("set tb"); - let sz = h.invoke_instance_method(&bt2, "size", id2, &[]).expect("size tb").unwrap(); - let gv = h.invoke_instance_method(&bt2, "get", id2, &[Box::new(StringBox::new("k"))]).expect("get tb").unwrap(); + let _ = h + .invoke_instance_method( + &bt2, + "set", + id2, + &[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))], + ) + .expect("set tb"); + let sz = h + .invoke_instance_method(&bt2, "size", id2, &[]) + .expect("size tb") + .unwrap(); + let gv = h + .invoke_instance_method(&bt2, "get", id2, &[Box::new(StringBox::new("k"))]) + .expect("get tb") + .unwrap(); (sz.to_string_box().value, gv.to_string_box().value) }; @@ -67,9 +96,22 @@ mod tests { let (bt1, id1, _hold1) = create_plugin_instance("ArrayBox"); let out_tlv = { let h = host.read().unwrap(); - let _ = h.invoke_instance_method(&bt1, "set", id1, &[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))]).expect("set tlv"); - let ln = h.invoke_instance_method(&bt1, "len", id1, &[]).expect("len tlv").unwrap(); - let gv = h.invoke_instance_method(&bt1, "get", id1, &[Box::new(IntegerBox::new(0))]).expect("get tlv").unwrap(); + let _ = h + .invoke_instance_method( + &bt1, + "set", + id1, + &[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))], + ) + .expect("set tlv"); + let ln = h + .invoke_instance_method(&bt1, "len", id1, &[]) + .expect("len tlv") + .unwrap(); + let gv = h + .invoke_instance_method(&bt1, "get", id1, &[Box::new(IntegerBox::new(0))]) + .expect("get tlv") + .unwrap(); (ln.to_string_box().value, gv.to_string_box().value) }; // TypeBox path @@ -77,12 +119,28 @@ mod tests { let (bt2, id2, _hold2) = create_plugin_instance("ArrayBox"); let out_tb = { let h = host.read().unwrap(); - let _ = h.invoke_instance_method(&bt2, "set", id2, &[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))]).expect("set tb"); - let ln = h.invoke_instance_method(&bt2, "length", id2, &[]).expect("len tb").unwrap(); - let gv = h.invoke_instance_method(&bt2, "get", id2, &[Box::new(IntegerBox::new(0))]).expect("get tb").unwrap(); + let _ = h + .invoke_instance_method( + &bt2, + "set", + id2, + &[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))], + ) + .expect("set tb"); + let ln = h + .invoke_instance_method(&bt2, "length", id2, &[]) + .expect("len tb") + .unwrap(); + let gv = h + .invoke_instance_method(&bt2, "get", id2, &[Box::new(IntegerBox::new(0))]) + .expect("get tb") + .unwrap(); (ln.to_string_box().value, gv.to_string_box().value) }; - assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (ArrayBox)"); + assert_eq!( + out_tlv, out_tb, + "TLV vs TypeBox results should match (ArrayBox)" + ); } #[test] @@ -96,8 +154,14 @@ mod tests { let out_tlv = { let h = host.read().unwrap(); // birth with init string: use fromUtf8 via set of arg in create? Current loader birth() no-arg, so concat - let _ = h.invoke_instance_method(&bt1, "concat", id1, &[Box::new(StringBox::new("ab"))]).expect("concat tlv").unwrap(); - let ln = h.invoke_instance_method(&bt1, "length", id1, &[]).expect("len tlv").unwrap(); + let _ = h + .invoke_instance_method(&bt1, "concat", id1, &[Box::new(StringBox::new("ab"))]) + .expect("concat tlv") + .unwrap(); + let ln = h + .invoke_instance_method(&bt1, "length", id1, &[]) + .expect("len tlv") + .unwrap(); (ln.to_string_box().value) }; // TypeBox path @@ -105,11 +169,20 @@ mod tests { let (bt2, id2, _hold2) = create_plugin_instance("StringBox"); let out_tb = { let h = host.read().unwrap(); - let _ = h.invoke_instance_method(&bt2, "concat", id2, &[Box::new(StringBox::new("ab"))]).expect("concat tb").unwrap(); - let ln = h.invoke_instance_method(&bt2, "length", id2, &[]).expect("len tb").unwrap(); + let _ = h + .invoke_instance_method(&bt2, "concat", id2, &[Box::new(StringBox::new("ab"))]) + .expect("concat tb") + .unwrap(); + let ln = h + .invoke_instance_method(&bt2, "length", id2, &[]) + .expect("len tb") + .unwrap(); (ln.to_string_box().value) }; - assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (StringBox)"); + assert_eq!( + out_tlv, out_tb, + "TLV vs TypeBox results should match (StringBox)" + ); } #[test] @@ -122,8 +195,14 @@ mod tests { let (bt1, id1, _hold1) = create_plugin_instance("IntegerBox"); let out_tlv = { let h = host.read().unwrap(); - let _ = h.invoke_instance_method(&bt1, "set", id1, &[Box::new(IntegerBox::new(123))]).expect("set tlv").unwrap(); - let gv = h.invoke_instance_method(&bt1, "get", id1, &[]).expect("get tlv").unwrap(); + let _ = h + .invoke_instance_method(&bt1, "set", id1, &[Box::new(IntegerBox::new(123))]) + .expect("set tlv") + .unwrap(); + let gv = h + .invoke_instance_method(&bt1, "get", id1, &[]) + .expect("get tlv") + .unwrap(); gv.to_string_box().value }; // TypeBox path @@ -131,11 +210,20 @@ mod tests { let (bt2, id2, _hold2) = create_plugin_instance("IntegerBox"); let out_tb = { let h = host.read().unwrap(); - let _ = h.invoke_instance_method(&bt2, "set", id2, &[Box::new(IntegerBox::new(123))]).expect("set tb").unwrap(); - let gv = h.invoke_instance_method(&bt2, "get", id2, &[]).expect("get tb").unwrap(); + let _ = h + .invoke_instance_method(&bt2, "set", id2, &[Box::new(IntegerBox::new(123))]) + .expect("set tb") + .unwrap(); + let gv = h + .invoke_instance_method(&bt2, "get", id2, &[]) + .expect("get tb") + .unwrap(); gv.to_string_box().value }; - assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (IntegerBox)"); + assert_eq!( + out_tlv, out_tb, + "TLV vs TypeBox results should match (IntegerBox)" + ); } #[test] @@ -148,7 +236,9 @@ mod tests { let (bt1, id1, _hold1) = create_plugin_instance("ConsoleBox"); let out_tlv_is_none = { let h = host.read().unwrap(); - let rv = h.invoke_instance_method(&bt1, "println", id1, &[Box::new(StringBox::new("hello"))]).expect("println tlv"); + let rv = h + .invoke_instance_method(&bt1, "println", id1, &[Box::new(StringBox::new("hello"))]) + .expect("println tlv"); rv.is_none() }; // TypeBox path @@ -156,10 +246,15 @@ mod tests { let (bt2, id2, _hold2) = create_plugin_instance("ConsoleBox"); let out_tb_is_none = { let h = host.read().unwrap(); - let rv = h.invoke_instance_method(&bt2, "println", id2, &[Box::new(StringBox::new("hello"))]).expect("println tb"); + let rv = h + .invoke_instance_method(&bt2, "println", id2, &[Box::new(StringBox::new("hello"))]) + .expect("println tb"); rv.is_none() }; - assert!(out_tlv_is_none && out_tb_is_none, "println should return void/None in both modes"); + assert!( + out_tlv_is_none && out_tb_is_none, + "println should return void/None in both modes" + ); } #[test] @@ -173,10 +268,22 @@ mod tests { let (bt1, id1, _hold1) = create_plugin_instance("MathBox"); let out_tlv = { let h = host.read().unwrap(); - let s1 = h.invoke_instance_method(&bt1, "sqrt", id1, &[Box::new(IntegerBox::new(9))]).expect("sqrt tlv").unwrap(); - let s2 = h.invoke_instance_method(&bt1, "sin", id1, &[Box::new(IntegerBox::new(0))]).expect("sin tlv").unwrap(); - let s3 = h.invoke_instance_method(&bt1, "cos", id1, &[Box::new(IntegerBox::new(0))]).expect("cos tlv").unwrap(); - let s4 = h.invoke_instance_method(&bt1, "round", id1, &[Box::new(IntegerBox::new(26))]).expect("round tlv").unwrap(); + let s1 = h + .invoke_instance_method(&bt1, "sqrt", id1, &[Box::new(IntegerBox::new(9))]) + .expect("sqrt tlv") + .unwrap(); + let s2 = h + .invoke_instance_method(&bt1, "sin", id1, &[Box::new(IntegerBox::new(0))]) + .expect("sin tlv") + .unwrap(); + let s3 = h + .invoke_instance_method(&bt1, "cos", id1, &[Box::new(IntegerBox::new(0))]) + .expect("cos tlv") + .unwrap(); + let s4 = h + .invoke_instance_method(&bt1, "round", id1, &[Box::new(IntegerBox::new(26))]) + .expect("round tlv") + .unwrap(); ( s1.to_string_box().value, s2.to_string_box().value, @@ -190,10 +297,22 @@ mod tests { let (bt2, id2, _hold2) = create_plugin_instance("MathBox"); let out_tb = { let h = host.read().unwrap(); - let s1 = h.invoke_instance_method(&bt2, "sqrt", id2, &[Box::new(IntegerBox::new(9))]).expect("sqrt tb").unwrap(); - let s2 = h.invoke_instance_method(&bt2, "sin", id2, &[Box::new(IntegerBox::new(0))]).expect("sin tb").unwrap(); - let s3 = h.invoke_instance_method(&bt2, "cos", id2, &[Box::new(IntegerBox::new(0))]).expect("cos tb").unwrap(); - let s4 = h.invoke_instance_method(&bt2, "round", id2, &[Box::new(IntegerBox::new(26))]).expect("round tb").unwrap(); + let s1 = h + .invoke_instance_method(&bt2, "sqrt", id2, &[Box::new(IntegerBox::new(9))]) + .expect("sqrt tb") + .unwrap(); + let s2 = h + .invoke_instance_method(&bt2, "sin", id2, &[Box::new(IntegerBox::new(0))]) + .expect("sin tb") + .unwrap(); + let s3 = h + .invoke_instance_method(&bt2, "cos", id2, &[Box::new(IntegerBox::new(0))]) + .expect("cos tb") + .unwrap(); + let s4 = h + .invoke_instance_method(&bt2, "round", id2, &[Box::new(IntegerBox::new(26))]) + .expect("round tb") + .unwrap(); ( s1.to_string_box().value, s2.to_string_box().value, @@ -201,7 +320,10 @@ mod tests { s4.to_string_box().value, ) }; - assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (MathBox)"); + assert_eq!( + out_tlv, out_tb, + "TLV vs TypeBox results should match (MathBox)" + ); } #[test] @@ -223,8 +345,19 @@ mod tests { let (bt1, id1, _hold1) = create_plugin_instance("EncodingBox"); let out_tlv = { let h = host.read().unwrap(); - let b64 = h.invoke_instance_method(&bt1, "base64Encode", id1, &[Box::new(StringBox::new("hi"))]).expect("b64 tlv").unwrap(); - let hex = h.invoke_instance_method(&bt1, "hexEncode", id1, &[Box::new(StringBox::new("hi"))]).expect("hex tlv").unwrap(); + let b64 = h + .invoke_instance_method( + &bt1, + "base64Encode", + id1, + &[Box::new(StringBox::new("hi"))], + ) + .expect("b64 tlv") + .unwrap(); + let hex = h + .invoke_instance_method(&bt1, "hexEncode", id1, &[Box::new(StringBox::new("hi"))]) + .expect("hex tlv") + .unwrap(); (b64.to_string_box().value, hex.to_string_box().value) }; @@ -233,11 +366,25 @@ mod tests { let (bt2, id2, _hold2) = create_plugin_instance("EncodingBox"); let out_tb = { let h = host.read().unwrap(); - let b64 = h.invoke_instance_method(&bt2, "base64Encode", id2, &[Box::new(StringBox::new("hi"))]).expect("b64 tb").unwrap(); - let hex = h.invoke_instance_method(&bt2, "hexEncode", id2, &[Box::new(StringBox::new("hi"))]).expect("hex tb").unwrap(); + let b64 = h + .invoke_instance_method( + &bt2, + "base64Encode", + id2, + &[Box::new(StringBox::new("hi"))], + ) + .expect("b64 tb") + .unwrap(); + let hex = h + .invoke_instance_method(&bt2, "hexEncode", id2, &[Box::new(StringBox::new("hi"))]) + .expect("hex tb") + .unwrap(); (b64.to_string_box().value, hex.to_string_box().value) }; - assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (EncodingBox)"); + assert_eq!( + out_tlv, out_tb, + "TLV vs TypeBox results should match (EncodingBox)" + ); } #[test] @@ -251,9 +398,17 @@ mod tests { let (bt1, id1, _hold1) = create_plugin_instance("RegexBox"); let out_tlv = { let h = host.read().unwrap(); - let _ = h.invoke_instance_method(&bt1, "compile", id1, &[Box::new(StringBox::new("h.+o"))]).expect("compile tlv"); - let m = h.invoke_instance_method(&bt1, "isMatch", id1, &[Box::new(StringBox::new("hello"))]).expect("isMatch tlv").unwrap(); - let f = h.invoke_instance_method(&bt1, "find", id1, &[Box::new(StringBox::new("hello"))]).expect("find tlv").unwrap(); + let _ = h + .invoke_instance_method(&bt1, "compile", id1, &[Box::new(StringBox::new("h.+o"))]) + .expect("compile tlv"); + let m = h + .invoke_instance_method(&bt1, "isMatch", id1, &[Box::new(StringBox::new("hello"))]) + .expect("isMatch tlv") + .unwrap(); + let f = h + .invoke_instance_method(&bt1, "find", id1, &[Box::new(StringBox::new("hello"))]) + .expect("find tlv") + .unwrap(); (m.to_string_box().value, f.to_string_box().value) }; @@ -262,12 +417,23 @@ mod tests { let (bt2, id2, _hold2) = create_plugin_instance("RegexBox"); let out_tb = { let h = host.read().unwrap(); - let _ = h.invoke_instance_method(&bt2, "compile", id2, &[Box::new(StringBox::new("h.+o"))]).expect("compile tb"); - let m = h.invoke_instance_method(&bt2, "isMatch", id2, &[Box::new(StringBox::new("hello"))]).expect("isMatch tb").unwrap(); - let f = h.invoke_instance_method(&bt2, "find", id2, &[Box::new(StringBox::new("hello"))]).expect("find tb").unwrap(); + let _ = h + .invoke_instance_method(&bt2, "compile", id2, &[Box::new(StringBox::new("h.+o"))]) + .expect("compile tb"); + let m = h + .invoke_instance_method(&bt2, "isMatch", id2, &[Box::new(StringBox::new("hello"))]) + .expect("isMatch tb") + .unwrap(); + let f = h + .invoke_instance_method(&bt2, "find", id2, &[Box::new(StringBox::new("hello"))]) + .expect("find tb") + .unwrap(); (m.to_string_box().value, f.to_string_box().value) }; - assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (RegexBox)"); + assert_eq!( + out_tlv, out_tb, + "TLV vs TypeBox results should match (RegexBox)" + ); } #[test] @@ -281,11 +447,51 @@ mod tests { let (bt1, id1, _hold1) = create_plugin_instance("PathBox"); let out_tlv = { let h = host.read().unwrap(); - let j = h.invoke_instance_method(&bt1, "join", id1, &[Box::new(StringBox::new("/a/b")), Box::new(StringBox::new("c.txt"))]).expect("join tlv").unwrap(); - let d = h.invoke_instance_method(&bt1, "dirname", id1, &[Box::new(StringBox::new("/a/b/c.txt"))]).expect("dirname tlv").unwrap(); - let b = h.invoke_instance_method(&bt1, "basename", id1, &[Box::new(StringBox::new("/a/b/c.txt"))]).expect("basename tlv").unwrap(); - let n = h.invoke_instance_method(&bt1, "normalize", id1, &[Box::new(StringBox::new("/a/./b/../b/c"))]).expect("normalize tlv").unwrap(); - (j.to_string_box().value, d.to_string_box().value, b.to_string_box().value, n.to_string_box().value) + let j = h + .invoke_instance_method( + &bt1, + "join", + id1, + &[ + Box::new(StringBox::new("/a/b")), + Box::new(StringBox::new("c.txt")), + ], + ) + .expect("join tlv") + .unwrap(); + let d = h + .invoke_instance_method( + &bt1, + "dirname", + id1, + &[Box::new(StringBox::new("/a/b/c.txt"))], + ) + .expect("dirname tlv") + .unwrap(); + let b = h + .invoke_instance_method( + &bt1, + "basename", + id1, + &[Box::new(StringBox::new("/a/b/c.txt"))], + ) + .expect("basename tlv") + .unwrap(); + let n = h + .invoke_instance_method( + &bt1, + "normalize", + id1, + &[Box::new(StringBox::new("/a/./b/../b/c"))], + ) + .expect("normalize tlv") + .unwrap(); + ( + j.to_string_box().value, + d.to_string_box().value, + b.to_string_box().value, + n.to_string_box().value, + ) }; // TypeBox path @@ -293,13 +499,56 @@ mod tests { let (bt2, id2, _hold2) = create_plugin_instance("PathBox"); let out_tb = { let h = host.read().unwrap(); - let j = h.invoke_instance_method(&bt2, "join", id2, &[Box::new(StringBox::new("/a/b")), Box::new(StringBox::new("c.txt"))]).expect("join tb").unwrap(); - let d = h.invoke_instance_method(&bt2, "dirname", id2, &[Box::new(StringBox::new("/a/b/c.txt"))]).expect("dirname tb").unwrap(); - let b = h.invoke_instance_method(&bt2, "basename", id2, &[Box::new(StringBox::new("/a/b/c.txt"))]).expect("basename tb").unwrap(); - let n = h.invoke_instance_method(&bt2, "normalize", id2, &[Box::new(StringBox::new("/a/./b/../b/c"))]).expect("normalize tb").unwrap(); - (j.to_string_box().value, d.to_string_box().value, b.to_string_box().value, n.to_string_box().value) + let j = h + .invoke_instance_method( + &bt2, + "join", + id2, + &[ + Box::new(StringBox::new("/a/b")), + Box::new(StringBox::new("c.txt")), + ], + ) + .expect("join tb") + .unwrap(); + let d = h + .invoke_instance_method( + &bt2, + "dirname", + id2, + &[Box::new(StringBox::new("/a/b/c.txt"))], + ) + .expect("dirname tb") + .unwrap(); + let b = h + .invoke_instance_method( + &bt2, + "basename", + id2, + &[Box::new(StringBox::new("/a/b/c.txt"))], + ) + .expect("basename tb") + .unwrap(); + let n = h + .invoke_instance_method( + &bt2, + "normalize", + id2, + &[Box::new(StringBox::new("/a/./b/../b/c"))], + ) + .expect("normalize tb") + .unwrap(); + ( + j.to_string_box().value, + d.to_string_box().value, + b.to_string_box().value, + n.to_string_box().value, + ) }; - assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (PathBox)"); + assert_eq!( + out_tlv, out_tb, + "TLV vs TypeBox results should match (PathBox)" + ); } #[test] @@ -314,9 +563,23 @@ mod tests { let (bt1, id1, _hold1) = create_plugin_instance("TOMLBox"); let out_tlv = { let h = host.read().unwrap(); - let _ = h.invoke_instance_method(&bt1, "parse", id1, &[Box::new(StringBox::new(toml_text))]).expect("parse tlv").unwrap(); - let name = h.invoke_instance_method(&bt1, "get", id1, &[Box::new(StringBox::new("package.name"))]).expect("get tlv").unwrap(); - let json = h.invoke_instance_method(&bt1, "toJson", id1, &[]).expect("toJson tlv").unwrap(); + let _ = h + .invoke_instance_method(&bt1, "parse", id1, &[Box::new(StringBox::new(toml_text))]) + .expect("parse tlv") + .unwrap(); + let name = h + .invoke_instance_method( + &bt1, + "get", + id1, + &[Box::new(StringBox::new("package.name"))], + ) + .expect("get tlv") + .unwrap(); + let json = h + .invoke_instance_method(&bt1, "toJson", id1, &[]) + .expect("toJson tlv") + .unwrap(); (name.to_string_box().value, json.to_string_box().value) }; @@ -325,12 +588,29 @@ mod tests { let (bt2, id2, _hold2) = create_plugin_instance("TOMLBox"); let out_tb = { let h = host.read().unwrap(); - let _ = h.invoke_instance_method(&bt2, "parse", id2, &[Box::new(StringBox::new(toml_text))]).expect("parse tb").unwrap(); - let name = h.invoke_instance_method(&bt2, "get", id2, &[Box::new(StringBox::new("package.name"))]).expect("get tb").unwrap(); - let json = h.invoke_instance_method(&bt2, "toJson", id2, &[]).expect("toJson tb").unwrap(); + let _ = h + .invoke_instance_method(&bt2, "parse", id2, &[Box::new(StringBox::new(toml_text))]) + .expect("parse tb") + .unwrap(); + let name = h + .invoke_instance_method( + &bt2, + "get", + id2, + &[Box::new(StringBox::new("package.name"))], + ) + .expect("get tb") + .unwrap(); + let json = h + .invoke_instance_method(&bt2, "toJson", id2, &[]) + .expect("toJson tb") + .unwrap(); (name.to_string_box().value, json.to_string_box().value) }; - assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (TOMLBox)"); + assert_eq!( + out_tlv, out_tb, + "TLV vs TypeBox results should match (TOMLBox)" + ); } #[test] @@ -344,7 +624,10 @@ mod tests { let (bt1, id1, _hold1) = create_plugin_instance("TimeBox"); let t_tlv = { let h = host.read().unwrap(); - let v = h.invoke_instance_method(&bt1, "now", id1, &[]).expect("now tlv").unwrap(); + let v = h + .invoke_instance_method(&bt1, "now", id1, &[]) + .expect("now tlv") + .unwrap(); v.to_string_box().value.parse::().unwrap_or(0) }; @@ -353,7 +636,10 @@ mod tests { let (bt2, id2, _hold2) = create_plugin_instance("TimeBox"); let t_tb = { let h = host.read().unwrap(); - let v = h.invoke_instance_method(&bt2, "now", id2, &[]).expect("now tb").unwrap(); + let v = h + .invoke_instance_method(&bt2, "now", id2, &[]) + .expect("now tb") + .unwrap(); v.to_string_box().value.parse::().unwrap_or(0) }; @@ -372,10 +658,21 @@ mod tests { let (bt1, id1, _hold1) = create_plugin_instance("CounterBox"); let (a1, b1) = { let h = host.read().unwrap(); - let a = h.invoke_instance_method(&bt1, "get", id1, &[]).expect("get tlv").unwrap(); - let _ = h.invoke_instance_method(&bt1, "inc", id1, &[]).expect("inc tlv"); - let b = h.invoke_instance_method(&bt1, "get", id1, &[]).expect("get2 tlv").unwrap(); - (a.to_string_box().value.parse::().unwrap_or(0), b.to_string_box().value.parse::().unwrap_or(0)) + let a = h + .invoke_instance_method(&bt1, "get", id1, &[]) + .expect("get tlv") + .unwrap(); + let _ = h + .invoke_instance_method(&bt1, "inc", id1, &[]) + .expect("inc tlv"); + let b = h + .invoke_instance_method(&bt1, "get", id1, &[]) + .expect("get2 tlv") + .unwrap(); + ( + a.to_string_box().value.parse::().unwrap_or(0), + b.to_string_box().value.parse::().unwrap_or(0), + ) }; assert_eq!(b1 - a1, 1, "CounterBox TLV should increment by 1"); @@ -384,10 +681,21 @@ mod tests { let (bt2, id2, _hold2) = create_plugin_instance("CounterBox"); let (a2, b2) = { let h = host.read().unwrap(); - let a = h.invoke_instance_method(&bt2, "get", id2, &[]).expect("get tb").unwrap(); - let _ = h.invoke_instance_method(&bt2, "inc", id2, &[]).expect("inc tb"); - let b = h.invoke_instance_method(&bt2, "get", id2, &[]).expect("get2 tb").unwrap(); - (a.to_string_box().value.parse::().unwrap_or(0), b.to_string_box().value.parse::().unwrap_or(0)) + let a = h + .invoke_instance_method(&bt2, "get", id2, &[]) + .expect("get tb") + .unwrap(); + let _ = h + .invoke_instance_method(&bt2, "inc", id2, &[]) + .expect("inc tb"); + let b = h + .invoke_instance_method(&bt2, "get", id2, &[]) + .expect("get2 tb") + .unwrap(); + ( + a.to_string_box().value.parse::().unwrap_or(0), + b.to_string_box().value.parse::().unwrap_or(0), + ) }; assert_eq!(b2 - a2, 1, "CounterBox TypeBox should increment by 1"); } @@ -400,7 +708,11 @@ mod tests { // Prepare temp file path let mut p = std::env::temp_dir(); - p.push(format!("nyash_test_{}_{}.txt", std::process::id(), rand_id())) ; + p.push(format!( + "nyash_test_{}_{}.txt", + std::process::id(), + rand_id() + )); let path_str = p.to_string_lossy().to_string(); // TLV path @@ -408,13 +720,42 @@ mod tests { let (bt1, id1, _hold1) = create_plugin_instance("FileBox"); let out_tlv = { let h = host.read().unwrap(); - let _ = h.invoke_instance_method(&bt1, "open", id1, &[Box::new(StringBox::new(&path_str)), Box::new(StringBox::new("w"))]).expect("open tlv"); - let _ = h.invoke_instance_method(&bt1, "write", id1, &[Box::new(StringBox::new("hello"))]).expect("write tlv"); - let _ = h.invoke_instance_method(&bt1, "close", id1, &[]).expect("close tlv"); + let _ = h + .invoke_instance_method( + &bt1, + "open", + id1, + &[ + Box::new(StringBox::new(&path_str)), + Box::new(StringBox::new("w")), + ], + ) + .expect("open tlv"); + let _ = h + .invoke_instance_method(&bt1, "write", id1, &[Box::new(StringBox::new("hello"))]) + .expect("write tlv"); + let _ = h + .invoke_instance_method(&bt1, "close", id1, &[]) + .expect("close tlv"); // reopen and read - let _ = h.invoke_instance_method(&bt1, "open", id1, &[Box::new(StringBox::new(&path_str)), Box::new(StringBox::new("r"))]).expect("open2 tlv"); - let rd = h.invoke_instance_method(&bt1, "read", id1, &[]).expect("read tlv").unwrap(); - let _ = h.invoke_instance_method(&bt1, "close", id1, &[]).expect("close2 tlv"); + let _ = h + .invoke_instance_method( + &bt1, + "open", + id1, + &[ + Box::new(StringBox::new(&path_str)), + Box::new(StringBox::new("r")), + ], + ) + .expect("open2 tlv"); + let rd = h + .invoke_instance_method(&bt1, "read", id1, &[]) + .expect("read tlv") + .unwrap(); + let _ = h + .invoke_instance_method(&bt1, "close", id1, &[]) + .expect("close2 tlv"); rd.to_string_box().value }; @@ -423,19 +764,51 @@ mod tests { let (bt2, id2, _hold2) = create_plugin_instance("FileBox"); let out_tb = { let h = host.read().unwrap(); - let _ = h.invoke_instance_method(&bt2, "open", id2, &[Box::new(StringBox::new(&path_str)), Box::new(StringBox::new("w"))]).expect("open tb"); - let _ = h.invoke_instance_method(&bt2, "write", id2, &[Box::new(StringBox::new("hello"))]).expect("write tb"); - let _ = h.invoke_instance_method(&bt2, "close", id2, &[]).expect("close tb"); - let _ = h.invoke_instance_method(&bt2, "open", id2, &[Box::new(StringBox::new(&path_str)), Box::new(StringBox::new("r"))]).expect("open2 tb"); - let rd = h.invoke_instance_method(&bt2, "read", id2, &[]).expect("read tb").unwrap(); - let _ = h.invoke_instance_method(&bt2, "close", id2, &[]).expect("close2 tb"); + let _ = h + .invoke_instance_method( + &bt2, + "open", + id2, + &[ + Box::new(StringBox::new(&path_str)), + Box::new(StringBox::new("w")), + ], + ) + .expect("open tb"); + let _ = h + .invoke_instance_method(&bt2, "write", id2, &[Box::new(StringBox::new("hello"))]) + .expect("write tb"); + let _ = h + .invoke_instance_method(&bt2, "close", id2, &[]) + .expect("close tb"); + let _ = h + .invoke_instance_method( + &bt2, + "open", + id2, + &[ + Box::new(StringBox::new(&path_str)), + Box::new(StringBox::new("r")), + ], + ) + .expect("open2 tb"); + let rd = h + .invoke_instance_method(&bt2, "read", id2, &[]) + .expect("read tb") + .unwrap(); + let _ = h + .invoke_instance_method(&bt2, "close", id2, &[]) + .expect("close2 tb"); rd.to_string_box().value }; // Cleanup best-effort let _ = fs::remove_file(&path_str); - assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (FileBox)"); + assert_eq!( + out_tlv, out_tb, + "TLV vs TypeBox results should match (FileBox)" + ); } fn rand_id() -> u64 { diff --git a/src/tests/vtable_array_ext.rs b/src/tests/vtable_array_ext.rs index 0d15ae6f..de16cebb 100644 --- a/src/tests/vtable_array_ext.rs +++ b/src/tests/vtable_array_ext.rs @@ -1,61 +1,190 @@ #[test] fn vtable_array_push_get_len_pop_clear() { use crate::backend::vm::VM; - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType}; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, + }; std::env::set_var("NYASH_ABI_VTABLE", "1"); // Case 1: push("x"); get(0) - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::String, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; let arr = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: arr, box_type: "ArrayBox".into(), args: vec![] }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: arr, + box_type: "ArrayBox".into(), + args: vec![], + }); let sval = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: sval, value: ConstValue::String("x".into()) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "push".into(), args: vec![sval], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: sval, + value: ConstValue::String("x".into()), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: arr, + method: "push".into(), + args: vec![sval], + method_id: None, + effects: EffectMask::PURE, + }); let idx0 = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: idx0, value: ConstValue::Integer(0) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: idx0, + value: ConstValue::Integer(0), + }); let got = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(got), box_val: arr, method: "get".into(), args: vec![idx0], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(got) }); - let mut m = MirModule::new("arr_push_get".into()); m.add_function(f); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(got), + box_val: arr, + method: "get".into(), + args: vec![idx0], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(got) }); + let mut m = MirModule::new("arr_push_get".into()); + m.add_function(f); let mut vm = VM::new(); let out = vm.execute_module(&m).expect("vm exec"); assert_eq!(out.to_string_box().value, "x"); // Case 2: push("y"); pop() -> "y" - let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE }; + let sig2 = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::String, + effects: EffectMask::PURE, + }; let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0)); let bb2 = f2.entry_block; let a2 = f2.next_value_id(); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: a2, box_type: "ArrayBox".into(), args: vec![] }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: a2, + box_type: "ArrayBox".into(), + args: vec![], + }); let y = f2.next_value_id(); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: y, value: ConstValue::String("y".into()) }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a2, method: "push".into(), args: vec![y], method_id: None, effects: EffectMask::PURE }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: y, + value: ConstValue::String("y".into()), + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a2, + method: "push".into(), + args: vec![y], + method_id: None, + effects: EffectMask::PURE, + }); let popped = f2.next_value_id(); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(popped), box_val: a2, method: "pop".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: Some(popped) }); - let mut m2 = MirModule::new("arr_pop".into()); m2.add_function(f2); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(popped), + box_val: a2, + method: "pop".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Return { + value: Some(popped), + }); + let mut m2 = MirModule::new("arr_pop".into()); + m2.add_function(f2); let mut vm2 = VM::new(); let out2 = vm2.execute_module(&m2).expect("vm exec"); assert_eq!(out2.to_string_box().value, "y"); // Case 3: push("z"); clear(); len() -> 0 - let sig3 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig3 = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f3 = MirFunction::new(sig3, BasicBlockId::new(0)); let bb3 = f3.entry_block; let a3 = f3.next_value_id(); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::NewBox { dst: a3, box_type: "ArrayBox".into(), args: vec![] }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: a3, + box_type: "ArrayBox".into(), + args: vec![], + }); let z = f3.next_value_id(); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: z, value: ConstValue::String("z".into()) }); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "push".into(), args: vec![z], method_id: None, effects: EffectMask::PURE }); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "clear".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: z, + value: ConstValue::String("z".into()), + }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a3, + method: "push".into(), + args: vec![z], + method_id: None, + effects: EffectMask::PURE, + }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a3, + method: "clear".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); let ln = f3.next_value_id(); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(ln), box_val: a3, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Return { value: Some(ln) }); - let mut m3 = MirModule::new("arr_clear_len".into()); m3.add_function(f3); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(ln), + box_val: a3, + method: "len".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(ln) }); + let mut m3 = MirModule::new("arr_clear_len".into()); + m3.add_function(f3); let mut vm3 = VM::new(); let out3 = vm3.execute_module(&m3).expect("vm exec"); assert_eq!(out3.to_string_box().value, "0"); } - diff --git a/src/tests/vtable_array_p1.rs b/src/tests/vtable_array_p1.rs index 9a58aa27..880662ff 100644 --- a/src/tests/vtable_array_p1.rs +++ b/src/tests/vtable_array_p1.rs @@ -1,77 +1,350 @@ #[test] fn vtable_array_contains_indexof_join() { use crate::backend::vm::VM; - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType}; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, + }; std::env::set_var("NYASH_ABI_VTABLE", "1"); // contains: ["a","b"].contains("b") == true; contains("c") == false - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; let arr = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: arr, box_type: "ArrayBox".into(), args: vec![] }); - let sa = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: sa, value: ConstValue::String("a".into()) }); - let sb = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: sb, value: ConstValue::String("b".into()) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "push".into(), args: vec![sa], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "push".into(), args: vec![sb], method_id: None, effects: EffectMask::PURE }); - let sc = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: sc, value: ConstValue::String("c".into()) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: arr, + box_type: "ArrayBox".into(), + args: vec![], + }); + let sa = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: sa, + value: ConstValue::String("a".into()), + }); + let sb = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: sb, + value: ConstValue::String("b".into()), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: arr, + method: "push".into(), + args: vec![sa], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: arr, + method: "push".into(), + args: vec![sb], + method_id: None, + effects: EffectMask::PURE, + }); + let sc = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: sc, + value: ConstValue::String("c".into()), + }); let got1 = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(got1), box_val: arr, method: "contains".into(), args: vec![sb], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(got1), + box_val: arr, + method: "contains".into(), + args: vec![sb], + method_id: None, + effects: EffectMask::PURE, + }); let got2 = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(got2), box_val: arr, method: "contains".into(), args: vec![sc], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(got2), + box_val: arr, + method: "contains".into(), + args: vec![sc], + method_id: None, + effects: EffectMask::PURE, + }); // return got1.equals(true) && got2.equals(false) as 1 for pass // Instead, just return 0 or 1 using simple branch-like comparison via toString // We check: got1==true -> "true", got2==false -> "false" and return 1 if both match else 0 // For brevity, just return got1.toString() ("true") length + got2.toString() ("false") length == 9 - let s1 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(s1), box_val: got1, method: "toString".into(), args: vec![], method_id: Some(0), effects: EffectMask::PURE }); - let s2 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(s2), box_val: got2, method: "toString".into(), args: vec![], method_id: Some(0), effects: EffectMask::PURE }); - let len1 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(len1), box_val: s1, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - let len2 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(len2), box_val: s2, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); + let s1 = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(s1), + box_val: got1, + method: "toString".into(), + args: vec![], + method_id: Some(0), + effects: EffectMask::PURE, + }); + let s2 = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(s2), + box_val: got2, + method: "toString".into(), + args: vec![], + method_id: Some(0), + effects: EffectMask::PURE, + }); + let len1 = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(len1), + box_val: s1, + method: "len".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + let len2 = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(len2), + box_val: s2, + method: "len".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); // len1 + len2 - let sum = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: sum, op: crate::mir::BinaryOp::Add, lhs: len1, rhs: len2 }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(sum) }); - let mut m = MirModule::new("arr_contains".into()); m.add_function(f); + let sum = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BinOp { + dst: sum, + op: crate::mir::BinaryOp::Add, + lhs: len1, + rhs: len2, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(sum) }); + let mut m = MirModule::new("arr_contains".into()); + m.add_function(f); let mut vm = VM::new(); let out = vm.execute_module(&m).expect("vm exec"); assert_eq!(out.to_string_box().value, "9"); // "true"(4)+"false"(5) // indexOf: ["x","y"].indexOf("y") == 1; indexOf("z") == -1 - let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig2 = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0)); let bb2 = f2.entry_block; let a2 = f2.next_value_id(); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: a2, box_type: "ArrayBox".into(), args: vec![] }); - let sx = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: sx, value: ConstValue::String("x".into()) }); - let sy = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: sy, value: ConstValue::String("y".into()) }); - let sz = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: sz, value: ConstValue::String("z".into()) }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a2, method: "push".into(), args: vec![sx], method_id: None, effects: EffectMask::PURE }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a2, method: "push".into(), args: vec![sy], method_id: None, effects: EffectMask::PURE }); - let i1 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(i1), box_val: a2, method: "indexOf".into(), args: vec![sy], method_id: None, effects: EffectMask::PURE }); - let i2 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(i2), box_val: a2, method: "indexOf".into(), args: vec![sz], method_id: None, effects: EffectMask::PURE }); - let sum2 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BinOp { dst: sum2, op: crate::mir::BinaryOp::Add, lhs: i1, rhs: i2 }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: Some(sum2) }); - let mut m2 = MirModule::new("arr_indexOf".into()); m2.add_function(f2); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: a2, + box_type: "ArrayBox".into(), + args: vec![], + }); + let sx = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: sx, + value: ConstValue::String("x".into()), + }); + let sy = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: sy, + value: ConstValue::String("y".into()), + }); + let sz = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: sz, + value: ConstValue::String("z".into()), + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a2, + method: "push".into(), + args: vec![sx], + method_id: None, + effects: EffectMask::PURE, + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a2, + method: "push".into(), + args: vec![sy], + method_id: None, + effects: EffectMask::PURE, + }); + let i1 = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(i1), + box_val: a2, + method: "indexOf".into(), + args: vec![sy], + method_id: None, + effects: EffectMask::PURE, + }); + let i2 = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(i2), + box_val: a2, + method: "indexOf".into(), + args: vec![sz], + method_id: None, + effects: EffectMask::PURE, + }); + let sum2 = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BinOp { + dst: sum2, + op: crate::mir::BinaryOp::Add, + lhs: i1, + rhs: i2, + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(sum2) }); + let mut m2 = MirModule::new("arr_indexOf".into()); + m2.add_function(f2); let mut vm2 = VM::new(); let out2 = vm2.execute_module(&m2).expect("vm exec"); assert_eq!(out2.to_string_box().value, "0"); // 1 + (-1) // join: ["a","b","c"].join("-") == "a-b-c" - let sig3 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE }; + let sig3 = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::String, + effects: EffectMask::PURE, + }; let mut f3 = MirFunction::new(sig3, BasicBlockId::new(0)); let bb3 = f3.entry_block; let a3 = f3.next_value_id(); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::NewBox { dst: a3, box_type: "ArrayBox".into(), args: vec![] }); - let a = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: a, value: ConstValue::String("a".into()) }); - let b = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: b, value: ConstValue::String("b".into()) }); - let c = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: c, value: ConstValue::String("c".into()) }); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "push".into(), args: vec![a], method_id: None, effects: EffectMask::PURE }); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "push".into(), args: vec![b], method_id: None, effects: EffectMask::PURE }); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "push".into(), args: vec![c], method_id: None, effects: EffectMask::PURE }); - let sep = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: sep, value: ConstValue::String("-".into()) }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: a3, + box_type: "ArrayBox".into(), + args: vec![], + }); + let a = f3.next_value_id(); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: a, + value: ConstValue::String("a".into()), + }); + let b = f3.next_value_id(); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: b, + value: ConstValue::String("b".into()), + }); + let c = f3.next_value_id(); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: c, + value: ConstValue::String("c".into()), + }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a3, + method: "push".into(), + args: vec![a], + method_id: None, + effects: EffectMask::PURE, + }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a3, + method: "push".into(), + args: vec![b], + method_id: None, + effects: EffectMask::PURE, + }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a3, + method: "push".into(), + args: vec![c], + method_id: None, + effects: EffectMask::PURE, + }); + let sep = f3.next_value_id(); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: sep, + value: ConstValue::String("-".into()), + }); let joined = f3.next_value_id(); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(joined), box_val: a3, method: "join".into(), args: vec![sep], method_id: None, effects: EffectMask::PURE }); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Return { value: Some(joined) }); - let mut m3 = MirModule::new("arr_join".into()); m3.add_function(f3); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(joined), + box_val: a3, + method: "join".into(), + args: vec![sep], + method_id: None, + effects: EffectMask::PURE, + }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Return { + value: Some(joined), + }); + let mut m3 = MirModule::new("arr_join".into()); + m3.add_function(f3); let mut vm3 = VM::new(); let out3 = vm3.execute_module(&m3).expect("vm exec"); assert_eq!(out3.to_string_box().value, "a-b-c"); diff --git a/src/tests/vtable_array_p2.rs b/src/tests/vtable_array_p2.rs index 40e55df1..04aa68c4 100644 --- a/src/tests/vtable_array_p2.rs +++ b/src/tests/vtable_array_p2.rs @@ -1,73 +1,315 @@ #[test] fn vtable_array_sort_reverse_slice() { use crate::backend::vm::VM; - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType}; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, + }; std::env::set_var("NYASH_ABI_VTABLE", "1"); // sort: push 3,1,2 -> sort() -> get(0) == 1 - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; let arr = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: arr, box_type: "ArrayBox".into(), args: vec![] }); - let c3 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: c3, value: ConstValue::Integer(3) }); - let c1 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: c1, value: ConstValue::Integer(1) }); - let c2 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: c2, value: ConstValue::Integer(2) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "push".into(), args: vec![c3], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "push".into(), args: vec![c1], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "push".into(), args: vec![c2], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "sort".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - let idx0 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: idx0, value: ConstValue::Integer(0) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: arr, + box_type: "ArrayBox".into(), + args: vec![], + }); + let c3 = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: c3, + value: ConstValue::Integer(3), + }); + let c1 = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: c1, + value: ConstValue::Integer(1), + }); + let c2 = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: c2, + value: ConstValue::Integer(2), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: arr, + method: "push".into(), + args: vec![c3], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: arr, + method: "push".into(), + args: vec![c1], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: arr, + method: "push".into(), + args: vec![c2], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: arr, + method: "sort".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + let idx0 = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: idx0, + value: ConstValue::Integer(0), + }); let got = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(got), box_val: arr, method: "get".into(), args: vec![idx0], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(got) }); - let mut m = MirModule::new("arr_sort".into()); m.add_function(f); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(got), + box_val: arr, + method: "get".into(), + args: vec![idx0], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(got) }); + let mut m = MirModule::new("arr_sort".into()); + m.add_function(f); let mut vm = VM::new(); let out = vm.execute_module(&m).expect("vm exec"); assert_eq!(out.to_string_box().value, "1"); // reverse: push 1,2 -> reverse() -> get(0) == 2 - let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig2 = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0)); let bb2 = f2.entry_block; let a2 = f2.next_value_id(); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: a2, box_type: "ArrayBox".into(), args: vec![] }); - let i1 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: i1, value: ConstValue::Integer(1) }); - let i2 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: i2, value: ConstValue::Integer(2) }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a2, method: "push".into(), args: vec![i1], method_id: None, effects: EffectMask::PURE }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a2, method: "push".into(), args: vec![i2], method_id: None, effects: EffectMask::PURE }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a2, method: "reverse".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - let z0 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: z0, value: ConstValue::Integer(0) }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: a2, + box_type: "ArrayBox".into(), + args: vec![], + }); + let i1 = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: i1, + value: ConstValue::Integer(1), + }); + let i2 = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: i2, + value: ConstValue::Integer(2), + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a2, + method: "push".into(), + args: vec![i1], + method_id: None, + effects: EffectMask::PURE, + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a2, + method: "push".into(), + args: vec![i2], + method_id: None, + effects: EffectMask::PURE, + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a2, + method: "reverse".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + let z0 = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: z0, + value: ConstValue::Integer(0), + }); let g2 = f2.next_value_id(); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(g2), box_val: a2, method: "get".into(), args: vec![z0], method_id: None, effects: EffectMask::PURE }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: Some(g2) }); - let mut m2 = MirModule::new("arr_reverse".into()); m2.add_function(f2); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(g2), + box_val: a2, + method: "get".into(), + args: vec![z0], + method_id: None, + effects: EffectMask::PURE, + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(g2) }); + let mut m2 = MirModule::new("arr_reverse".into()); + m2.add_function(f2); let mut vm2 = VM::new(); let out2 = vm2.execute_module(&m2).expect("vm exec"); assert_eq!(out2.to_string_box().value, "2"); // slice: push "a","b","c" -> slice(0,2) -> len()==2 - let sig3 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig3 = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f3 = MirFunction::new(sig3, BasicBlockId::new(0)); let bb3 = f3.entry_block; let a3 = f3.next_value_id(); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::NewBox { dst: a3, box_type: "ArrayBox".into(), args: vec![] }); - let sa = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: sa, value: ConstValue::String("a".into()) }); - let sb = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: sb, value: ConstValue::String("b".into()) }); - let sc = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: sc, value: ConstValue::String("c".into()) }); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "push".into(), args: vec![sa], method_id: None, effects: EffectMask::PURE }); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "push".into(), args: vec![sb], method_id: None, effects: EffectMask::PURE }); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "push".into(), args: vec![sc], method_id: None, effects: EffectMask::PURE }); - let s0 = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: s0, value: ConstValue::Integer(0) }); - let s2 = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: s2, value: ConstValue::Integer(2) }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: a3, + box_type: "ArrayBox".into(), + args: vec![], + }); + let sa = f3.next_value_id(); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: sa, + value: ConstValue::String("a".into()), + }); + let sb = f3.next_value_id(); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: sb, + value: ConstValue::String("b".into()), + }); + let sc = f3.next_value_id(); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: sc, + value: ConstValue::String("c".into()), + }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a3, + method: "push".into(), + args: vec![sa], + method_id: None, + effects: EffectMask::PURE, + }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a3, + method: "push".into(), + args: vec![sb], + method_id: None, + effects: EffectMask::PURE, + }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: a3, + method: "push".into(), + args: vec![sc], + method_id: None, + effects: EffectMask::PURE, + }); + let s0 = f3.next_value_id(); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: s0, + value: ConstValue::Integer(0), + }); + let s2 = f3.next_value_id(); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: s2, + value: ConstValue::Integer(2), + }); let sub = f3.next_value_id(); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(sub), box_val: a3, method: "slice".into(), args: vec![s0, s2], method_id: None, effects: EffectMask::PURE }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(sub), + box_val: a3, + method: "slice".into(), + args: vec![s0, s2], + method_id: None, + effects: EffectMask::PURE, + }); let ln = f3.next_value_id(); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(ln), box_val: sub, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Return { value: Some(ln) }); - let mut m3 = MirModule::new("arr_slice".into()); m3.add_function(f3); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(ln), + box_val: sub, + method: "len".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(ln) }); + let mut m3 = MirModule::new("arr_slice".into()); + m3.add_function(f3); let mut vm3 = VM::new(); let out3 = vm3.execute_module(&m3).expect("vm exec"); assert_eq!(out3.to_string_box().value, "2"); } - diff --git a/src/tests/vtable_array_string.rs b/src/tests/vtable_array_string.rs index a59ba6ae..8386fdcd 100644 --- a/src/tests/vtable_array_string.rs +++ b/src/tests/vtable_array_string.rs @@ -1,45 +1,126 @@ #[test] fn vtable_array_and_string_len_get_set() { use crate::backend::vm::VM; - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType}; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, + }; std::env::set_var("NYASH_ABI_VTABLE", "1"); // Array: set(0, "x"); len(); get(0) - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::String, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; let arr = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: arr, box_type: "ArrayBox".into(), args: vec![] }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: arr, + box_type: "ArrayBox".into(), + args: vec![], + }); let idx0 = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: idx0, value: ConstValue::Integer(0) }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: idx0, + value: ConstValue::Integer(0), + }); let sval = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: sval, value: ConstValue::String("x".into()) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "set".into(), args: vec![idx0, sval], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: sval, + value: ConstValue::String("x".into()), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: arr, + method: "set".into(), + args: vec![idx0, sval], + method_id: None, + effects: EffectMask::PURE, + }); let lenv = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(lenv), box_val: arr, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(lenv), + box_val: arr, + method: "len".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); // sanity: len should be 1 (not asserted here, just exercise path) let got = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(got), box_val: arr, method: "get".into(), args: vec![idx0], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(got) }); - let mut m = MirModule::new("tarr".into()); m.add_function(f); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(got), + box_val: arr, + method: "get".into(), + args: vec![idx0], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(got) }); + let mut m = MirModule::new("tarr".into()); + m.add_function(f); let mut vm = VM::new(); let out = vm.execute_module(&m).expect("vm exec"); assert_eq!(out.to_string_box().value, "x"); // String: len() - let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig2 = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0)); let bb2 = f2.entry_block; let s = f2.next_value_id(); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: s, value: ConstValue::String("abc".into()) }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: s, + value: ConstValue::String("abc".into()), + }); let sb = f2.next_value_id(); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: sb, box_type: "StringBox".into(), args: vec![s] }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: sb, + box_type: "StringBox".into(), + args: vec![s], + }); let ln = f2.next_value_id(); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(ln), box_val: sb, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: Some(ln) }); - let mut m2 = MirModule::new("tstr".into()); m2.add_function(f2); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(ln), + box_val: sb, + method: "len".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(ln) }); + let mut m2 = MirModule::new("tstr".into()); + m2.add_function(f2); let mut vm2 = VM::new(); let out2 = vm2.execute_module(&m2).expect("vm exec"); assert_eq!(out2.to_string_box().value, "3"); } - diff --git a/src/tests/vtable_console.rs b/src/tests/vtable_console.rs index 33c40391..2b5f2539 100644 --- a/src/tests/vtable_console.rs +++ b/src/tests/vtable_console.rs @@ -1,22 +1,68 @@ #[test] fn vtable_console_log_clear_smoke() { use crate::backend::vm::VM; - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType}; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, + }; std::env::set_var("NYASH_ABI_VTABLE", "1"); - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; let con = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: con, box_type: "ConsoleBox".into(), args: vec![] }); - let msg = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: msg, value: ConstValue::String("hi".into()) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: con, method: "log".into(), args: vec![msg], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: con, method: "clear".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - let zero = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: zero, value: ConstValue::Integer(0) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(zero) }); - let mut m = MirModule::new("console_smoke".into()); m.add_function(f); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: con, + box_type: "ConsoleBox".into(), + args: vec![], + }); + let msg = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: msg, + value: ConstValue::String("hi".into()), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: con, + method: "log".into(), + args: vec![msg], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: con, + method: "clear".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + let zero = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: zero, + value: ConstValue::Integer(0), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(zero) }); + let mut m = MirModule::new("console_smoke".into()); + m.add_function(f); let mut vm = VM::new(); let out = vm.execute_module(&m).expect("vm exec"); assert_eq!(out.to_string_box().value, "0"); } - diff --git a/src/tests/vtable_map_ext.rs b/src/tests/vtable_map_ext.rs index 2c85f8ba..4ae61b14 100644 --- a/src/tests/vtable_map_ext.rs +++ b/src/tests/vtable_map_ext.rs @@ -1,51 +1,226 @@ #[test] fn vtable_map_keys_values_delete_clear() { use crate::backend::vm::VM; - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType}; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, + }; std::env::set_var("NYASH_ABI_VTABLE", "1"); // keys/values size check - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; let m = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: m, box_type: "MapBox".into(), args: vec![] }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: m, + box_type: "MapBox".into(), + args: vec![], + }); // set two entries - let k1 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k1, value: ConstValue::String("a".into()) }); - let v1 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v1, value: ConstValue::Integer(1) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m, method: "set".into(), args: vec![k1, v1], method_id: None, effects: EffectMask::PURE }); - let k2 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k2, value: ConstValue::String("b".into()) }); - let v2 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v2, value: ConstValue::Integer(2) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m, method: "set".into(), args: vec![k2, v2], method_id: None, effects: EffectMask::PURE }); + let k1 = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: k1, + value: ConstValue::String("a".into()), + }); + let v1 = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v1, + value: ConstValue::Integer(1), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: m, + method: "set".into(), + args: vec![k1, v1], + method_id: None, + effects: EffectMask::PURE, + }); + let k2 = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: k2, + value: ConstValue::String("b".into()), + }); + let v2 = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v2, + value: ConstValue::Integer(2), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: m, + method: "set".into(), + args: vec![k2, v2], + method_id: None, + effects: EffectMask::PURE, + }); // keys().len + values().len == 4 - let keys = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(keys), box_val: m, method: "keys".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - let klen = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(klen), box_val: keys, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - let vals = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(vals), box_val: m, method: "values".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - let vlen = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(vlen), box_val: vals, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - let sum = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: sum, op: crate::mir::BinaryOp::Add, lhs: klen, rhs: vlen }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(sum) }); - let mut m1 = MirModule::new("map_keys_values".into()); m1.add_function(f); + let keys = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(keys), + box_val: m, + method: "keys".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + let klen = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(klen), + box_val: keys, + method: "len".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + let vals = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(vals), + box_val: m, + method: "values".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + let vlen = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(vlen), + box_val: vals, + method: "len".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + let sum = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BinOp { + dst: sum, + op: crate::mir::BinaryOp::Add, + lhs: klen, + rhs: vlen, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(sum) }); + let mut m1 = MirModule::new("map_keys_values".into()); + m1.add_function(f); let mut vm1 = VM::new(); let out1 = vm1.execute_module(&m1).expect("vm exec"); assert_eq!(out1.to_string_box().value, "4"); // delete + clear → size 0 - let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig2 = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0)); let bb2 = f2.entry_block; let m2v = f2.next_value_id(); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: m2v, box_type: "MapBox".into(), args: vec![] }); - let k = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: k, value: ConstValue::String("x".into()) }); - let v = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: v, value: ConstValue::String("y".into()) }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m2v, method: "set".into(), args: vec![k, v], method_id: None, effects: EffectMask::PURE }); - let dk = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: dk, value: ConstValue::String("x".into()) }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m2v, method: "delete".into(), args: vec![dk], method_id: None, effects: EffectMask::PURE }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m2v, method: "clear".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - let sz = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(sz), box_val: m2v, method: "size".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: Some(sz) }); - let mut mm2 = MirModule::new("map_delete_clear".into()); mm2.add_function(f2); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: m2v, + box_type: "MapBox".into(), + args: vec![], + }); + let k = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: k, + value: ConstValue::String("x".into()), + }); + let v = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v, + value: ConstValue::String("y".into()), + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: m2v, + method: "set".into(), + args: vec![k, v], + method_id: None, + effects: EffectMask::PURE, + }); + let dk = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: dk, + value: ConstValue::String("x".into()), + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: m2v, + method: "delete".into(), + args: vec![dk], + method_id: None, + effects: EffectMask::PURE, + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: m2v, + method: "clear".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + let sz = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(sz), + box_val: m2v, + method: "size".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(sz) }); + let mut mm2 = MirModule::new("map_delete_clear".into()); + mm2.add_function(f2); let mut vm2 = VM::new(); let out2 = vm2.execute_module(&mm2).expect("vm exec"); assert_eq!(out2.to_string_box().value, "0"); } - diff --git a/src/tests/vtable_strict.rs b/src/tests/vtable_strict.rs index bff443a3..df5e1700 100644 --- a/src/tests/vtable_strict.rs +++ b/src/tests/vtable_strict.rs @@ -1,40 +1,109 @@ #[test] fn vtable_map_set_and_strict_unknown() { use crate::backend::vm::VM; - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType}; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, + }; std::env::set_var("NYASH_ABI_VTABLE", "1"); // Build: new MapBox; call set("k","v"); size(); return size - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; let mapv = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: mapv, box_type: "MapBox".into(), args: vec![] }); - let k = f.next_value_id(); let v = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k, value: ConstValue::String("k".into()) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v, value: ConstValue::String("v".into()) }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: mapv, method: "set".into(), args: vec![k, v], method_id: None, effects: EffectMask::PURE }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: mapv, + box_type: "MapBox".into(), + args: vec![], + }); + let k = f.next_value_id(); + let v = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: k, + value: ConstValue::String("k".into()), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: v, + value: ConstValue::String("v".into()), + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: mapv, + method: "set".into(), + args: vec![k, v], + method_id: None, + effects: EffectMask::PURE, + }); let sz = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(sz), box_val: mapv, method: "size".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(sz) }); - let mut m = MirModule::new("t".into()); m.add_function(f); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(sz), + box_val: mapv, + method: "size".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(sz) }); + let mut m = MirModule::new("t".into()); + m.add_function(f); let mut vm = VM::new(); let out = vm.execute_module(&m).expect("vm exec"); - let s = out.to_string_box().value; assert_eq!(s, "1"); + let s = out.to_string_box().value; + assert_eq!(s, "1"); // STRICT unknown method on MapBox should error std::env::set_var("NYASH_ABI_STRICT", "1"); - let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Void, effects: EffectMask::PURE }; + let sig2 = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Void, + effects: EffectMask::PURE, + }; let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0)); let bb2 = f2.entry_block; let m2 = f2.next_value_id(); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: m2, box_type: "MapBox".into(), args: vec![] }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: m2, + box_type: "MapBox".into(), + args: vec![], + }); // Call unknown method - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m2, method: "unknown".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: None }); - let mut mm = MirModule::new("t2".into()); mm.add_function(f2); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: None, + box_val: m2, + method: "unknown".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Return { value: None }); + let mut mm = MirModule::new("t2".into()); + mm.add_function(f2); let mut vm2 = VM::new(); let res = vm2.execute_module(&mm); assert!(res.is_err(), "STRICT should error on unknown vtable method"); } - diff --git a/src/tests/vtable_string.rs b/src/tests/vtable_string.rs index a4d0ebeb..ca623b34 100644 --- a/src/tests/vtable_string.rs +++ b/src/tests/vtable_string.rs @@ -1,38 +1,120 @@ #[test] fn vtable_string_substring_concat() { use crate::backend::vm::VM; - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType}; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, + }; std::env::set_var("NYASH_ABI_VTABLE", "1"); // substring: "hello".substring(1,4) == "ell" - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::String, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; - let s = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: s, value: ConstValue::String("hello".into()) }); - let sb = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: sb, box_type: "StringBox".into(), args: vec![s] }); - let i1 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: i1, value: ConstValue::Integer(1) }); - let i4 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: i4, value: ConstValue::Integer(4) }); + let s = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: s, + value: ConstValue::String("hello".into()), + }); + let sb = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: sb, + box_type: "StringBox".into(), + args: vec![s], + }); + let i1 = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: i1, + value: ConstValue::Integer(1), + }); + let i4 = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: i4, + value: ConstValue::Integer(4), + }); let sub = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(sub), box_val: sb, method: "substring".into(), args: vec![i1, i4], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(sub) }); - let mut m = MirModule::new("str_sub".into()); m.add_function(f); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(sub), + box_val: sb, + method: "substring".into(), + args: vec![i1, i4], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(sub) }); + let mut m = MirModule::new("str_sub".into()); + m.add_function(f); let mut vm = VM::new(); let out = vm.execute_module(&m).expect("vm exec"); assert_eq!(out.to_string_box().value, "ell"); // concat: "ab".concat("cd") == "abcd" - let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE }; + let sig2 = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::String, + effects: EffectMask::PURE, + }; let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0)); let bb2 = f2.entry_block; - let a = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: a, value: ConstValue::String("ab".into()) }); - let ab = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: ab, box_type: "StringBox".into(), args: vec![a] }); - let c = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: c, value: ConstValue::String("cd".into()) }); + let a = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: a, + value: ConstValue::String("ab".into()), + }); + let ab = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: ab, + box_type: "StringBox".into(), + args: vec![a], + }); + let c = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: c, + value: ConstValue::String("cd".into()), + }); let joined = f2.next_value_id(); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(joined), box_val: ab, method: "concat".into(), args: vec![c], method_id: None, effects: EffectMask::PURE }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: Some(joined) }); - let mut m2 = MirModule::new("str_concat".into()); m2.add_function(f2); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(joined), + box_val: ab, + method: "concat".into(), + args: vec![c], + method_id: None, + effects: EffectMask::PURE, + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Return { + value: Some(joined), + }); + let mut m2 = MirModule::new("str_concat".into()); + m2.add_function(f2); let mut vm2 = VM::new(); let out2 = vm2.execute_module(&m2).expect("vm exec"); assert_eq!(out2.to_string_box().value, "abcd"); } - diff --git a/src/tests/vtable_string_p1.rs b/src/tests/vtable_string_p1.rs index 2a4b9179..3354738d 100644 --- a/src/tests/vtable_string_p1.rs +++ b/src/tests/vtable_string_p1.rs @@ -1,53 +1,184 @@ #[test] fn vtable_string_indexof_replace_trim_upper_lower() { use crate::backend::vm::VM; - use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType}; + use crate::mir::{ + BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, + MirModule, MirType, + }; std::env::set_var("NYASH_ABI_VTABLE", "1"); // indexOf("b") in "abc" == 1 - let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE }; + let sig = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::Integer, + effects: EffectMask::PURE, + }; let mut f = MirFunction::new(sig, BasicBlockId::new(0)); let bb = f.entry_block; - let s = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: s, value: ConstValue::String("abc".into()) }); - let sb = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: sb, box_type: "StringBox".into(), args: vec![s] }); - let b = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: b, value: ConstValue::String("b".into()) }); + let s = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: s, + value: ConstValue::String("abc".into()), + }); + let sb = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: sb, + box_type: "StringBox".into(), + args: vec![s], + }); + let b = f.next_value_id(); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: b, + value: ConstValue::String("b".into()), + }); let idx = f.next_value_id(); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(idx), box_val: sb, method: "indexOf".into(), args: vec![b], method_id: None, effects: EffectMask::PURE }); - f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(idx) }); - let mut m = MirModule::new("str_indexof".into()); m.add_function(f); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(idx), + box_val: sb, + method: "indexOf".into(), + args: vec![b], + method_id: None, + effects: EffectMask::PURE, + }); + f.get_block_mut(bb) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(idx) }); + let mut m = MirModule::new("str_indexof".into()); + m.add_function(f); let mut vm = VM::new(); let out = vm.execute_module(&m).expect("vm exec"); assert_eq!(out.to_string_box().value, "1"); // replace: "a-b" -> replace("-","+") == "a+b" - let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE }; + let sig2 = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::String, + effects: EffectMask::PURE, + }; let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0)); let bb2 = f2.entry_block; - let s2 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: s2, value: ConstValue::String("a-b".into()) }); - let sb2 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: sb2, box_type: "StringBox".into(), args: vec![s2] }); - let dash = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: dash, value: ConstValue::String("-".into()) }); - let plus = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: plus, value: ConstValue::String("+".into()) }); + let s2 = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: s2, + value: ConstValue::String("a-b".into()), + }); + let sb2 = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: sb2, + box_type: "StringBox".into(), + args: vec![s2], + }); + let dash = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: dash, + value: ConstValue::String("-".into()), + }); + let plus = f2.next_value_id(); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: plus, + value: ConstValue::String("+".into()), + }); let rep = f2.next_value_id(); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(rep), box_val: sb2, method: "replace".into(), args: vec![dash, plus], method_id: None, effects: EffectMask::PURE }); - f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: Some(rep) }); - let mut m2 = MirModule::new("str_replace".into()); m2.add_function(f2); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(rep), + box_val: sb2, + method: "replace".into(), + args: vec![dash, plus], + method_id: None, + effects: EffectMask::PURE, + }); + f2.get_block_mut(bb2) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(rep) }); + let mut m2 = MirModule::new("str_replace".into()); + m2.add_function(f2); let mut vm2 = VM::new(); let out2 = vm2.execute_module(&m2).expect("vm exec"); assert_eq!(out2.to_string_box().value, "a+b"); // trim + toUpper + toLower: " Xy " -> trim=="Xy" -> upper=="XY" -> lower=="xy" - let sig3 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE }; + let sig3 = FunctionSignature { + name: "main".into(), + params: vec![], + return_type: MirType::String, + effects: EffectMask::PURE, + }; let mut f3 = MirFunction::new(sig3, BasicBlockId::new(0)); let bb3 = f3.entry_block; - let s3 = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: s3, value: ConstValue::String(" Xy ".into()) }); - let sb3 = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::NewBox { dst: sb3, box_type: "StringBox".into(), args: vec![s3] }); - let t = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(t), box_val: sb3, method: "trim".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - let u = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(u), box_val: t, method: "toUpper".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - let l = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(l), box_val: u, method: "toLower".into(), args: vec![], method_id: None, effects: EffectMask::PURE }); - f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Return { value: Some(l) }); - let mut m3 = MirModule::new("str_trim_upper_lower".into()); m3.add_function(f3); + let s3 = f3.next_value_id(); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Const { + dst: s3, + value: ConstValue::String(" Xy ".into()), + }); + let sb3 = f3.next_value_id(); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::NewBox { + dst: sb3, + box_type: "StringBox".into(), + args: vec![s3], + }); + let t = f3.next_value_id(); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(t), + box_val: sb3, + method: "trim".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + let u = f3.next_value_id(); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(u), + box_val: t, + method: "toUpper".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + let l = f3.next_value_id(); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::BoxCall { + dst: Some(l), + box_val: u, + method: "toLower".into(), + args: vec![], + method_id: None, + effects: EffectMask::PURE, + }); + f3.get_block_mut(bb3) + .unwrap() + .add_instruction(MirInstruction::Return { value: Some(l) }); + let mut m3 = MirModule::new("str_trim_upper_lower".into()); + m3.add_function(f3); let mut vm3 = VM::new(); let out3 = vm3.execute_module(&m3).expect("vm exec"); assert_eq!(out3.to_string_box().value, "xy"); } - diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 09ba1c8d..90f4268b 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1,12 +1,12 @@ /*! * Nyash Tokenizer - .nyashソースコードをトークン列に変換 - * + * * Python版nyashc_v4.pyのNyashTokenizerをRustで完全再実装 * 正規表現ベース → 高速なcharレベル処理に最適化 */ -use thiserror::Error; use crate::grammar::engine; +use thiserror::Error; /// トークンの種類を表すenum #[derive(Debug, Clone, PartialEq)] @@ -14,11 +14,11 @@ pub enum TokenType { // リテラル STRING(String), NUMBER(i64), - FLOAT(f64), // 浮動小数点数サポート追加 + FLOAT(f64), // 浮動小数点数サポート追加 TRUE, FALSE, - NULL, // null リテラル - + NULL, // null リテラル + // キーワード BOX, GLOBAL, @@ -36,75 +36,75 @@ pub enum TokenType { PRINT, THIS, ME, - INIT, // init (初期化ブロック) - PACK, // pack (コンストラクタ - 互換性) - BIRTH, // birth (コンストラクタ) - NOWAIT, // nowait - AWAIT, // await - INTERFACE, // interface - COLON, // : (継承用) - INCLUDE, // include (ファイル読み込み) - TRY, // try - CATCH, // catch - FINALLY, // finally - THROW, // throw - LOCAL, // local (一時変数宣言) - STATIC, // static (静的メソッド) - OUTBOX, // outbox (所有権移転変数) - NOT, // not (否定演算子) - OVERRIDE, // override (明示的オーバーライド) - FROM, // from (親メソッド呼び出し) - WEAK, // weak (弱参照修飾子) - USING, // using (名前空間インポート) - IMPORT, // import (Phase 12.7) - + INIT, // init (初期化ブロック) + PACK, // pack (コンストラクタ - 互換性) + BIRTH, // birth (コンストラクタ) + NOWAIT, // nowait + AWAIT, // await + INTERFACE, // interface + COLON, // : (継承用) + INCLUDE, // include (ファイル読み込み) + TRY, // try + CATCH, // catch + FINALLY, // finally + THROW, // throw + LOCAL, // local (一時変数宣言) + STATIC, // static (静的メソッド) + OUTBOX, // outbox (所有権移転変数) + NOT, // not (否定演算子) + OVERRIDE, // override (明示的オーバーライド) + FROM, // from (親メソッド呼び出し) + WEAK, // weak (弱参照修飾子) + USING, // using (名前空間インポート) + IMPORT, // import (Phase 12.7) + // 演算子 (長いものから先に定義) - ShiftLeft, // << (bitwise shift-left) - ShiftRight, // >> (bitwise shift-right) - BitAnd, // & (bitwise and) - BitOr, // | (bitwise or) - BitXor, // ^ (bitwise xor) - FatArrow, // => (peek arms) - EQUALS, // == - NotEquals, // != - LessEquals, // <= - GreaterEquals, // >= - AND, // && または and - OR, // || または or + ShiftLeft, // << (bitwise shift-left) + ShiftRight, // >> (bitwise shift-right) + BitAnd, // & (bitwise and) + BitOr, // | (bitwise or) + BitXor, // ^ (bitwise xor) + FatArrow, // => (peek arms) + EQUALS, // == + NotEquals, // != + LessEquals, // <= + GreaterEquals, // >= + AND, // && または and + OR, // || または or // Phase 12.7-B 基本糖衣: 2文字演算子(最長一致優先) - PipeForward, // |> - QmarkDot, // ?. - QmarkQmark, // ?? - PlusAssign, // += - MinusAssign, // -= - MulAssign, // *= - DivAssign, // /= - RANGE, // .. - LESS, // < - GREATER, // > - ASSIGN, // = - PLUS, // + - MINUS, // - - MULTIPLY, // * - DIVIDE, // / - MODULO, // % - + PipeForward, // |> + QmarkDot, // ?. + QmarkQmark, // ?? + PlusAssign, // += + MinusAssign, // -= + MulAssign, // *= + DivAssign, // /= + RANGE, // .. + LESS, // < + GREATER, // > + ASSIGN, // = + PLUS, // + + MINUS, // - + MULTIPLY, // * + DIVIDE, // / + MODULO, // % + // 記号 - DOT, // . - DoubleColon, // :: (Parent::method) - P1用(定義のみ) - LPAREN, // ( - RPAREN, // ) - LBRACK, // [ - RBRACK, // ] - LBRACE, // { - RBRACE, // } - COMMA, // , - QUESTION, // ? (postfix result propagation) - NEWLINE, // \n - + DOT, // . + DoubleColon, // :: (Parent::method) - P1用(定義のみ) + LPAREN, // ( + RPAREN, // ) + LBRACK, // [ + RBRACK, // ] + LBRACE, // { + RBRACE, // } + COMMA, // , + QUESTION, // ? (postfix result propagation) + NEWLINE, // \n + // 識別子 IDENTIFIER(String), - + // 特殊 EOF, } @@ -119,7 +119,11 @@ pub struct Token { impl Token { pub fn new(token_type: TokenType, line: usize, column: usize) -> Self { - Self { token_type, line, column } + Self { + token_type, + line, + column, + } } } @@ -127,14 +131,18 @@ impl Token { #[derive(Error, Debug)] pub enum TokenizeError { #[error("Unexpected character '{char}' at line {line}, column {column}")] - UnexpectedCharacter { char: char, line: usize, column: usize }, - + UnexpectedCharacter { + char: char, + line: usize, + column: usize, + }, + #[error("Unterminated string literal at line {line}")] UnterminatedString { line: usize }, - + #[error("Invalid number format at line {line}")] InvalidNumber { line: usize }, - + #[error("Comment not closed at line {line}")] UnterminatedComment { line: usize }, } @@ -162,11 +170,11 @@ impl NyashTokenizer { column: 1, } } - + /// 完全なトークナイズを実行 pub fn tokenize(&mut self) -> Result, TokenizeError> { let mut tokens = Vec::new(); - + while !self.is_at_end() { // 空白・コメントをスキップ self.skip_whitespace(); @@ -179,34 +187,36 @@ impl NyashTokenizer { continue; } // line comments: // ... or # ... - if (self.current_char() == Some('/') && self.peek_char() == Some('/')) || self.current_char() == Some('#') { + if (self.current_char() == Some('/') && self.peek_char() == Some('/')) + || self.current_char() == Some('#') + { self.skip_line_comment(); self.skip_whitespace(); continue; } break; } - + if self.is_at_end() { break; } - + // 次のトークンを読み取り let token = self.tokenize_next()?; tokens.push(token); } - + // EOF トークンを追加 tokens.push(Token::new(TokenType::EOF, self.line, self.column)); - + Ok(tokens) } - + /// 次の一つのトークンを読み取り fn tokenize_next(&mut self) -> Result { let start_line = self.line; let start_column = self.column; - + match self.current_char() { // Block comment should have been skipped by tokenize() pre-loop, but be defensive here Some('/') if self.peek_char() == Some('*') => { @@ -261,7 +271,11 @@ impl NyashTokenizer { } Some('"') => { let string_value = self.read_string()?; - Ok(Token::new(TokenType::STRING(string_value), start_line, start_column)) + Ok(Token::new( + TokenType::STRING(string_value), + start_line, + start_column, + )) } Some(c) if c.is_ascii_digit() => { let token_type = self.read_numeric_literal()?; @@ -278,7 +292,7 @@ impl NyashTokenizer { } Some('#') => { self.skip_line_comment(); - self.skip_whitespace(); // コメント後の空白もスキップ + self.skip_whitespace(); // コメント後の空白もスキップ return self.tokenize_next(); } Some('>') if self.peek_char() == Some('>') && !Self::strict_12_7() => { @@ -324,7 +338,11 @@ impl NyashTokenizer { Some('>') if self.peek_char() == Some('=') => { self.advance(); self.advance(); - Ok(Token::new(TokenType::GreaterEquals, start_line, start_column)) + Ok(Token::new( + TokenType::GreaterEquals, + start_line, + start_column, + )) } Some('&') if self.peek_char() == Some('&') => { self.advance(); @@ -422,32 +440,28 @@ impl NyashTokenizer { self.advance(); Ok(Token::new(TokenType::NEWLINE, start_line, start_column)) } - Some(c) => { - Err(TokenizeError::UnexpectedCharacter { - char: c, - line: self.line, - column: self.column, - }) - } - None => { - Ok(Token::new(TokenType::EOF, self.line, self.column)) - } + Some(c) => Err(TokenizeError::UnexpectedCharacter { + char: c, + line: self.line, + column: self.column, + }), + None => Ok(Token::new(TokenType::EOF, self.line, self.column)), } } - + /// 文字列リテラルを読み取り fn read_string(&mut self) -> Result { let start_line = self.line; self.advance(); // 開始の '"' をスキップ - + let mut string_value = String::new(); - + while let Some(c) = self.current_char() { if c == '"' { self.advance(); // 終了の '"' をスキップ return Ok(string_value); } - + // エスケープ文字の処理 if c == '\\' { self.advance(); @@ -466,25 +480,28 @@ impl NyashTokenizer { } else { string_value.push(c); } - + self.advance(); } - + Err(TokenizeError::UnterminatedString { line: start_line }) } - + /// 数値リテラル(整数または浮動小数点数)を読み取り fn read_numeric_literal(&mut self) -> Result { let start_line = self.line; let mut number_str = String::new(); let mut has_dot = false; - + // 整数部分を読み取り while let Some(c) = self.current_char() { if c.is_ascii_digit() { number_str.push(c); self.advance(); - } else if c == '.' && !has_dot && self.peek_char().map_or(false, |ch| ch.is_ascii_digit()) { + } else if c == '.' + && !has_dot + && self.peek_char().map_or(false, |ch| ch.is_ascii_digit()) + { // 小数点の後に数字が続く場合のみ受け入れる has_dot = true; number_str.push(c); @@ -493,24 +510,26 @@ impl NyashTokenizer { break; } } - + if has_dot { // 浮動小数点数として解析 - number_str.parse::() + number_str + .parse::() .map(TokenType::FLOAT) .map_err(|_| TokenizeError::InvalidNumber { line: start_line }) } else { // 整数として解析 - number_str.parse::() + number_str + .parse::() .map(TokenType::NUMBER) .map_err(|_| TokenizeError::InvalidNumber { line: start_line }) } } - + /// キーワードまたは識別子を読み取り fn read_keyword_or_identifier(&mut self) -> TokenType { let mut identifier = String::new(); - + while let Some(c) = self.current_char() { if c.is_alphanumeric() || c == '_' { identifier.push(c); @@ -519,8 +538,8 @@ impl NyashTokenizer { break; } } - - // キーワードチェック + + // キーワードチェック let mut tok = match identifier.as_str() { "box" => TokenType::BOX, "global" => TokenType::GLOBAL, @@ -568,15 +587,16 @@ impl NyashTokenizer { // 12.7 Strict mode: fallback extended keywords to IDENTIFIER if Self::strict_12_7() { - let is_extended = matches!(tok, + let is_extended = matches!( + tok, TokenType::INTERFACE - | TokenType::USING - | TokenType::INCLUDE - | TokenType::OUTBOX - | TokenType::NOWAIT - | TokenType::OVERRIDE - | TokenType::WEAK - | TokenType::PACK + | TokenType::USING + | TokenType::INCLUDE + | TokenType::OUTBOX + | TokenType::NOWAIT + | TokenType::OVERRIDE + | TokenType::WEAK + | TokenType::PACK ); if is_extended { tok = TokenType::IDENTIFIER(identifier.clone()); @@ -589,12 +609,18 @@ impl NyashTokenizer { let kw = engine::get().is_keyword_str(&identifier); match (&tok, kw) { (TokenType::IDENTIFIER(_), Some(name)) => { - eprintln!("[GRAMMAR-DIFF] tokenizer=IDENT, grammar=KEYWORD({}) word='{}'", name, identifier); + eprintln!( + "[GRAMMAR-DIFF] tokenizer=IDENT, grammar=KEYWORD({}) word='{}'", + name, identifier + ); } (TokenType::IDENTIFIER(_), None) => {} // tokenizerがキーワード、grammarが未定義 (t, None) if !matches!(t, TokenType::IDENTIFIER(_)) => { - eprintln!("[GRAMMAR-DIFF] tokenizer=KEYWORD, grammar=IDENT word='{}'", identifier); + eprintln!( + "[GRAMMAR-DIFF] tokenizer=KEYWORD, grammar=IDENT word='{}'", + identifier + ); } _ => {} } @@ -602,7 +628,7 @@ impl NyashTokenizer { tok } - + /// 行コメントをスキップ fn skip_line_comment(&mut self) { while let Some(c) = self.current_char() { @@ -612,7 +638,7 @@ impl NyashTokenizer { self.advance(); } } - + /// ブロックコメントをスキップ: /* ... */(ネスト非対応) fn skip_block_comment(&mut self) -> Result<(), TokenizeError> { // Assume current position is at '/' and next is '*' @@ -630,7 +656,7 @@ impl NyashTokenizer { // EOF reached without closing */ Err(TokenizeError::UnterminatedComment { line: self.line }) } - + /// 空白文字をスキップ(改行は除く:改行はNEWLINEトークンとして扱う) fn skip_whitespace(&mut self) { while let Some(c) = self.current_char() { @@ -641,17 +667,17 @@ impl NyashTokenizer { } } } - + /// 現在の文字を取得 fn current_char(&self) -> Option { self.input.get(self.position).copied() } - + /// 次の文字を先読み fn peek_char(&self) -> Option { self.input.get(self.position + 1).copied() } - + /// 位置を1つ進める fn advance(&mut self) { if let Some(c) = self.current_char() { @@ -664,7 +690,7 @@ impl NyashTokenizer { self.position += 1; } } - + /// 入力の終端に達したかチェック fn is_at_end(&self) -> bool { self.position >= self.input.len() @@ -676,12 +702,12 @@ impl NyashTokenizer { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_simple_tokens() { let mut tokenizer = NyashTokenizer::new("box new = + - *"); let tokens = tokenizer.tokenize().unwrap(); - + assert_eq!(tokens.len(), 7); // 6 tokens + EOF assert_eq!(tokens[0].token_type, TokenType::BOX); assert_eq!(tokens[1].token_type, TokenType::NEW); @@ -691,24 +717,24 @@ mod tests { assert_eq!(tokens[5].token_type, TokenType::MULTIPLY); assert_eq!(tokens[6].token_type, TokenType::EOF); } - + #[test] fn test_string_literal() { let mut tokenizer = NyashTokenizer::new(r#""Hello, World!""#); let tokens = tokenizer.tokenize().unwrap(); - + assert_eq!(tokens.len(), 2); // STRING + EOF match &tokens[0].token_type { TokenType::STRING(s) => assert_eq!(s, "Hello, World!"), _ => panic!("Expected STRING token"), } } - + #[test] fn test_number_literal() { let mut tokenizer = NyashTokenizer::new("42 123 0"); let tokens = tokenizer.tokenize().unwrap(); - + assert_eq!(tokens.len(), 4); // 3 numbers + EOF match &tokens[0].token_type { TokenType::NUMBER(n) => assert_eq!(*n, 42), @@ -723,12 +749,12 @@ mod tests { _ => panic!("Expected NUMBER token"), } } - + #[test] fn test_identifier() { let mut tokenizer = NyashTokenizer::new("test_var myBox getValue"); let tokens = tokenizer.tokenize().unwrap(); - + assert_eq!(tokens.len(), 4); // 3 identifiers + EOF match &tokens[0].token_type { TokenType::IDENTIFIER(s) => assert_eq!(s, "test_var"), @@ -743,12 +769,12 @@ mod tests { _ => panic!("Expected IDENTIFIER token"), } } - + #[test] fn test_operators() { let mut tokenizer = NyashTokenizer::new(">> == != <= >= < >"); let tokens = tokenizer.tokenize().unwrap(); - + assert_eq!(tokens[0].token_type, TokenType::ShiftRight); assert_eq!(tokens[1].token_type, TokenType::EQUALS); assert_eq!(tokens[2].token_type, TokenType::NotEquals); @@ -757,7 +783,7 @@ mod tests { assert_eq!(tokens[5].token_type, TokenType::LESS); assert_eq!(tokens[6].token_type, TokenType::GREATER); } - + #[test] fn test_complex_code() { let code = r#" @@ -772,10 +798,10 @@ mod tests { obj = new TestBox() obj.value = "test123" "#; - + let mut tokenizer = NyashTokenizer::new(code); let tokens = tokenizer.tokenize().unwrap(); - + // 基本的なトークンがある事を確認 let token_types: Vec<_> = tokens.iter().map(|t| &t.token_type).collect(); assert!(token_types.contains(&&TokenType::BOX)); @@ -784,7 +810,7 @@ mod tests { assert!(token_types.contains(&&TokenType::RETURN)); assert!(token_types.contains(&&TokenType::DOT)); } - + #[test] fn test_line_numbers() { let code = "box\ntest\nvalue"; @@ -792,34 +818,38 @@ mod tests { let tokens = tokenizer.tokenize().unwrap(); // NEWLINEトークンを除外して確認 - let non_newline: Vec<&Token> = tokens.iter().filter(|t| !matches!(t.token_type, TokenType::NEWLINE)).collect(); + let non_newline: Vec<&Token> = tokens + .iter() + .filter(|t| !matches!(t.token_type, TokenType::NEWLINE)) + .collect(); assert_eq!(non_newline[0].line, 1); // box assert_eq!(non_newline[1].line, 2); // test assert_eq!(non_newline[2].line, 3); // value } - + #[test] fn test_comments() { let code = r#"box Test // this is a comment # this is also a comment value"#; - + let mut tokenizer = NyashTokenizer::new(code); let tokens = tokenizer.tokenize().unwrap(); - + // コメントは除外されている - let token_types: Vec<_> = tokens.iter() + let token_types: Vec<_> = tokens + .iter() .filter(|t| !matches!(t.token_type, TokenType::NEWLINE)) .map(|t| &t.token_type) .collect(); assert_eq!(token_types.len(), 4); // box, Test, value, EOF } - + #[test] fn test_error_handling() { let mut tokenizer = NyashTokenizer::new("@#$%"); let result = tokenizer.tokenize(); - + assert!(result.is_err()); match result { Err(TokenizeError::UnexpectedCharacter { char, line, column }) => { @@ -838,14 +868,30 @@ value"#; // 分かりやすく固めたケース let mut t2 = NyashTokenizer::new("|> ?.? ?? += -= *= /= .."); let toks = t2.tokenize().unwrap(); - assert!(toks.iter().any(|k| matches!(k.token_type, TokenType::PipeForward))); - assert!(toks.iter().any(|k| matches!(k.token_type, TokenType::QmarkDot))); - assert!(toks.iter().any(|k| matches!(k.token_type, TokenType::QmarkQmark))); - assert!(toks.iter().any(|k| matches!(k.token_type, TokenType::PlusAssign))); - assert!(toks.iter().any(|k| matches!(k.token_type, TokenType::MinusAssign))); - assert!(toks.iter().any(|k| matches!(k.token_type, TokenType::MulAssign))); - assert!(toks.iter().any(|k| matches!(k.token_type, TokenType::DivAssign))); - assert!(toks.iter().any(|k| matches!(k.token_type, TokenType::RANGE))); + assert!(toks + .iter() + .any(|k| matches!(k.token_type, TokenType::PipeForward))); + assert!(toks + .iter() + .any(|k| matches!(k.token_type, TokenType::QmarkDot))); + assert!(toks + .iter() + .any(|k| matches!(k.token_type, TokenType::QmarkQmark))); + assert!(toks + .iter() + .any(|k| matches!(k.token_type, TokenType::PlusAssign))); + assert!(toks + .iter() + .any(|k| matches!(k.token_type, TokenType::MinusAssign))); + assert!(toks + .iter() + .any(|k| matches!(k.token_type, TokenType::MulAssign))); + assert!(toks + .iter() + .any(|k| matches!(k.token_type, TokenType::DivAssign))); + assert!(toks + .iter() + .any(|k| matches!(k.token_type, TokenType::RANGE))); } #[test] diff --git a/src/transport/inprocess.rs b/src/transport/inprocess.rs index 4e3713b7..75f0413e 100644 --- a/src/transport/inprocess.rs +++ b/src/transport/inprocess.rs @@ -1,9 +1,9 @@ /*! 🏠 InProcessTransport - Local Process Communication - * + * * ## 📝 概要 * InProcessTransportは、同一プロセス内でのP2P通信を実装します。 * MessageBusを使用して高速なローカルメッセージ配送を行います。 - * + * * ## 🏗️ 設計 * - **MessageBus Integration**: グローバルMessageBusを使用 * - **Zero-Copy**: プロセス内での直接参照渡し @@ -11,9 +11,9 @@ * - **Thread-Safe**: 並行アクセス対応 */ -use super::{Transport, IntentEnvelope, SendOpts, TransportError}; -use crate::messaging::{MessageBus, MessageBusData, BusEndpoint, SendError, IntentHandler}; +use super::{IntentEnvelope, SendOpts, Transport, TransportError}; use crate::boxes::IntentBox; +use crate::messaging::{BusEndpoint, IntentHandler, MessageBus, MessageBusData, SendError}; use std::sync::{Arc, Mutex}; /// InProcessTransport - プロセス内通信実装 @@ -40,13 +40,13 @@ impl InProcessTransport { pub fn new(node_id: String) -> Self { let bus = MessageBusData::global(); let endpoint = BusEndpoint::new(node_id.clone()); - + // ノードをバスに登録 { let mut bus_data = bus.lock().unwrap(); bus_data.register_node(node_id.clone(), endpoint.clone()); } - + InProcessTransport { node_id, bus, @@ -54,7 +54,7 @@ impl InProcessTransport { receive_callback: Arc::new(Mutex::new(None)), } } - + /// イベントハンドラーを追加 pub fn add_handler(&self, intent_name: &str, handler: IntentHandler) { self.endpoint.add_handler(intent_name, handler); @@ -65,10 +65,10 @@ impl Transport for InProcessTransport { fn node_id(&self) -> &str { &self.node_id } - + fn send(&self, to: &str, intent: IntentBox, _opts: SendOpts) -> Result<(), TransportError> { let bus = self.bus.lock().unwrap(); - + match bus.route(to, intent.clone(), &self.node_id) { Ok(_) => { // 受信コールバックがある場合は実行 @@ -89,38 +89,48 @@ impl Transport for InProcessTransport { Err(SendError::BusError(msg)) => Err(TransportError::NetworkError(msg)), } } - + fn on_receive(&mut self, callback: Box) { let mut receive_callback = self.receive_callback.lock().unwrap(); *receive_callback = Some(callback); } - + fn is_reachable(&self, node_id: &str) -> bool { let bus = self.bus.lock().unwrap(); bus.node_exists(node_id) } - + fn transport_type(&self) -> &'static str { "inprocess" } - fn register_intent_handler(&mut self, intent: &str, cb: Box) { + fn register_intent_handler( + &mut self, + intent: &str, + cb: Box, + ) { let intent_name = intent.to_string(); let cb = std::sync::Arc::new(cb); let cb_clone = cb.clone(); // Adapt to MessageBus handler signature if std::env::var("NYASH_DEBUG_P2P").unwrap_or_default() == "1" { - eprintln!("[InProcessTransport] register handler node={} intent={}", self.node_id, intent_name); + eprintln!( + "[InProcessTransport] register handler node={} intent={}", + self.node_id, intent_name + ); } - self.add_handler(&intent_name, Box::new(move |intent_box: IntentBox, from: &str| { - let env = IntentEnvelope { - from: from.to_string(), - to: String::new(), // not tracked at this layer for handler - intent: intent_box, - timestamp: std::time::Instant::now(), - }; - (cb_clone)(env); - })); + self.add_handler( + &intent_name, + Box::new(move |intent_box: IntentBox, from: &str| { + let env = IntentEnvelope { + from: from.to_string(), + to: String::new(), // not tracked at this layer for handler + intent: intent_box, + timestamp: std::time::Instant::now(), + }; + (cb_clone)(env); + }), + ); } fn debug_list_nodes(&self) -> Option> { @@ -142,9 +152,7 @@ impl Drop for InProcessTransport { if std::env::var("NYASH_DEBUG_P2P").unwrap_or_default() == "1" { eprintln!( "[InProcessTransport::drop] node_id={} removed={} (bus={:?})", - self.node_id, - removed, - &*bus + self.node_id, removed, &*bus ); } } diff --git a/src/transport/mod.rs b/src/transport/mod.rs index ecc8dcb1..756f7a80 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -1,5 +1,5 @@ /*! 🚀 Transport Module - Communication Layer Abstraction - * + * * This module defines the Transport trait and implementations for different * communication methods (InProcess, WebSocket, WebRTC, etc.) */ @@ -37,29 +37,48 @@ pub enum TransportError { pub trait Transport: Send + Sync + std::fmt::Debug { /// Get the node ID of this transport fn node_id(&self) -> &str; - + /// Send a message to a specific node fn send(&self, to: &str, intent: IntentBox, opts: SendOpts) -> Result<(), TransportError>; - + /// Register a callback for receiving messages fn on_receive(&mut self, callback: Box); - + /// Check if a node is reachable fn is_reachable(&self, node_id: &str) -> bool; - + /// Get transport type identifier fn transport_type(&self) -> &'static str; /// Downcast support for dynamic transports - fn as_any(&self) -> &dyn std::any::Any where Self: 'static + Sized { self } - fn as_any_mut(&mut self) -> &mut dyn std::any::Any where Self: 'static + Sized { self } + fn as_any(&self) -> &dyn std::any::Any + where + Self: 'static + Sized, + { + self + } + fn as_any_mut(&mut self) -> &mut dyn std::any::Any + where + Self: 'static + Sized, + { + self + } /// Register an intent handler (default: no-op) - fn register_intent_handler(&mut self, _intent: &str, _cb: Box) { } + fn register_intent_handler( + &mut self, + _intent: &str, + _cb: Box, + ) { + } /// Debug helper: enumerate known nodes (if supported) - fn debug_list_nodes(&self) -> Option> { None } - fn debug_bus_id(&self) -> Option { None } + fn debug_list_nodes(&self) -> Option> { + None + } + fn debug_bus_id(&self) -> Option { + None + } } pub use inprocess::InProcessTransport; diff --git a/src/type_box.rs b/src/type_box.rs index df9aafe1..5425ce86 100644 --- a/src/type_box.rs +++ b/src/type_box.rs @@ -1,15 +1,15 @@ -/*! +/*! * TypeBox - Everything is Box極限実現 - * + * * 型情報もBoxとして表現し、実行時型チェック、メタプログラミング、 * ジェネリクス基盤を提供する革命的システム */ -use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; -use std::collections::HashMap; -use std::sync::Arc; -use std::fmt::{Debug, Display}; +use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use std::any::Any; +use std::collections::HashMap; +use std::fmt::{Debug, Display}; +use std::sync::Arc; /// メソッドシグニチャ情報 #[derive(Debug, Clone)] @@ -31,12 +31,12 @@ impl MethodSignature { is_static: false, } } - + pub fn with_types( - name: String, - parameters: Vec, - parameter_types: Vec>, - return_type: Arc + name: String, + parameters: Vec, + parameter_types: Vec>, + return_type: Arc, ) -> Self { Self { name, @@ -53,25 +53,25 @@ impl MethodSignature { pub struct TypeBox { /// 型名 pub name: String, - + /// フィールド情報 (field_name -> field_type) pub fields: HashMap>, - + /// メソッドシグニチャ情報 pub methods: HashMap, - + /// 親型(継承) pub parent_type: Option>, - + /// ジェネリクス型パラメータ pub type_parameters: Vec, - + /// インスタンス化された具体型(ジェネリクス用) pub concrete_types: HashMap>, - + /// ビルトイン型かどうか pub is_builtin: bool, - + /// Box基底 base: BoxBase, } @@ -90,96 +90,96 @@ impl TypeBox { base: BoxBase::new(), } } - + /// ビルトイン型を作成 pub fn builtin(name: &str) -> Self { let mut type_box = Self::new(name); type_box.is_builtin = true; type_box } - + /// フィールドを追加 pub fn add_field(&mut self, name: &str, field_type: Arc) { self.fields.insert(name.to_string(), field_type); } - + /// メソッドを追加 pub fn add_method(&mut self, method: MethodSignature) { self.methods.insert(method.name.clone(), method); } - + /// 親型を設定 pub fn set_parent(&mut self, parent: Arc) { self.parent_type = Some(parent); } - + /// 型パラメータを追加 pub fn add_type_parameter(&mut self, param: String) { self.type_parameters.push(param); } - + /// 具体型を設定(ジェネリクス用) pub fn set_concrete_type(&mut self, param: &str, concrete_type: Arc) { self.concrete_types.insert(param.to_string(), concrete_type); } - + /// フィールドの型を取得 pub fn get_field_type(&self, field_name: &str) -> Option> { // 自分のフィールドをチェック if let Some(field_type) = self.fields.get(field_name) { return Some(Arc::clone(field_type)); } - + // 親型のフィールドをチェック(継承) if let Some(parent) = &self.parent_type { return parent.get_field_type(field_name); } - + None } - + /// メソッドシグニチャを取得 pub fn get_method_signature(&self, method_name: &str) -> Option<&MethodSignature> { // 自分のメソッドをチェック if let Some(method) = self.methods.get(method_name) { return Some(method); } - + // 親型のメソッドをチェック(継承) if let Some(parent) = &self.parent_type { return parent.get_method_signature(method_name); } - + None } - + /// 型互換性チェック pub fn is_compatible_with(&self, other: &TypeBox) -> bool { // 同じ型 if self.name == other.name { return true; } - + // 継承チェック if let Some(parent) = &self.parent_type { if parent.is_compatible_with(other) { return true; } } - + false } - + /// ジェネリクス型かどうか pub fn is_generic(&self) -> bool { !self.type_parameters.is_empty() } - + /// 具体化されたジェネリクス型かどうか pub fn is_concrete_generic(&self) -> bool { !self.concrete_types.is_empty() } - + /// 型名を完全表示(ジェネリクス対応) pub fn full_name(&self) -> String { if self.concrete_types.is_empty() { @@ -187,44 +187,47 @@ impl TypeBox { } else { let mut result = self.name.clone(); result.push('<'); - - let concrete_names: Vec = self.type_parameters.iter() + + let concrete_names: Vec = self + .type_parameters + .iter() .map(|param| { - self.concrete_types.get(param) + self.concrete_types + .get(param) .map(|t| t.name.clone()) .unwrap_or_else(|| param.clone()) }) .collect(); - + result.push_str(&concrete_names.join(", ")); result.push('>'); result } } - + /// 基本型の定数 pub fn void_type() -> TypeBox { TypeBox::builtin("Void") } - + pub fn string_type() -> TypeBox { TypeBox::builtin("StringBox") } - + pub fn integer_type() -> TypeBox { TypeBox::builtin("IntegerBox") } - + pub fn bool_type() -> TypeBox { TypeBox::builtin("BoolBox") } - + pub fn array_type() -> TypeBox { let mut type_box = TypeBox::builtin("ArrayBox"); type_box.add_type_parameter("T".to_string()); type_box } - + pub fn method_box_type() -> TypeBox { let mut type_box = TypeBox::builtin("MethodBox"); type_box.add_type_parameter("T".to_string()); @@ -237,7 +240,7 @@ impl NyashBox for TypeBox { fn to_string_box(&self) -> StringBox { StringBox::new(format!("", self.full_name())) } - + fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(other_type) = other.as_any().downcast_ref::() { BoolBox::new(self.name == other_type.name) @@ -245,15 +248,15 @@ impl NyashBox for TypeBox { BoolBox::new(false) } } - + fn type_name(&self) -> &'static str { "TypeBox" } - + fn clone_box(&self) -> Box { Box::new(self.clone()) } - + /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() @@ -272,11 +275,11 @@ impl BoxCore for TypeBox { fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "", self.full_name()) } - + fn as_any(&self) -> &dyn Any { self } - + fn as_any_mut(&mut self) -> &mut dyn Any { self } @@ -293,10 +296,10 @@ impl Display for TypeBox { pub struct TypeRegistry { /// 登録済み型 types: HashMap>, - + /// 継承チェーン情報(高速化用) inheritance_chains: HashMap>, - + /// メソッドキャッシュ(将来の最適化用) #[allow(dead_code)] method_cache: HashMap<(String, String), MethodSignature>, @@ -310,12 +313,12 @@ impl TypeRegistry { inheritance_chains: HashMap::new(), method_cache: HashMap::new(), }; - + // ビルトイン型を登録 registry.register_builtin_types(); registry } - + /// ビルトイン型を登録 fn register_builtin_types(&mut self) { self.register_type(Arc::new(TypeBox::void_type())); @@ -325,11 +328,11 @@ impl TypeRegistry { self.register_type(Arc::new(TypeBox::array_type())); self.register_type(Arc::new(TypeBox::method_box_type())); } - + /// 型を登録 pub fn register_type(&mut self, type_box: Arc) { let name = type_box.name.clone(); - + // 継承チェーンを構築 let mut chain = vec![name.clone()]; let mut current = &type_box.parent_type; @@ -337,67 +340,75 @@ impl TypeRegistry { chain.push(parent.name.clone()); current = &parent.parent_type; } - + self.inheritance_chains.insert(name.clone(), chain); self.types.insert(name, type_box); } - + /// 型を取得 pub fn get_type(&self, name: &str) -> Option> { self.types.get(name).map(Arc::clone) } - + /// 型互換性チェック pub fn is_compatible(&self, from_type: &str, to_type: &str) -> bool { if from_type == to_type { return true; } - + if let Some(chain) = self.inheritance_chains.get(from_type) { chain.contains(&to_type.to_string()) } else { false } } - + /// すべての型名を取得 pub fn get_all_type_names(&self) -> Vec { self.types.keys().cloned().collect() } - + /// ジェネリクス型をインスタンス化 - pub fn instantiate_generic(&mut self, base_type: &str, concrete_types: &[&str]) -> Result, String> { - let base = self.get_type(base_type) + pub fn instantiate_generic( + &mut self, + base_type: &str, + concrete_types: &[&str], + ) -> Result, String> { + let base = self + .get_type(base_type) .ok_or_else(|| format!("Base type '{}' not found", base_type))?; - + if !base.is_generic() { return Err(format!("Type '{}' is not generic", base_type)); } - + if base.type_parameters.len() != concrete_types.len() { return Err(format!( "Generic type '{}' expects {} type parameters, got {}", - base_type, base.type_parameters.len(), concrete_types.len() + base_type, + base.type_parameters.len(), + concrete_types.len() )); } - + // 新しい具体化型を作成 let mut concrete_type = (*base).clone(); concrete_type.name = format!("{}_{}", base_type, concrete_types.join("_")); concrete_type.concrete_types.clear(); - + // 具体型を設定 for (i, param) in base.type_parameters.iter().enumerate() { - let concrete = self.get_type(concrete_types[i]) + let concrete = self + .get_type(concrete_types[i]) .ok_or_else(|| format!("Concrete type '{}' not found", concrete_types[i]))?; concrete_type.set_concrete_type(param, concrete); } - + let result = Arc::new(concrete_type); - + // レジストリに登録 self.register_type(Arc::clone(&result)); - + Ok(result) } } @@ -414,33 +425,33 @@ impl TypeBoxBuilder { type_box: TypeBox::new(name), } } - + /// フィールドを追加 pub fn field(mut self, name: &str, field_type: Arc) -> Self { self.type_box.add_field(name, field_type); self } - + /// メソッドを追加 pub fn method(mut self, method: MethodSignature) -> Self { self.type_box.add_method(method); self } - + /// 親型を設定 pub fn parent(mut self, parent: Arc) -> Self { self.type_box.set_parent(parent); self } - + /// 型パラメータを追加 pub fn type_param(mut self, param: &str) -> Self { self.type_box.add_type_parameter(param.to_string()); self } - + /// TypeBoxを完成 pub fn build(self) -> TypeBox { self.type_box } -} \ No newline at end of file +} diff --git a/src/value.rs b/src/value.rs index 2de2b5e7..365df9c9 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,36 +1,36 @@ /*! * NyashValue - Revolutionary unified value representation system - * + * * Replaces Arc> overuse with direct value storage for primitives * and smart synchronization only where needed. - * + * * Inspired by Lua's TValue system for performance-critical language implementations. */ -use std::sync::{Arc, Mutex, Weak}; -use std::collections::HashMap; -use std::fmt::{self, Display, Debug}; use crate::box_trait::NyashBox; +use std::collections::HashMap; +use std::fmt::{self, Debug, Display}; +use std::sync::{Arc, Mutex, Weak}; /// Revolutionary unified value type - replaces individual Box allocations #[derive(Clone)] pub enum NyashValue { // Direct primitive values - no Arc overhead Integer(i64), - Float(f64), + Float(f64), Bool(bool), String(String), - + // Collections - need synchronization Array(Arc>>), Map(Arc>>), - + // Legacy Box compatibility and custom types Box(Arc>), - + // 🔗 Weak reference system - prevents circular reference memory leaks WeakBox(Weak>), - + // Special values Null, Void, @@ -41,35 +41,35 @@ impl NyashValue { pub fn new_integer(value: i64) -> Self { NyashValue::Integer(value) } - + pub fn new_float(value: f64) -> Self { NyashValue::Float(value) } - + pub fn new_bool(value: bool) -> Self { NyashValue::Bool(value) } - + pub fn new_string(value: String) -> Self { NyashValue::String(value) } - + pub fn new_array() -> Self { NyashValue::Array(Arc::new(Mutex::new(Vec::new()))) } - + pub fn new_map() -> Self { NyashValue::Map(Arc::new(Mutex::new(HashMap::new()))) } - + pub fn new_null() -> Self { NyashValue::Null } - + pub fn new_void() -> Self { NyashValue::Void } - + /// Convert to string representation pub fn to_string(&self) -> String { match self { @@ -79,31 +79,30 @@ impl NyashValue { NyashValue::String(s) => s.clone(), NyashValue::Array(arr) => { if let Ok(guard) = arr.try_lock() { - let elements: Vec = guard.iter() - .map(|v| v.to_string()) - .collect(); + let elements: Vec = guard.iter().map(|v| v.to_string()).collect(); format!("[{}]", elements.join(", ")) } else { "[Array (locked)]".to_string() } - }, + } NyashValue::Map(map) => { if let Ok(guard) = map.try_lock() { - let pairs: Vec = guard.iter() + let pairs: Vec = guard + .iter() .map(|(k, v)| format!("{}: {}", k, v.to_string())) .collect(); format!("{{{}}}", pairs.join(", ")) } else { "{Map (locked)}".to_string() } - }, + } NyashValue::Box(b) => { if let Ok(guard) = b.try_lock() { guard.to_string_box().value } else { "Box (locked)".to_string() } - }, + } NyashValue::WeakBox(weak_ref) => { if let Some(arc) = weak_ref.upgrade() { if let Ok(guard) = arc.try_lock() { @@ -114,40 +113,38 @@ impl NyashValue { } else { "WeakRef(null)".to_string() } - }, + } NyashValue::Null => "null".to_string(), NyashValue::Void => "void".to_string(), } } - + /// Convert to integer (with type coercion) pub fn to_integer(&self) -> Result { match self { NyashValue::Integer(n) => Ok(*n), NyashValue::Float(f) => Ok(*f as i64), NyashValue::Bool(b) => Ok(if *b { 1 } else { 0 }), - NyashValue::String(s) => { - s.parse::() - .map_err(|_| format!("Cannot convert '{}' to integer", s)) - }, + NyashValue::String(s) => s + .parse::() + .map_err(|_| format!("Cannot convert '{}' to integer", s)), _ => Err(format!("Cannot convert {:?} to integer", self.type_name())), } } - + /// Convert to float (with type coercion) pub fn to_float(&self) -> Result { match self { NyashValue::Integer(n) => Ok(*n as f64), NyashValue::Float(f) => Ok(*f), NyashValue::Bool(b) => Ok(if *b { 1.0 } else { 0.0 }), - NyashValue::String(s) => { - s.parse::() - .map_err(|_| format!("Cannot convert '{}' to float", s)) - }, + NyashValue::String(s) => s + .parse::() + .map_err(|_| format!("Cannot convert '{}' to float", s)), _ => Err(format!("Cannot convert {:?} to float", self.type_name())), } } - + /// Convert to boolean (with type coercion) pub fn to_bool(&self) -> Result { match self { @@ -160,16 +157,16 @@ impl NyashValue { NyashValue::WeakBox(weak_ref) => { // WeakBox is truthy if it can be upgraded (still alive) Ok(weak_ref.upgrade().is_some()) - }, + } _ => Ok(true), // Arrays, Maps, Boxes are truthy } } - + /// Get the type name for error messages pub fn type_name(&self) -> &'static str { match self { NyashValue::Integer(_) => "Integer", - NyashValue::Float(_) => "Float", + NyashValue::Float(_) => "Float", NyashValue::Bool(_) => "Bool", NyashValue::String(_) => "String", NyashValue::Array(_) => "Array", @@ -180,47 +177,43 @@ impl NyashValue { NyashValue::Void => "Void", } } - + /// Check if this value is numeric (Integer or Float) pub fn is_numeric(&self) -> bool { matches!(self, NyashValue::Integer(_) | NyashValue::Float(_)) } - + /// Check if this value is falsy pub fn is_falsy(&self) -> bool { - matches!(self, NyashValue::Null | NyashValue::Void) || - self.to_bool().unwrap_or(false) == false + matches!(self, NyashValue::Null | NyashValue::Void) + || self.to_bool().unwrap_or(false) == false } - + /// 🔗 Weak Reference System - Core functionality - + /// Upgrade a weak reference to a strong reference /// Returns None if the referenced object has been dropped pub fn upgrade_weak(&self) -> Option { match self { - NyashValue::WeakBox(weak_ref) => { - weak_ref.upgrade().map(|arc| NyashValue::Box(arc)) - }, + NyashValue::WeakBox(weak_ref) => weak_ref.upgrade().map(|arc| NyashValue::Box(arc)), _ => Some(self.clone()), // Non-weak values return themselves } } - + /// Downgrade a strong reference to a weak reference /// Only works on Box values, others return None pub fn downgrade_to_weak(&self) -> Option { match self { - NyashValue::Box(arc) => { - Some(NyashValue::WeakBox(Arc::downgrade(arc))) - }, + NyashValue::Box(arc) => Some(NyashValue::WeakBox(Arc::downgrade(arc))), _ => None, // Can only create weak refs from strong Box refs } } - + /// Check if this is a weak reference pub fn is_weak_reference(&self) -> bool { matches!(self, NyashValue::WeakBox(_)) } - + /// Check if a weak reference is still valid (not dropped) pub fn is_weak_alive(&self) -> bool { match self { @@ -240,26 +233,26 @@ impl PartialEq for NyashValue { (NyashValue::String(a), NyashValue::String(b)) => a == b, (NyashValue::Null, NyashValue::Null) => true, (NyashValue::Void, NyashValue::Void) => true, - + // Cross-type numeric equality (42 == 42.0) (NyashValue::Integer(a), NyashValue::Float(b)) => (*a as f64 - b).abs() < f64::EPSILON, (NyashValue::Float(a), NyashValue::Integer(b)) => (a - *b as f64).abs() < f64::EPSILON, - + // Arrays and Maps require deep comparison (simplified for now) (NyashValue::Array(a), NyashValue::Array(b)) => Arc::ptr_eq(a, b), (NyashValue::Map(a), NyashValue::Map(b)) => Arc::ptr_eq(a, b), (NyashValue::Box(a), NyashValue::Box(b)) => Arc::ptr_eq(a, b), - + // Weak reference equality (NyashValue::WeakBox(a), NyashValue::WeakBox(b)) => { // Compare if they point to the same object (if both still alive) match (a.upgrade(), b.upgrade()) { (Some(arc_a), Some(arc_b)) => Arc::ptr_eq(&arc_a, &arc_b), - (None, None) => true, // Both dropped, consider equal - _ => false, // One dropped, one alive + (None, None) => true, // Both dropped, consider equal + _ => false, // One dropped, one alive } - }, - + } + // WeakBox vs Box comparison (upgrade weak, then compare) (NyashValue::WeakBox(weak), NyashValue::Box(strong)) => { if let Some(upgraded) = weak.upgrade() { @@ -267,15 +260,15 @@ impl PartialEq for NyashValue { } else { false // Dropped weak ref != any strong ref } - }, + } (NyashValue::Box(strong), NyashValue::WeakBox(weak)) => { if let Some(upgraded) = weak.upgrade() { Arc::ptr_eq(strong, &upgraded) } else { false // Dropped weak ref != any strong ref } - }, - + } + // Everything else is not equal _ => false, } @@ -304,7 +297,7 @@ impl Debug for NyashValue { } else { write!(f, "WeakBox(dropped)") } - }, + } NyashValue::Null => write!(f, "Null"), NyashValue::Void => write!(f, "Void"), } @@ -319,76 +312,64 @@ impl NyashValue { if let Ok(guard) = nyash_box.try_lock() { let type_name = guard.type_name(); let string_rep = guard.to_string_box().value; - + // Convert common types to direct values match type_name { "IntegerBox" => { if let Ok(value) = string_rep.parse::() { return NyashValue::Integer(value); } - }, + } "FloatBox" => { if let Ok(value) = string_rep.parse::() { return NyashValue::Float(value); } - }, + } "BoolBox" => { if let Ok(value) = string_rep.parse::() { return NyashValue::Bool(value); } - }, + } "StringBox" => { return NyashValue::String(string_rep); - }, + } "NullBox" => { return NyashValue::Null; - }, + } "VoidBox" => { return NyashValue::Void; - }, + } _ => {} } } - + // Fallback to Box wrapper NyashValue::Box(nyash_box) } - + /// Convert back to a legacy NyashBox for compatibility pub fn to_box(&self) -> Result>, String> { - use crate::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox}; + use crate::box_trait::{BoolBox, IntegerBox, StringBox, VoidBox}; use crate::boxes::null_box::NullBox; - + match self { - NyashValue::Integer(n) => { - Ok(Arc::new(Mutex::new(IntegerBox::new(*n)))) - }, + NyashValue::Integer(n) => Ok(Arc::new(Mutex::new(IntegerBox::new(*n)))), NyashValue::Float(f) => { // Note: Need FloatBox implementation - for now convert to string Ok(Arc::new(Mutex::new(StringBox::new(f.to_string())))) - }, - NyashValue::Bool(b) => { - Ok(Arc::new(Mutex::new(BoolBox::new(*b)))) - }, - NyashValue::String(s) => { - Ok(Arc::new(Mutex::new(StringBox::new(s.clone())))) - }, - NyashValue::Null => { - Ok(Arc::new(Mutex::new(NullBox::new()))) - }, - NyashValue::Void => { - Ok(Arc::new(Mutex::new(VoidBox::new()))) - }, - NyashValue::Box(b) => { - Ok(b.clone()) - }, + } + NyashValue::Bool(b) => Ok(Arc::new(Mutex::new(BoolBox::new(*b)))), + NyashValue::String(s) => Ok(Arc::new(Mutex::new(StringBox::new(s.clone())))), + NyashValue::Null => Ok(Arc::new(Mutex::new(NullBox::new()))), + NyashValue::Void => Ok(Arc::new(Mutex::new(VoidBox::new()))), + NyashValue::Box(b) => Ok(b.clone()), NyashValue::WeakBox(weak_ref) => { // Try to upgrade weak reference match weak_ref.upgrade() { Some(arc) => Ok(arc), None => Err("Cannot convert dropped weak reference to Box".to_string()), } - }, + } _ => Err(format!("Cannot convert {} to legacy Box", self.type_name())), } } @@ -405,37 +386,31 @@ impl NyashValue { None => String::new(), }; Ok(NyashValue::String(value)) - }, + } "IntegerBox" => { let value = match args.get(0) { Some(arg) => arg.to_integer()?, None => 0, }; Ok(NyashValue::Integer(value)) - }, + } "FloatBox" => { let value = match args.get(0) { Some(arg) => arg.to_float()?, None => 0.0, }; Ok(NyashValue::Float(value)) - }, + } "BoolBox" => { let value = match args.get(0) { Some(arg) => arg.to_bool()?, None => false, }; Ok(NyashValue::Bool(value)) - }, - "ArrayBox" => { - Ok(NyashValue::Array(Arc::new(Mutex::new(Vec::new())))) - }, - "MapBox" => { - Ok(NyashValue::Map(Arc::new(Mutex::new(HashMap::new())))) - }, - _ => { - Err(format!("Unknown object type: {}", type_name)) } + "ArrayBox" => Ok(NyashValue::Array(Arc::new(Mutex::new(Vec::new())))), + "MapBox" => Ok(NyashValue::Map(Arc::new(Mutex::new(HashMap::new())))), + _ => Err(format!("Unknown object type: {}", type_name)), } } } @@ -443,57 +418,58 @@ impl NyashValue { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_basic_creation() { let int_val = NyashValue::new_integer(42); let float_val = NyashValue::new_float(3.14); let bool_val = NyashValue::new_bool(true); let string_val = NyashValue::new_string("hello".to_string()); - + assert_eq!(int_val.to_string(), "42"); assert_eq!(float_val.to_string(), "3.14"); assert_eq!(bool_val.to_string(), "true"); assert_eq!(string_val.to_string(), "hello"); } - + #[test] fn test_type_conversion() { let int_val = NyashValue::new_integer(42); assert_eq!(int_val.to_float().unwrap(), 42.0); assert_eq!(int_val.to_bool().unwrap(), true); - + let float_val = NyashValue::new_float(3.14); assert_eq!(float_val.to_integer().unwrap(), 3); - + let zero_val = NyashValue::new_integer(0); assert_eq!(zero_val.to_bool().unwrap(), false); } - + #[test] fn test_cross_type_equality() { let int_val = NyashValue::new_integer(42); let float_val = NyashValue::new_float(42.0); - + assert_eq!(int_val, float_val); assert_eq!(float_val, int_val); } - + #[test] fn test_object_creation() { - let string_obj = NyashValue::create_object("StringBox", vec![ - NyashValue::new_string("test".to_string()) - ]).unwrap(); - + let string_obj = NyashValue::create_object( + "StringBox", + vec![NyashValue::new_string("test".to_string())], + ) + .unwrap(); + assert_eq!(string_obj.to_string(), "test"); - - let int_obj = NyashValue::create_object("IntegerBox", vec![ - NyashValue::new_integer(100) - ]).unwrap(); - + + let int_obj = + NyashValue::create_object("IntegerBox", vec![NyashValue::new_integer(100)]).unwrap(); + assert_eq!(int_obj.to_integer().unwrap(), 100); } - + #[test] fn test_type_names() { assert_eq!(NyashValue::new_integer(1).type_name(), "Integer"); @@ -503,73 +479,74 @@ mod tests { assert_eq!(NyashValue::new_null().type_name(), "Null"); assert_eq!(NyashValue::new_void().type_name(), "Void"); } - + #[test] fn test_weak_reference_basic() { use crate::box_trait::StringBox; - + // Create a strong reference let strong_ref = NyashValue::Box(Arc::new(Mutex::new(StringBox::new("test".to_string())))); - + // Create weak reference let weak_ref = strong_ref.downgrade_to_weak().unwrap(); assert!(weak_ref.is_weak_reference()); assert_eq!(weak_ref.type_name(), "WeakBox"); - + // Upgrade should work let upgraded = weak_ref.upgrade_weak().unwrap(); assert_eq!(upgraded, strong_ref); - + // Both should be alive initially assert!(weak_ref.is_weak_alive()); } - + #[test] fn test_weak_reference_drop() { use crate::box_trait::StringBox; - + let weak_ref = { - let strong_ref = NyashValue::Box(Arc::new(Mutex::new(StringBox::new("test".to_string())))); + let strong_ref = + NyashValue::Box(Arc::new(Mutex::new(StringBox::new("test".to_string())))); strong_ref.downgrade_to_weak().unwrap() }; // strong_ref goes out of scope and is dropped - + // Weak reference should now be invalid assert!(!weak_ref.is_weak_alive()); assert!(weak_ref.upgrade_weak().is_none()); - + // to_bool should return false for dropped weak ref assert_eq!(weak_ref.to_bool().unwrap(), false); } - + #[test] fn test_weak_reference_equality() { use crate::box_trait::StringBox; - + let strong_ref = NyashValue::Box(Arc::new(Mutex::new(StringBox::new("test".to_string())))); let weak_ref1 = strong_ref.downgrade_to_weak().unwrap(); let weak_ref2 = strong_ref.downgrade_to_weak().unwrap(); - + // Weak refs to same object should be equal assert_eq!(weak_ref1, weak_ref2); - + // Weak ref should equal its strong ref assert_eq!(weak_ref1, strong_ref); assert_eq!(strong_ref, weak_ref1); } - - #[test] + + #[test] fn test_weak_reference_string_representation() { use crate::box_trait::StringBox; - + let strong_ref = NyashValue::Box(Arc::new(Mutex::new(StringBox::new("hello".to_string())))); let weak_ref = strong_ref.downgrade_to_weak().unwrap(); - + // Should show weak reference to the content assert!(weak_ref.to_string().contains("WeakRef")); assert!(weak_ref.to_string().contains("hello")); - + // After dropping strong ref drop(strong_ref); assert_eq!(weak_ref.to_string(), "WeakRef(null)"); } -} \ No newline at end of file +} diff --git a/src/wasm_test.rs b/src/wasm_test.rs index c8ef80d6..9f1d5b11 100644 --- a/src/wasm_test.rs +++ b/src/wasm_test.rs @@ -1,49 +1,49 @@ #[cfg(target_arch = "wasm32")] pub mod wasm_test { use wasm_bindgen::prelude::*; - use web_sys::{window, HtmlCanvasElement, CanvasRenderingContext2d}; - + use web_sys::{window, CanvasRenderingContext2d, HtmlCanvasElement}; + #[wasm_bindgen] pub fn test_direct_canvas_draw() -> Result<(), JsValue> { // Get window and document let window = window().ok_or("no window")?; let document = window.document().ok_or("no document")?; - + // Get canvas element let canvas = document .get_element_by_id("test-canvas") .ok_or("canvas not found")? .dyn_into::()?; - + // Set canvas size canvas.set_width(400); canvas.set_height(300); - + // Get 2D context let context = canvas .get_context("2d")? .ok_or("no 2d context")? .dyn_into::()?; - + // Draw black background context.set_fill_style(&JsValue::from_str("black")); context.fill_rect(0.0, 0.0, 400.0, 300.0); - + // Draw red rectangle context.set_fill_style(&JsValue::from_str("red")); context.fill_rect(50.0, 50.0, 100.0, 80.0); - + // Draw blue circle context.set_fill_style(&JsValue::from_str("blue")); context.begin_path(); context.arc(250.0, 100.0, 40.0, 0.0, 2.0 * std::f64::consts::PI)?; context.fill(); - + // Draw text context.set_font("20px Arial"); context.set_fill_style(&JsValue::from_str("white")); context.fill_text("Hello Direct Canvas!", 100.0, 200.0)?; - + Ok(()) } -} \ No newline at end of file +}