chore(fmt): add legacy stubs and strip trailing whitespace to unblock cargo fmt
This commit is contained in:
198
src/ast.rs
198
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<ASTNode>,
|
||||
constructors: Vec<ASTNode>,
|
||||
init_fields: Vec<String>,
|
||||
weak_fields: Vec<String>, // 🔗 weak修飾子が付いたフィールドのリスト
|
||||
weak_fields: Vec<String>, // 🔗 weak修飾子が付いたフィールドのリスト
|
||||
is_interface: bool,
|
||||
extends: Vec<String>, // 🚀 Multi-delegation: Changed from Option<String> to Vec<String>
|
||||
extends: Vec<String>, // 🚀 Multi-delegation: Changed from Option<String> to Vec<String>
|
||||
implements: Vec<String>,
|
||||
/// 🔥 ジェネリクス型パラメータ (例: ["T", "U"])
|
||||
type_parameters: Vec<String>,
|
||||
@ -50,8 +50,8 @@ pub enum StructureNode {
|
||||
name: String,
|
||||
params: Vec<String>,
|
||||
body: Vec<ASTNode>,
|
||||
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<String>, // None = catch-all
|
||||
pub variable_name: Option<String>, // 例外を受け取る変数名
|
||||
pub exception_type: Option<String>, // None = catch-all
|
||||
pub variable_name: Option<String>, // 例外を受け取る変数名
|
||||
pub body: Vec<ASTNode>, // 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<dyn NyashBox> {
|
||||
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<LiteralValue> {
|
||||
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::<StringBox>() {
|
||||
Some(LiteralValue::String(string_box.value.clone()))
|
||||
} else if let Some(int_box) = box_val.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -226,7 +226,11 @@ impl LiteralValue {
|
||||
Some(LiteralValue::Float(float_box.value))
|
||||
} else if let Some(bool_box) = box_val.as_any().downcast_ref::<BoolBox>() {
|
||||
Some(LiteralValue::Bool(bool_box.value))
|
||||
} else if box_val.as_any().downcast_ref::<crate::boxes::null_box::NullBox>().is_some() {
|
||||
} else if box_val
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::null_box::NullBox>()
|
||||
.is_some()
|
||||
{
|
||||
Some(LiteralValue::Null)
|
||||
} else if box_val.as_any().downcast_ref::<VoidBox>().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<ASTNode>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
// ===== 文 (Statements) =====
|
||||
|
||||
/// 代入文: target = value
|
||||
Assignment {
|
||||
target: Box<ASTNode>,
|
||||
value: Box<ASTNode>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// print文: print(expression)
|
||||
Print {
|
||||
expression: Box<ASTNode>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// if文: if condition { then_body } else { else_body }
|
||||
If {
|
||||
condition: Box<ASTNode>,
|
||||
@ -346,60 +349,53 @@ pub enum ASTNode {
|
||||
else_body: Option<Vec<ASTNode>>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// loop文: loop(condition) { body } のみ
|
||||
Loop {
|
||||
condition: Box<ASTNode>,
|
||||
body: Vec<ASTNode>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// return文: return value
|
||||
Return {
|
||||
value: Option<Box<ASTNode>>,
|
||||
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<String>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// nowait文: nowait variable = expression
|
||||
Nowait {
|
||||
variable: String,
|
||||
expression: Box<ASTNode>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// await式: await expression
|
||||
AwaitExpression {
|
||||
expression: Box<ASTNode>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// result伝播: expr? (ResultBoxなら isOk/getValue or 早期return)
|
||||
QMarkPropagate {
|
||||
expression: Box<ASTNode>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// peek式: peek <expr> { lit => expr, ... else => expr }
|
||||
PeekExpr {
|
||||
scrutinee: Box<ASTNode>,
|
||||
@ -408,30 +404,27 @@ pub enum ASTNode {
|
||||
span: Span,
|
||||
},
|
||||
/// 配列リテラル(糖衣): [e1, e2, ...]
|
||||
ArrayLiteral {
|
||||
elements: Vec<ASTNode>,
|
||||
span: Span,
|
||||
},
|
||||
ArrayLiteral { elements: Vec<ASTNode>, span: Span },
|
||||
/// マップリテラル(糖衣): { "k": v, ... } (Stage‑2: 文字列キー限定)
|
||||
MapLiteral {
|
||||
entries: Vec<(String, ASTNode)>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// 無名関数(最小P1: 値としてのみ。呼び出しは未対応)
|
||||
Lambda {
|
||||
params: Vec<String>,
|
||||
body: Vec<ASTNode>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// arrow文: (sender >> receiver).method(args)
|
||||
Arrow {
|
||||
sender: Box<ASTNode>,
|
||||
receiver: Box<ASTNode>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// try/catch/finally文: try { ... } catch (Type e) { ... } finally { ... }
|
||||
TryCatch {
|
||||
try_body: Vec<ASTNode>,
|
||||
@ -439,15 +432,14 @@ pub enum ASTNode {
|
||||
finally_body: Option<Vec<ASTNode>>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// throw文: throw expression
|
||||
Throw {
|
||||
expression: Box<ASTNode>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
// ===== 宣言 (Declarations) =====
|
||||
|
||||
/// box宣言: box Name { fields... methods... }
|
||||
BoxDeclaration {
|
||||
name: String,
|
||||
@ -458,65 +450,58 @@ pub enum ASTNode {
|
||||
private_fields: Vec<String>,
|
||||
methods: HashMap<String, ASTNode>, // method_name -> FunctionDeclaration
|
||||
constructors: HashMap<String, ASTNode>, // constructor_key -> FunctionDeclaration
|
||||
init_fields: Vec<String>, // initブロック内のフィールド定義
|
||||
weak_fields: Vec<String>, // 🔗 weak修飾子が付いたフィールドのリスト
|
||||
is_interface: bool, // interface box かどうか
|
||||
extends: Vec<String>, // 🚀 Multi-delegation: Changed from Option<String> to Vec<String>
|
||||
implements: Vec<String>, // 実装するinterface名のリスト
|
||||
type_parameters: Vec<String>, // 🔥 ジェネリクス型パラメータ (例: ["T", "U"])
|
||||
init_fields: Vec<String>, // initブロック内のフィールド定義
|
||||
weak_fields: Vec<String>, // 🔗 weak修飾子が付いたフィールドのリスト
|
||||
is_interface: bool, // interface box かどうか
|
||||
extends: Vec<String>, // 🚀 Multi-delegation: Changed from Option<String> to Vec<String>
|
||||
implements: Vec<String>, // 実装するinterface名のリスト
|
||||
type_parameters: Vec<String>, // 🔥 ジェネリクス型パラメータ (例: ["T", "U"])
|
||||
/// 🔥 Static boxかどうかのフラグ
|
||||
is_static: bool,
|
||||
/// 🔥 Static初期化ブロック (static { ... })
|
||||
static_init: Option<Vec<ASTNode>>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// 関数宣言: functionName(params) { body }
|
||||
FunctionDeclaration {
|
||||
name: String,
|
||||
params: Vec<String>,
|
||||
body: Vec<ASTNode>,
|
||||
is_static: bool, // 🔥 静的メソッドフラグ
|
||||
is_override: bool, // 🔥 オーバーライドフラグ
|
||||
is_static: bool, // 🔥 静的メソッドフラグ
|
||||
is_override: bool, // 🔥 オーバーライドフラグ
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// グローバル変数: global name = value
|
||||
GlobalVar {
|
||||
name: String,
|
||||
value: Box<ASTNode>,
|
||||
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<ASTNode>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// 二項演算: left operator right
|
||||
BinaryOp {
|
||||
operator: BinaryOperator,
|
||||
left: Box<ASTNode>,
|
||||
right: Box<ASTNode>,
|
||||
span: Span,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// メソッド呼び出し: object.method(arguments)
|
||||
MethodCall {
|
||||
object: Box<ASTNode>,
|
||||
@ -524,58 +509,45 @@ pub enum ASTNode {
|
||||
arguments: Vec<ASTNode>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// フィールドアクセス: object.field
|
||||
FieldAccess {
|
||||
object: Box<ASTNode>,
|
||||
field: String,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// コンストラクタ呼び出し: new ClassName(arguments)
|
||||
New {
|
||||
class: String,
|
||||
arguments: Vec<ASTNode>,
|
||||
type_arguments: Vec<String>, // 🔥 ジェネリクス型引数 (例: ["IntegerBox", "StringBox"])
|
||||
type_arguments: Vec<String>, // 🔥 ジェネリクス型引数 (例: ["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<ASTNode>, // 引数
|
||||
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<String>,
|
||||
@ -583,7 +555,7 @@ pub enum ASTNode {
|
||||
initial_values: Vec<Option<Box<ASTNode>>>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// Outbox変数宣言: outbox x, y, z (static関数内専用)
|
||||
Outbox {
|
||||
variables: Vec<String>,
|
||||
@ -591,14 +563,14 @@ pub enum ASTNode {
|
||||
initial_values: Vec<Option<Box<ASTNode>>>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// 関数呼び出し: functionName(arguments)
|
||||
FunctionCall {
|
||||
name: String,
|
||||
arguments: Vec<ASTNode>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
|
||||
/// 一般式呼び出し: (callee)(arguments)
|
||||
Call {
|
||||
callee: Box<ASTNode>,
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<dyn NyashBox>) -> Handle {
|
||||
pub fn handle_get(h: Handle) -> Option<Arc<dyn NyashBox>> {
|
||||
crate::jit::rt::handles::get(h)
|
||||
}
|
||||
|
||||
|
||||
@ -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<Self, AotError> {
|
||||
// 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<Vec<u8>, 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<Vec<u8>, 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<Vec<u8>, 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<i32, AotError> {
|
||||
// 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Self, AotError> {
|
||||
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<Self, AotError> {
|
||||
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<Self, AotError> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<u8>) -> Result<(), AotError> {
|
||||
self.precompiled_module = Some(module_data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Create the standalone executable
|
||||
pub fn create_executable<P: AsRef<Path>>(&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<String, AotError> {
|
||||
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<P: AsRef<Path>, Q: AsRef<Path>>(&self, temp_dir: P, output_path: Q) -> Result<(), AotError> {
|
||||
fn compile_rust_executable<P: AsRef<Path>, Q: AsRef<Path>>(
|
||||
&self,
|
||||
temp_dir: P,
|
||||
output_path: Q,
|
||||
) -> Result<(), AotError> {
|
||||
let temp_dir = temp_dir.as_ref();
|
||||
let 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<dyn std::error::Error>> {
|
||||
#[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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Self, AotError> {
|
||||
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<Self, AotError> {
|
||||
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<P: AsRef<Path>>(
|
||||
&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<P: AsRef<Path>>(
|
||||
&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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(())
|
||||
}
|
||||
|
||||
|
||||
@ -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<i64, String> {
|
||||
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) {}
|
||||
}
|
||||
|
||||
@ -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<BasicBlockId, Block>);
|
||||
|
||||
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<cranelift_codegen::ir::Value, String> {
|
||||
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<cranelift_codegen::ir::Value, String> {
|
||||
self.vals
|
||||
.get(id)
|
||||
.cloned()
|
||||
.ok_or_else(|| format!("undef {:?}", id))
|
||||
}
|
||||
pub fn set_val(&mut self, id: ValueId, v: cranelift_codegen::ir::Value) {
|
||||
self.vals.insert(id, v);
|
||||
}
|
||||
pub fn get_mem_or(
|
||||
&self,
|
||||
id: &ValueId,
|
||||
default: cranelift_codegen::ir::Value,
|
||||
) -> cranelift_codegen::ir::Value {
|
||||
*self.mem.get(id).unwrap_or(&default)
|
||||
}
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<i64, String> {
|
||||
|
||||
// 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<i64, String> {
|
||||
let mut vals: HashMap<ValueId, cranelift_codegen::ir::Value> = HashMap::new();
|
||||
let mut slots: HashMap<ValueId, StackSlot> = HashMap::new();
|
||||
|
||||
for (bb_id, _) in &main.blocks { clif_blocks.insert(*bb_id, builder.create_block()); }
|
||||
for (bb_id, _) in &main.blocks {
|
||||
clif_blocks.insert(*bb_id, builder.create_block());
|
||||
}
|
||||
// Switch to entry
|
||||
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<i64, String> {
|
||||
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<i64, String> {
|
||||
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<i64, String> {
|
||||
}
|
||||
}
|
||||
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<i64, String> {
|
||||
// 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<i64, String> {
|
||||
|
||||
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();
|
||||
|
||||
|
||||
@ -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<Box<dyn NyashBox>, String> {
|
||||
pub fn compile_and_execute(
|
||||
mir_module: &MirModule,
|
||||
_temp_name: &str,
|
||||
) -> Result<Box<dyn NyashBox>, String> {
|
||||
// Minimal semantics: Const/Return/Add only (straight-line code)
|
||||
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<B
|
||||
// 実行(引数なし)。戻り値は MIR の型に合わせて変換
|
||||
let out = engine.execute_handle(h, &[]);
|
||||
if let Some(jv) = out {
|
||||
let vmv = crate::jit::boundary::CallBoundaryBox::to_vm(&main.signature.return_type, jv);
|
||||
let vmv =
|
||||
crate::jit::boundary::CallBoundaryBox::to_vm(&main.signature.return_type, jv);
|
||||
let boxed: Box<dyn NyashBox> = match vmv {
|
||||
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<B
|
||||
let mut cur = main.entry_block;
|
||||
let mut last_pred: Option<crate::mir::BasicBlockId> = 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<B
|
||||
ConstValue::Null | ConstValue::Void => 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<Box<dyn crate::box_trait::NyashBox>> = Vec::new();
|
||||
let mut ny_args: Vec<Box<dyn crate::box_trait::NyashBox>> =
|
||||
Vec::new();
|
||||
for vid in args.iter().skip(1) {
|
||||
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<B
|
||||
};
|
||||
return Ok(out);
|
||||
}
|
||||
Some(MirInstruction::Jump { target }) => { 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 };
|
||||
}
|
||||
|
||||
@ -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<ControlFlow, VMError> {
|
||||
pub(super) fn execute_instruction(
|
||||
vm: &mut VM,
|
||||
instruction: &MirInstruction,
|
||||
debug_global: bool,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
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::<crate::box_trait::IntegerBox>().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::<crate::box_trait::IntegerBox>()
|
||||
.map(|x| x.value)
|
||||
.or_else(|| lb.to_string_box().value.trim().parse::<i64>().ok());
|
||||
let ri = rb.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value)
|
||||
let ri = rb
|
||||
.as_any()
|
||||
.downcast_ref::<crate::box_trait::IntegerBox>()
|
||||
.map(|x| x.value)
|
||||
.or_else(|| rb.to_string_box().value.trim().parse::<i64>().ok());
|
||||
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<DispatchEntry> { None }
|
||||
pub fn resolve(&self, _instr: &MirInstruction) -> Option<DispatchEntry> {
|
||||
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(())
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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::<crate::boxes::array::ArrayBox>().is_some() {
|
||||
if b.as_any()
|
||||
.downcast_ref::<crate::boxes::array::ArrayBox>()
|
||||
.is_some()
|
||||
{
|
||||
return ARRAY_METHODS.iter().any(|m| *m == method);
|
||||
}
|
||||
if b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>().is_some() {
|
||||
if b.as_any()
|
||||
.downcast_ref::<crate::boxes::map_box::MapBox>()
|
||||
.is_some()
|
||||
{
|
||||
return MAP_METHODS.iter().any(|m| *m == method);
|
||||
}
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,4 +13,3 @@ pub mod compiler {
|
||||
pub mod box_types {
|
||||
pub use crate::backend::llvm_legacy::box_types::*;
|
||||
}
|
||||
|
||||
|
||||
@ -3,4 +3,3 @@
|
||||
pub fn load_box_type_ids() -> std::collections::HashMap<String, u32> {
|
||||
std::collections::HashMap::new()
|
||||
}
|
||||
|
||||
|
||||
@ -1,2 +1 @@
|
||||
// legacy aot placeholder; full implementation retained in archived branch or prior history
|
||||
|
||||
|
||||
10
src/backend/llvm_legacy/compiler/codegen.rs
Normal file
10
src/backend/llvm_legacy/compiler/codegen.rs
Normal file
@ -0,0 +1,10 @@
|
||||
//! Legacy LLVM codegen placeholder.
|
||||
//!
|
||||
//! The original inkwell-based implementation was removed during the Phase-15
|
||||
//! refactor. We keep a stub module so tools like `cargo fmt` can resolve the
|
||||
//! module tree even when the legacy feature is gated off.
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn lower_module() {
|
||||
// Intentionally left blank.
|
||||
}
|
||||
@ -1,2 +1 @@
|
||||
// legacy helpers placeholder; kept to satisfy module structure after move
|
||||
|
||||
|
||||
6
src/backend/llvm_legacy/compiler/interpreter.rs
Normal file
6
src/backend/llvm_legacy/compiler/interpreter.rs
Normal file
@ -0,0 +1,6 @@
|
||||
//! Legacy LLVM interpreter placeholder.
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn execute() {
|
||||
// Stub implementation.
|
||||
}
|
||||
@ -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<Self, String> {
|
||||
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<Box<dyn NyashBox>, String> {
|
||||
pub fn compile_and_execute(
|
||||
&mut self,
|
||||
_mir: &MirModule,
|
||||
_out: &str,
|
||||
) -> Result<Box<dyn NyashBox>, String> {
|
||||
Ok(Box::new(IntegerBox::new(0)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -31,4 +31,3 @@ mod tests {
|
||||
assert!(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -11,19 +11,21 @@ pub struct CodegenContext {
|
||||
#[cfg(not(feature = "llvm-inkwell-legacy"))]
|
||||
impl CodegenContext {
|
||||
pub fn new(_module_name: &str) -> Result<Self, String> {
|
||||
Ok(Self { _phantom: std::marker::PhantomData })
|
||||
Ok(Self {
|
||||
_phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Real implementation (compiled only when feature "llvm-inkwell-legacy" is enabled)
|
||||
#[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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -34,4 +34,3 @@ mod tests {
|
||||
assert!(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<Box<dyn NyashBox>, VMError> {
|
||||
let func = module.functions.get("main").ok_or_else(|| VMError::InvalidInstruction("missing main".into()))?;
|
||||
let func = module
|
||||
.functions
|
||||
.get("main")
|
||||
.ok_or_else(|| VMError::InvalidInstruction("missing main".into()))?;
|
||||
let ret = self.execute_function(func)?;
|
||||
Ok(ret.to_nyash_box())
|
||||
}
|
||||
@ -36,7 +47,10 @@ impl MirInterpreter {
|
||||
let mut cur = func.entry_block;
|
||||
let mut last_pred: Option<BasicBlockId> = 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<Box<dyn crate::box_trait::NyashBox>> = Vec::new();
|
||||
for vid in args { a.push(self.reg_load(*vid)?.to_nyash_box()); }
|
||||
for vid in args {
|
||||
a.push(self.reg_load(*vid)?.to_nyash_box());
|
||||
}
|
||||
// Use unified global registry (plugins already initialized by runner)
|
||||
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<dyn crate::box_trait::NyashBox> = 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::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host();
|
||||
if let Some(p) = recv_box
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>(
|
||||
) {
|
||||
let host =
|
||||
crate::runtime::plugin_loader_unified::get_global_plugin_host();
|
||||
let host = host.read().unwrap();
|
||||
let mut argv: Vec<Box<dyn crate::box_trait::NyashBox>> = 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::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
if let Some(p) = recv_box
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>(
|
||||
) {
|
||||
// Special-case: ConsoleBox.readLine → stdin fallback if not provided by plugin
|
||||
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<Box<dyn crate::box_trait::NyashBox>> = 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<VMValue, VMError> {
|
||||
self.regs.get(&id).cloned().ok_or_else(|| VMError::InvalidValue(format!("use of undefined value {:?}", id)))
|
||||
self.regs
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.ok_or_else(|| VMError::InvalidValue(format!("use of undefined value {:?}", id)))
|
||||
}
|
||||
|
||||
fn eval_binop(&self, op: BinaryOp, a: VMValue, b: VMValue) -> Result<VMValue, VMError> {
|
||||
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<bool, VMError> {
|
||||
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
|
||||
)))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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<dyn NyashBox>, method: &str, _args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, VMError> {
|
||||
pub(super) fn call_box_method_impl(
|
||||
&self,
|
||||
box_value: Box<dyn NyashBox>,
|
||||
method: &str,
|
||||
_args: Vec<Box<dyn NyashBox>>,
|
||||
) -> Result<Box<dyn NyashBox>, VMError> {
|
||||
// PluginBoxV2: delegate to unified plugin host (BID-FFI v1)
|
||||
if let Some(pbox) = box_value.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
if let Some(pbox) = box_value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
{
|
||||
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
|
||||
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::<crate::boxes::math_box::MathBox>() {
|
||||
if let Some(math) = box_value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::math_box::MathBox>()
|
||||
{
|
||||
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::<crate::boxes::result::NyashResultBox>() {
|
||||
if let Some(result_box) = box_value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::result::NyashResultBox>()
|
||||
{
|
||||
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::<crate::boxes::jit_config_box::JitConfigBox>() {
|
||||
if let Some(jcb) = box_value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::jit_config_box::JitConfigBox>()
|
||||
{
|
||||
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::<i64>().unwrap_or(0);
|
||||
return Ok(jcb.set_threshold(iv).unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
|
||||
return Ok(jcb
|
||||
.set_threshold(iv)
|
||||
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
|
||||
}
|
||||
return Ok(Box::new(StringBox::new("setThreshold(n) requires 1 arg")));
|
||||
}
|
||||
"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::<crate::boxes::jit_policy_box::JitPolicyBox>() {
|
||||
if let Some(jpb) = box_value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::jit_policy_box::JitPolicyBox>()
|
||||
{
|
||||
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::<crate::boxes::jit_stats_box::JitStatsBox>() {
|
||||
if let Some(jsb) = box_value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::jit_stats_box::JitStatsBox>()
|
||||
{
|
||||
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<serde_json::Value> = v.into_iter().map(|(name, hits, compiled, handle)| {
|
||||
serde_json::json!({
|
||||
"name": name,
|
||||
"hits": hits,
|
||||
"compiled": compiled,
|
||||
"handle": handle
|
||||
let arr: Vec<serde_json::Value> = 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<serde_json::Value> = perf.into_iter().map(|(name, phi_t, phi_b1, rb, hits, compiled, handle)| serde_json::json!({
|
||||
"name": name, "phi_total": phi_t, "phi_b1": phi_b1, "ret_bool_hint": rb, "hits": hits, "compiled": compiled, "handle": handle
|
||||
})).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<serde_json::Value> = 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<serde_json::Value> = 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::<StringBox>() {
|
||||
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::<i64>() { Ok(v) => v.max(0) as usize, Err(_) => 0 };
|
||||
let e = match _args[1].to_string_box().value.parse::<i64>() { Ok(v) => v.max(0) as usize, Err(_) => string_box.value.chars().count() };
|
||||
let s = match _args[0].to_string_box().value.parse::<i64>() {
|
||||
Ok(v) => v.max(0) as usize,
|
||||
Err(_) => 0,
|
||||
};
|
||||
let e = match _args[1].to_string_box().value.parse::<i64>() {
|
||||
Ok(v) => v.max(0) as usize,
|
||||
Err(_) => string_box.value.chars().count(),
|
||||
};
|
||||
return Ok(string_box.substring(s, e));
|
||||
}
|
||||
return Ok(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::<crate::boxes::array::ArrayBox>() {
|
||||
if let Some(array_box) = box_value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::array::ArrayBox>()
|
||||
{
|
||||
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::<crate::boxes::map_box::MapBox>() {
|
||||
if let Some(map_box) = box_value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::map_box::MapBox>()
|
||||
{
|
||||
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::<crate::boxes::task_group_box::TaskGroupBox>().is_some() {
|
||||
if box_value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::task_group_box::TaskGroupBox>()
|
||||
.is_some()
|
||||
{
|
||||
let mut owned = box_value;
|
||||
if let Some(tg) = (&mut *owned).as_any_mut().downcast_mut::<crate::boxes::task_group_box::TaskGroupBox>() {
|
||||
if let Some(tg) = (&mut *owned)
|
||||
.as_any_mut()
|
||||
.downcast_mut::<crate::boxes::task_group_box::TaskGroupBox>()
|
||||
{
|
||||
match method {
|
||||
"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::<i64>().unwrap_or(2000));
|
||||
let ms = _args
|
||||
.get(0)
|
||||
.map(|a| a.to_string_box().value.parse::<i64>().unwrap_or(2000));
|
||||
return Ok(tg.joinAll(ms));
|
||||
}
|
||||
_ => { return Ok(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::<crate::boxes::p2p_box::P2PBox>() {
|
||||
if let Some(p2p) = box_value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::p2p_box::P2PBox>()
|
||||
{
|
||||
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::<crate::boxes::socket_box::SocketBox>() {
|
||||
if let Some(sock) = box_value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::socket_box::SocketBox>()
|
||||
{
|
||||
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::<IntegerBox>() { match method { "toString" => { return Ok(Box::new(StringBox::new(integer_box.value.to_string()))); }, "abs" => { return Ok(Box::new(IntegerBox::new(integer_box.value.abs()))); }, _ => return Ok(Box::new(VoidBox::new())), } }
|
||||
if let Some(integer_box) = box_value.as_any().downcast_ref::<IntegerBox>() {
|
||||
match method {
|
||||
"toString" => {
|
||||
return Ok(Box::new(StringBox::new(integer_box.value.to_string())));
|
||||
}
|
||||
"abs" => {
|
||||
return Ok(Box::new(IntegerBox::new(integer_box.value.abs())));
|
||||
}
|
||||
_ => return Ok(Box::new(VoidBox::new())),
|
||||
}
|
||||
}
|
||||
|
||||
// BoolBox methods
|
||||
if let Some(bool_box) = box_value.as_any().downcast_ref::<BoolBox>() { match method { "toString" => { return Ok(Box::new(StringBox::new(bool_box.value.to_string()))); }, _ => return Ok(Box::new(VoidBox::new())), } }
|
||||
if let Some(bool_box) = box_value.as_any().downcast_ref::<BoolBox>() {
|
||||
match method {
|
||||
"toString" => {
|
||||
return Ok(Box::new(StringBox::new(bool_box.value.to_string())));
|
||||
}
|
||||
_ => return Ok(Box::new(VoidBox::new())),
|
||||
}
|
||||
}
|
||||
|
||||
// Default: return void for any unrecognized box type or method
|
||||
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<dyn NyashBox>], stage: &str, result: Option<&VMValue>) {
|
||||
pub(super) fn debug_log_boxcall(
|
||||
&self,
|
||||
recv: &VMValue,
|
||||
method: &str,
|
||||
args: &[Box<dyn NyashBox>],
|
||||
stage: &str,
|
||||
result: Option<&VMValue>,
|
||||
) {
|
||||
if std::env::var("NYASH_VM_DEBUG_BOXCALL").ok().as_deref() == Some("1") {
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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::<u32>() { if t > 0 { jm.set_threshold(t); } }
|
||||
if let Ok(t) = s.parse::<u32>() {
|
||||
if t > 0 {
|
||||
jm.set_threshold(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
jm.record_entry(&function.signature.name);
|
||||
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<ControlFlow, VMError> {
|
||||
pub(super) fn execute_instruction(
|
||||
&mut self,
|
||||
instruction: &MirInstruction,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let debug_global = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1");
|
||||
let debug_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)
|
||||
}
|
||||
|
||||
@ -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("<none>").to_string();
|
||||
let func = self
|
||||
.current_function
|
||||
.as_deref()
|
||||
.unwrap_or("<none>")
|
||||
.to_string();
|
||||
let bb = self.frame.current_block.map(|b| b.0 as i64).unwrap_or(-1);
|
||||
let pc = self.frame.pc as i64;
|
||||
(func, bb, pc)
|
||||
@ -78,7 +84,10 @@ impl VM {
|
||||
}
|
||||
if let Some(map) = b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
||||
let vals = map.values();
|
||||
if let Some(arr2) = vals.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
if let Some(arr2) = vals
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::array::ArrayBox>()
|
||||
{
|
||||
if let Ok(items) = arr2.items.read() {
|
||||
for item in items.iter() {
|
||||
let tn = item.type_name().to_string();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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<ValueId>, func: ValueId, args: &[ValueId]) -> Result<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_call(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
func: ValueId,
|
||||
args: &[ValueId],
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
// Evaluate function value
|
||||
let fval = self.get_value(func)?;
|
||||
match fval {
|
||||
VMValue::String(func_name) => {
|
||||
// Legacy: call function by name
|
||||
let arg_values: Vec<VMValue> = args.iter().map(|arg| self.get_value(*arg)).collect::<Result<Vec<_>, _>>()?;
|
||||
let arg_values: Vec<VMValue> = args
|
||||
.iter()
|
||||
.map(|arg| self.get_value(*arg))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let result = self.call_function_by_name(&func_name, arg_values)?;
|
||||
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::<crate::boxes::function_box::FunctionBox>() {
|
||||
if let Some(fun) = arc_box
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::function_box::FunctionBox>()
|
||||
{
|
||||
// Convert args to NyashBox for interpreter helper
|
||||
let nyash_args: Vec<Box<dyn NyashBox>> = args.iter()
|
||||
let nyash_args: Vec<Box<dyn NyashBox>> = args
|
||||
.iter()
|
||||
.map(|a| self.get_value(*a).map(|v| v.to_nyash_box()))
|
||||
.collect::<Result<Vec<_>, 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
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<u16>) -> String {
|
||||
pub(super) fn build_pic_key(
|
||||
&self,
|
||||
recv: &VMValue,
|
||||
method: &str,
|
||||
method_id: Option<u16>,
|
||||
) -> String {
|
||||
let label = self.cache_label_for_recv(recv);
|
||||
let 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<String> {
|
||||
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<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_const(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
value: &ConstValue,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
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<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_binop(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
op: &BinaryOp,
|
||||
lhs: ValueId,
|
||||
rhs: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
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<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_unaryop(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
op: &UnaryOp,
|
||||
operand: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let operand_val = self.get_value(operand)?;
|
||||
let 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<ControlFlow, VMError> {
|
||||
let debug_cmp = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1") || std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1");
|
||||
if debug_cmp { eprintln!("[VM] execute_compare enter op={:?} lhs={:?} rhs={:?}", op, lhs, rhs); }
|
||||
pub(crate) fn execute_compare(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
op: &CompareOp,
|
||||
lhs: ValueId,
|
||||
rhs: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let debug_cmp = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1")
|
||||
|| std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1");
|
||||
if debug_cmp {
|
||||
eprintln!(
|
||||
"[VM] execute_compare enter op={:?} lhs={:?} rhs={:?}",
|
||||
op, lhs, rhs
|
||||
);
|
||||
}
|
||||
let mut left = self.get_value(lhs)?;
|
||||
let mut 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::<crate::box_trait::IntegerBox>() { VMValue::Integer(ib.value) }
|
||||
else { match b.to_string_box().value.trim().parse::<i64>() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) } }
|
||||
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
||||
VMValue::Integer(ib.value)
|
||||
} else {
|
||||
match b.to_string_box().value.trim().parse::<i64>() {
|
||||
Ok(n) => VMValue::Integer(n),
|
||||
Err(_) => VMValue::BoxRef(b),
|
||||
}
|
||||
}
|
||||
}
|
||||
other => other,
|
||||
};
|
||||
right = match right {
|
||||
VMValue::BoxRef(b) => {
|
||||
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { VMValue::Integer(ib.value) }
|
||||
else { match b.to_string_box().value.trim().parse::<i64>() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) } }
|
||||
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
||||
VMValue::Integer(ib.value)
|
||||
} else {
|
||||
match b.to_string_box().value.trim().parse::<i64>() {
|
||||
Ok(n) => VMValue::Integer(n),
|
||||
Err(_) => VMValue::BoxRef(b),
|
||||
}
|
||||
}
|
||||
}
|
||||
other => other,
|
||||
};
|
||||
@ -139,33 +237,70 @@ impl VM {
|
||||
println!("{}", val.to_string());
|
||||
Ok(ControlFlow::Continue)
|
||||
}
|
||||
pub(crate) fn execute_jump(&self, target: BasicBlockId) -> Result<ControlFlow, VMError> { Ok(ControlFlow::Jump(target)) }
|
||||
pub(crate) fn execute_branch(&self, condition: ValueId, then_bb: BasicBlockId, else_bb: BasicBlockId) -> Result<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_jump(&self, target: BasicBlockId) -> Result<ControlFlow, VMError> {
|
||||
Ok(ControlFlow::Jump(target))
|
||||
}
|
||||
pub(crate) fn execute_branch(
|
||||
&self,
|
||||
condition: ValueId,
|
||||
then_bb: BasicBlockId,
|
||||
else_bb: BasicBlockId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let cond_val = self.get_value(condition)?;
|
||||
let 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::<BoolBox>() { bool_box.value }
|
||||
else if b.as_any().downcast_ref::<VoidBox>().is_some() { false }
|
||||
else { return Err(VMError::TypeError(format!("Branch condition must be bool, void, or integer, got BoxRef({})", b.type_name()))); }
|
||||
if let Some(bool_box) = b.as_any().downcast_ref::<BoolBox>() {
|
||||
bool_box.value
|
||||
} else if b.as_any().downcast_ref::<VoidBox>().is_some() {
|
||||
false
|
||||
} else {
|
||||
return Err(VMError::TypeError(format!(
|
||||
"Branch condition must be bool, void, or integer, got BoxRef({})",
|
||||
b.type_name()
|
||||
)));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(VMError::TypeError(format!(
|
||||
"Branch condition must be bool, void, or integer, got {:?}",
|
||||
cond_val
|
||||
)))
|
||||
}
|
||||
_ => return Err(VMError::TypeError(format!("Branch condition must be bool, void, or integer, got {:?}", cond_val))),
|
||||
};
|
||||
Ok(ControlFlow::Jump(if should_branch { then_bb } else { else_bb }))
|
||||
Ok(ControlFlow::Jump(if should_branch {
|
||||
then_bb
|
||||
} else {
|
||||
else_bb
|
||||
}))
|
||||
}
|
||||
pub(crate) fn execute_return(&self, value: Option<ValueId>) -> Result<ControlFlow, VMError> {
|
||||
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<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_typeop(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
op: &TypeOpKind,
|
||||
value: ValueId,
|
||||
ty: &MirType,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let val = self.get_value(value)?;
|
||||
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<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_phi(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
inputs: &[(BasicBlockId, ValueId)],
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let selected = self.loop_execute_phi(dst, inputs)?;
|
||||
self.set_value(dst, selected);
|
||||
Ok(ControlFlow::Continue)
|
||||
}
|
||||
pub(crate) fn execute_load(&mut self, dst: ValueId, ptr: ValueId) -> Result<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_load(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
ptr: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let loaded_value = self.get_value(ptr)?;
|
||||
self.set_value(dst, loaded_value);
|
||||
Ok(ControlFlow::Continue)
|
||||
}
|
||||
pub(crate) fn execute_store(&mut self, value: ValueId, ptr: ValueId) -> Result<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_store(
|
||||
&mut self,
|
||||
value: ValueId,
|
||||
ptr: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let val = self.get_value(value)?;
|
||||
self.set_value(ptr, val);
|
||||
Ok(ControlFlow::Continue)
|
||||
}
|
||||
pub(crate) fn execute_copy(&mut self, dst: ValueId, src: ValueId) -> Result<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_copy(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
src: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let value = self.get_value(src)?;
|
||||
let 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<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_array_get(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
array: ValueId,
|
||||
index: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let array_val = self.get_value(array)?;
|
||||
let 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<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_array_set(
|
||||
&mut self,
|
||||
array: ValueId,
|
||||
index: ValueId,
|
||||
value: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let array_val = self.get_value(array)?;
|
||||
let 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<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_ref_new(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
box_val: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let box_value = self.get_value(box_val)?;
|
||||
self.set_value(dst, box_value);
|
||||
Ok(ControlFlow::Continue)
|
||||
}
|
||||
pub(crate) fn execute_ref_get(&mut self, dst: ValueId, reference: ValueId, field: &str) -> Result<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_ref_get(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
reference: ValueId,
|
||||
field: &str,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let debug_ref = std::env::var("NYASH_VM_DEBUG_REF").ok().as_deref() == Some("1");
|
||||
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<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_ref_set(
|
||||
&mut self,
|
||||
reference: ValueId,
|
||||
field: &str,
|
||||
value: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let debug_ref = std::env::var("NYASH_VM_DEBUG_REF").ok().as_deref() == Some("1");
|
||||
let 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<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_weak_new(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
box_val: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let box_value = self.get_value(box_val)?;
|
||||
self.set_value(dst, box_value);
|
||||
Ok(ControlFlow::Continue)
|
||||
}
|
||||
pub(crate) fn execute_weak_load(&mut self, dst: ValueId, weak_ref: ValueId) -> Result<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_weak_load(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
weak_ref: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let weak_value = self.get_value(weak_ref)?;
|
||||
self.set_value(dst, weak_value);
|
||||
Ok(ControlFlow::Continue)
|
||||
}
|
||||
pub(crate) fn execute_barrier_read(&mut self, dst: ValueId, value: ValueId) -> Result<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_barrier_read(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
value: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let val = self.get_value(value)?;
|
||||
self.set_value(dst, val);
|
||||
Ok(ControlFlow::Continue)
|
||||
}
|
||||
pub(crate) fn execute_barrier_write(&mut self, _value: ValueId) -> Result<ControlFlow, VMError> { Ok(ControlFlow::Continue) }
|
||||
pub(crate) fn execute_barrier_write(
|
||||
&mut self,
|
||||
_value: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
Ok(ControlFlow::Continue)
|
||||
}
|
||||
pub(crate) fn execute_throw(&mut self, exception: ValueId) -> Result<ControlFlow, VMError> {
|
||||
let exc_value = self.get_value(exception)?;
|
||||
Err(VMError::InvalidInstruction(format!("Exception thrown: {:?}", exc_value)))
|
||||
Err(VMError::InvalidInstruction(format!(
|
||||
"Exception thrown: {:?}",
|
||||
exc_value
|
||||
)))
|
||||
}
|
||||
pub(crate) fn execute_catch(&mut self, exception_value: ValueId) -> Result<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_catch(
|
||||
&mut self,
|
||||
exception_value: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
self.set_value(exception_value, VMValue::Void);
|
||||
Ok(ControlFlow::Continue)
|
||||
}
|
||||
|
||||
// ---- Futures ----
|
||||
pub(crate) fn execute_await(&mut self, dst: ValueId, future: ValueId) -> Result<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_await(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
future: ValueId,
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
let future_val = self.get_value(future)?;
|
||||
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
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<ValueId>, iface_name: &str, method_name: &str, args: &[ValueId]) -> Result<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_extern_call(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
iface_name: &str,
|
||||
method_name: &str,
|
||||
args: &[ValueId],
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
// Core-13 pure shims: env.local.{get,set}, env.box.new
|
||||
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<Box<dyn NyashBox>> = 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<VMValue> = args.iter().filter_map(|a| self.get_value(*a).ok()).collect();
|
||||
let vm_args: Vec<VMValue> = args
|
||||
.iter()
|
||||
.filter_map(|a| self.get_value(*a).ok())
|
||||
.collect();
|
||||
match (iface_name, method_name, slot) {
|
||||
("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<Box<dyn NyashBox>> = Vec::new();
|
||||
for v in vm_args.iter().skip(1) { ny_args.push(v.to_nyash_box()); }
|
||||
for v in vm_args.iter().skip(1) {
|
||||
ny_args.push(v.to_nyash_box());
|
||||
}
|
||||
let reg = crate::runtime::box_registry::get_global_registry();
|
||||
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<VMValue> = args.iter().filter_map(|a| self.get_value(*a).ok()).collect();
|
||||
let vm_args: Vec<VMValue> = args
|
||||
.iter()
|
||||
.filter_map(|a| self.get_value(*a).ok())
|
||||
.collect();
|
||||
if vm_args.len() >= 2 {
|
||||
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<VMValue> = args.iter().filter_map(|a| self.get_value(*a).ok()).collect();
|
||||
let vm_args: Vec<VMValue> = args
|
||||
.iter()
|
||||
.filter_map(|a| self.get_value(*a).ok())
|
||||
.collect();
|
||||
if let Some(k) = vm_args.get(0) {
|
||||
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<VMValue> = args.iter().filter_map(|a| self.get_value(*a).ok()).collect();
|
||||
let vm_args: Vec<VMValue> = args
|
||||
.iter()
|
||||
.filter_map(|a| self.get_value(*a).ok())
|
||||
.collect();
|
||||
match method_name {
|
||||
"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<Box<dyn NyashBox>> = Vec::new();
|
||||
for arg_id in args { let arg_value = self.get_value(*arg_id)?; nyash_args.push(arg_value.to_nyash_box()); }
|
||||
for arg_id in args {
|
||||
let arg_value = self.get_value(*arg_id)?;
|
||||
nyash_args.push(arg_value.to_nyash_box());
|
||||
}
|
||||
|
||||
if crate::config::env::extern_trace() {
|
||||
if 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));
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_newbox(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
box_type: &str,
|
||||
args: &[ValueId],
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
// Convert args to NyashBox values
|
||||
let arg_values: Vec<Box<dyn NyashBox>> = args.iter()
|
||||
let arg_values: Vec<Box<dyn NyashBox>> = args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
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::<crate::box_trait::IntegerBox>() {
|
||||
if let Some(ib) = new_box
|
||||
.as_any()
|
||||
.downcast_ref::<crate::box_trait::IntegerBox>()
|
||||
{
|
||||
self.set_value(dst, VMValue::Integer(ib.value));
|
||||
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::<crate::box_trait::StringBox>() {
|
||||
if let Some(sb) = new_box
|
||||
.as_any()
|
||||
.downcast_ref::<crate::box_trait::StringBox>()
|
||||
{
|
||||
self.set_value(dst, VMValue::String(sb.value.clone()));
|
||||
return Ok(ControlFlow::Continue);
|
||||
}
|
||||
|
||||
@ -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<ValueId>, box_val: ValueId, method: &str, args: &[ValueId]) -> Result<ControlFlow, VMError> {
|
||||
pub(crate) fn execute_plugin_invoke(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
box_val: ValueId,
|
||||
method: &str,
|
||||
args: &[ValueId],
|
||||
) -> Result<ControlFlow, VMError> {
|
||||
// Helper: extract UTF-8 string from internal StringBox, Result.Ok(String-like), or plugin StringBox via toUtf8
|
||||
fn extract_string_from_box(bx: &dyn crate::box_trait::NyashBox) -> Option<String> {
|
||||
if let Some(sb) = bx.as_any().downcast_ref::<crate::box_trait::StringBox>() { return Some(sb.value.clone()); }
|
||||
if let Some(res) = bx.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() {
|
||||
if let crate::boxes::result::NyashResultBox::Ok(inner) = res { return extract_string_from_box(inner.as_ref()); }
|
||||
if let Some(sb) = bx.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||
return Some(sb.value.clone());
|
||||
}
|
||||
if let Some(p) = bx.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
if let Some(res) = bx
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::result::NyashResultBox>()
|
||||
{
|
||||
if let crate::boxes::result::NyashResultBox::Ok(inner) = res {
|
||||
return extract_string_from_box(inner.as_ref());
|
||||
}
|
||||
}
|
||||
if let Some(p) = bx
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
{
|
||||
if p.box_type == "StringBox" {
|
||||
let host = crate::runtime::get_global_plugin_host();
|
||||
let tmp: Option<String> = if let Ok(ro) = host.read() {
|
||||
if let Ok(val_opt) = ro.invoke_instance_method("StringBox", "toUtf8", p.inner.instance_id, &[]) {
|
||||
if let Some(vb) = val_opt { if let Some(sb2) = vb.as_any().downcast_ref::<crate::box_trait::StringBox>() { Some(sb2.value.clone()) } else { None } } else { None }
|
||||
} else { None }
|
||||
} else { None };
|
||||
if tmp.is_some() { return tmp; }
|
||||
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::<crate::box_trait::StringBox>()
|
||||
{
|
||||
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::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some()) {
|
||||
if method == "birth"
|
||||
&& !matches!(recv, VMValue::BoxRef(ref b) if b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some())
|
||||
{
|
||||
eprintln!("[VM PluginInvoke] static birth fallback recv={:?}", recv);
|
||||
let mut created: Option<VMValue> = None;
|
||||
match &recv {
|
||||
VMValue::String(s) => {
|
||||
let host = crate::runtime::get_global_plugin_host();
|
||||
let host = host.read().unwrap();
|
||||
let sb: Box<dyn crate::box_trait::NyashBox> = 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<dyn crate::box_trait::NyashBox> =
|
||||
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::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
if let Some(p) = pbox
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
{
|
||||
let host = crate::runtime::get_global_plugin_host();
|
||||
let host = 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::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
if let Some(h) = b
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
{
|
||||
if h.box_type == "StringBox" {
|
||||
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::<crate::box_trait::StringBox>().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::<crate::box_trait::StringBox>()
|
||||
.map(|s| s.value.clone())
|
||||
}) {
|
||||
crate::runtime::plugin_ffi_common::encode::string(
|
||||
&mut tlv, &sb,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if h.box_type == "IntegerBox" {
|
||||
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::<crate::box_trait::IntegerBox>().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::<crate::box_trait::IntegerBox>()
|
||||
.map(|i| i.value)
|
||||
}) {
|
||||
crate::runtime::plugin_ffi_common::encode::i64(
|
||||
&mut tlv, ib,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
crate::runtime::plugin_ffi_common::encode::plugin_handle(&mut tlv, h.inner.type_id, h.inner.instance_id);
|
||||
crate::runtime::plugin_ffi_common::encode::plugin_handle(
|
||||
&mut tlv,
|
||||
h.inner.type_id,
|
||||
h.inner.instance_id,
|
||||
);
|
||||
} else {
|
||||
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<dyn crate::box_trait::NyashBox>))
|
||||
} else { vm_out_raw }
|
||||
VMValue::BoxRef(std::sync::Arc::from(
|
||||
Box::new(res) as Box<dyn crate::box_trait::NyashBox>
|
||||
))
|
||||
} else {
|
||||
vm_out_raw
|
||||
}
|
||||
};
|
||||
if let Some(dst_id) = dst { self.set_value(dst_id, vm_out); }
|
||||
if let Some(dst_id) = dst {
|
||||
self.set_value(dst_id, vm_out);
|
||||
}
|
||||
return Ok(ControlFlow::Continue);
|
||||
}
|
||||
}
|
||||
@ -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<dyn crate::box_trait::NyashBox>))); }
|
||||
if let Some(dst_id) = dst {
|
||||
self.set_value(
|
||||
dst_id,
|
||||
VMValue::BoxRef(std::sync::Arc::from(
|
||||
out as Box<dyn crate::box_trait::NyashBox>,
|
||||
)),
|
||||
);
|
||||
}
|
||||
return Ok(ControlFlow::Continue);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(VMError::InvalidInstruction(format!("PluginInvoke requires PluginBox receiver; method={} got {:?}", method, recv)))
|
||||
Err(VMError::InvalidInstruction(format!(
|
||||
"PluginInvoke requires PluginBox receiver; method={} got {:?}",
|
||||
method, recv
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<BasicBlockId>,
|
||||
|
||||
|
||||
/// Phi nodeの値キャッシュ(最適化用)
|
||||
phi_cache: HashMap<ValueId, VMValue>,
|
||||
}
|
||||
@ -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<VMValue, VMError>,
|
||||
) -> Result<VMValue, VMError> {
|
||||
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<BasicBlockId, usize>,
|
||||
}
|
||||
@ -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<VMValue, VMError> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<VMValue, VMError> {
|
||||
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
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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::<Vec<_>>()
|
||||
});
|
||||
println!("{}", serde_json::to_string_pretty(&payload).unwrap_or_else(|_| String::from("{}")));
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&payload).unwrap_or_else(|_| String::from("{}"))
|
||||
);
|
||||
} else {
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<i64, VMError> {
|
||||
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::<BoolBox>() { return Ok(bb.value); }
|
||||
if let Some(ib) = b.as_any().downcast_ref::<IntegerBox>() { return Ok(ib.value != 0); }
|
||||
if let Some(ib) = b.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>() { return Ok(ib.value != 0); }
|
||||
if b.as_any().downcast_ref::<VoidBox>().is_some() { return Ok(false); }
|
||||
Err(VMError::TypeError(format!("Expected bool, got BoxRef({})", b.type_name())))
|
||||
if let Some(bb) = b.as_any().downcast_ref::<BoolBox>() {
|
||||
return Ok(bb.value);
|
||||
}
|
||||
if let Some(ib) = b.as_any().downcast_ref::<IntegerBox>() {
|
||||
return Ok(ib.value != 0);
|
||||
}
|
||||
if let Some(ib) = b
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
|
||||
{
|
||||
return Ok(ib.value != 0);
|
||||
}
|
||||
if b.as_any().downcast_ref::<VoidBox>().is_some() {
|
||||
return Ok(false);
|
||||
}
|
||||
Err(VMError::TypeError(format!(
|
||||
"Expected bool, got BoxRef({})",
|
||||
b.type_name()
|
||||
)))
|
||||
}
|
||||
VMValue::Void => Ok(false),
|
||||
VMValue::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<dyn crate::box_trait::NyashBox>) -> VMValue {
|
||||
if nyash_box.as_any().downcast_ref::<crate::boxes::null_box::NullBox>().is_some() {
|
||||
if nyash_box
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::null_box::NullBox>()
|
||||
.is_some()
|
||||
{
|
||||
// Treat NullBox as Void in VMValue to align with `null` literal semantics
|
||||
VMValue::Void
|
||||
} else if let Some(int_box) = nyash_box.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -129,7 +150,10 @@ impl VMValue {
|
||||
VMValue::Bool(bool_box.value)
|
||||
} else if let Some(string_box) = nyash_box.as_any().downcast_ref::<StringBox>() {
|
||||
VMValue::String(string_box.value.clone())
|
||||
} else if let Some(future_box) = nyash_box.as_any().downcast_ref::<crate::boxes::future::FutureBox>() {
|
||||
} else if let Some(future_box) = nyash_box
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::future::FutureBox>()
|
||||
{
|
||||
VMValue::Future(future_box.clone())
|
||||
} else {
|
||||
VMValue::BoxRef(Arc::from(nyash_box))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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<WasmModule, WasmError> {
|
||||
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<String, WasmError> {
|
||||
fn generate_function(
|
||||
&mut self,
|
||||
name: &str,
|
||||
mir_function: MirFunction,
|
||||
) -> Result<String, WasmError> {
|
||||
// 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<u32, WasmError> {
|
||||
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<Vec<String>, 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<Vec<String>, WasmError> {
|
||||
let block = mir_function.blocks.get(&block_id).ok_or_else(|| {
|
||||
WasmError::CodegenError(format!("Basic block {:?} not found", block_id))
|
||||
})?;
|
||||
|
||||
let mut instructions = Vec::new();
|
||||
|
||||
|
||||
// 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<Vec<String>, WasmError> {
|
||||
fn generate_instruction(
|
||||
&mut self,
|
||||
instruction: &MirInstruction,
|
||||
) -> Result<Vec<String>, 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<Vec<String>, WasmError> {
|
||||
fn generate_const(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
value: &ConstValue,
|
||||
) -> Result<Vec<String>, 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<Vec<String>, WasmError> {
|
||||
fn generate_binop(
|
||||
&self,
|
||||
dst: ValueId,
|
||||
op: BinaryOp,
|
||||
lhs: ValueId,
|
||||
rhs: ValueId,
|
||||
) -> Result<Vec<String>, 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<Vec<String>, WasmError> {
|
||||
fn generate_compare(
|
||||
&self,
|
||||
dst: ValueId,
|
||||
op: CompareOp,
|
||||
lhs: ValueId,
|
||||
rhs: ValueId,
|
||||
) -> Result<Vec<String>, 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<Vec<String>, 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<Vec<String>, WasmError> {
|
||||
fn generate_string_box_const(
|
||||
&self,
|
||||
dst: ValueId,
|
||||
data_offset: u32,
|
||||
string_len: u32,
|
||||
) -> Result<Vec<String>, WasmError> {
|
||||
// Allocate a StringBox using the StringBox allocator
|
||||
// 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<Vec<String>, 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<String> {
|
||||
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::<String>();
|
||||
|
||||
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<u32, WasmError> {
|
||||
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<ValueId>, box_val: ValueId, method: &str, args: &[ValueId]) -> Result<Vec<String>, WasmError> {
|
||||
fn generate_box_call(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
box_val: ValueId,
|
||||
method: &str,
|
||||
args: &[ValueId],
|
||||
) -> Result<Vec<String>, WasmError> {
|
||||
match method {
|
||||
"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<ValueId>, box_val: ValueId) -> Result<Vec<String>, WasmError> {
|
||||
fn generate_to_string_call(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
box_val: ValueId,
|
||||
) -> Result<Vec<String>, WasmError> {
|
||||
let Some(dst) = dst else {
|
||||
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<ValueId>, box_val: ValueId) -> Result<Vec<String>, WasmError> {
|
||||
fn generate_print_call(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
box_val: ValueId,
|
||||
) -> Result<Vec<String>, WasmError> {
|
||||
let mut instructions = vec![
|
||||
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<ValueId>, box_val: ValueId, args: &[ValueId]) -> Result<Vec<String>, WasmError> {
|
||||
fn generate_equals_call(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
box_val: ValueId,
|
||||
args: &[ValueId],
|
||||
) -> Result<Vec<String>, WasmError> {
|
||||
let Some(dst) = dst else {
|
||||
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<ValueId>, box_val: ValueId) -> Result<Vec<String>, WasmError> {
|
||||
fn generate_clone_call(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
box_val: ValueId,
|
||||
) -> Result<Vec<String>, WasmError> {
|
||||
let Some(dst) = dst else {
|
||||
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<ValueId>, box_val: ValueId, args: &[ValueId]) -> Result<Vec<String>, WasmError> {
|
||||
let mut instructions = vec![
|
||||
format!(";; log() implementation for ValueId({})", box_val.as_u32()),
|
||||
];
|
||||
|
||||
fn generate_log_call(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
box_val: ValueId,
|
||||
args: &[ValueId],
|
||||
) -> Result<Vec<String>, WasmError> {
|
||||
let mut instructions = vec![format!(
|
||||
";; log() implementation for ValueId({})",
|
||||
box_val.as_u32()
|
||||
)];
|
||||
|
||||
// Load box_val (ConsoleBox instance)
|
||||
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
|
||||
}
|
||||
|
||||
@ -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::<u32>() % 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<u32> {
|
||||
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<String>) {
|
||||
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<String> {
|
||||
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<String, WasmError> {
|
||||
let layout = self.get_box_layout(type_name)
|
||||
let layout = self
|
||||
.get_box_layout(type_name)
|
||||
.ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?;
|
||||
|
||||
|
||||
Ok(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<String, WasmError> {
|
||||
let layout = self.get_box_layout(type_name)
|
||||
pub fn get_field_get_function(
|
||||
&self,
|
||||
type_name: &str,
|
||||
field_name: &str,
|
||||
) -> Result<String, WasmError> {
|
||||
let layout = self
|
||||
.get_box_layout(type_name)
|
||||
.ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?;
|
||||
|
||||
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<String, WasmError> {
|
||||
let layout = self.get_box_layout(type_name)
|
||||
pub fn get_field_set_function(
|
||||
&self,
|
||||
type_name: &str,
|
||||
field_name: &str,
|
||||
) -> Result<String, WasmError> {
|
||||
let layout = self
|
||||
.get_box_layout(type_name)
|
||||
.ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?;
|
||||
|
||||
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<u32> {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Vec<u8>, 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<Vec<u8>, 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<String, WasmError> {
|
||||
let wasm_module = self.codegen.generate_module(mir_module, &self.memory_manager, &self.runtime)?;
|
||||
let wasm_module =
|
||||
self.codegen
|
||||
.generate_module(mir_module, &self.memory_manager, &self.runtime)?;
|
||||
Ok(wasm_module.to_wat())
|
||||
}
|
||||
|
||||
|
||||
/// Execute WASM bytes using wasmtime (for testing)
|
||||
pub fn execute_wasm(&self, wasm_bytes: &[u8]) -> Result<i32, WasmError> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<String> {
|
||||
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<String>, result: Option<String>) {
|
||||
pub fn add_import(
|
||||
&mut self,
|
||||
module: String,
|
||||
name: String,
|
||||
params: Vec<String>,
|
||||
result: Option<String>,
|
||||
) {
|
||||
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<String, Vec<&ImportFunction>> = std::collections::HashMap::new();
|
||||
let mut modules: std::collections::HashMap<String, Vec<&ImportFunction>> =
|
||||
std::collections::HashMap::new();
|
||||
for import in &self.imports {
|
||||
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<String, WasmError> {
|
||||
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!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Box<dyn crate::box_trait::NyashBox>, String> {
|
||||
pub fn compile_and_execute_v2(
|
||||
_module: &crate::mir::MirModule,
|
||||
_temp_name: &str,
|
||||
) -> Result<Box<dyn crate::box_trait::NyashBox>, String> {
|
||||
// 1) ConsoleBoxを生成(WASM環境ではブラウザコンソールに委譲)
|
||||
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<dyn NyashBox>];
|
||||
let args =
|
||||
vec![Box::new(StringBox::new("🎉 WASM v2 console.log working!")) as Box<dyn NyashBox>];
|
||||
let _ = unified_dispatch::dispatch_by_slot(slot_id, console.as_ref(), &args);
|
||||
}
|
||||
// 3) 結果を返す
|
||||
Ok(Box::new(StringBox::new("WASM v2 unified dispatch test completed")))
|
||||
Ok(Box::new(StringBox::new(
|
||||
"WASM v2 unified dispatch test completed",
|
||||
)))
|
||||
}
|
||||
|
||||
@ -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<u16> {
|
||||
@ -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::<MapBox>() {
|
||||
@ -113,7 +113,7 @@ pub fn dispatch_by_slot(
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
_ => None
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<BenchmarkResult> {
|
||||
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<BenchmarkResult, Box<dyn std::error::Error>> {
|
||||
fn run_interpreter_benchmark(
|
||||
&self,
|
||||
name: &str,
|
||||
source: &str,
|
||||
) -> Result<BenchmarkResult, Box<dyn std::error::Error>> {
|
||||
let mut total_duration = 0.0;
|
||||
|
||||
|
||||
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<BenchmarkResult, Box<dyn std::error::Error>> {
|
||||
fn run_vm_benchmark(
|
||||
&self,
|
||||
name: &str,
|
||||
source: &str,
|
||||
) -> Result<BenchmarkResult, Box<dyn std::error::Error>> {
|
||||
let mut total_duration = 0.0;
|
||||
|
||||
|
||||
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<BenchmarkResult, Box<dyn std::error::Error>> {
|
||||
fn run_wasm_benchmark(
|
||||
&self,
|
||||
name: &str,
|
||||
source: &str,
|
||||
) -> Result<BenchmarkResult, Box<dyn std::error::Error>> {
|
||||
let mut total_duration = 0.0;
|
||||
|
||||
|
||||
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<String, Vec<&BenchmarkResult>> = std::collections::HashMap::new();
|
||||
let mut benchmarks: std::collections::HashMap<String, Vec<&BenchmarkResult>> =
|
||||
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::<f64>() / results.iter().filter(|r| r.backend == "Interpreter").count() as f64;
|
||||
|
||||
let vm_avg: f64 = results.iter()
|
||||
.sum::<f64>()
|
||||
/ 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::<f64>() / results.iter().filter(|r| r.backend == "VM").count() as f64;
|
||||
|
||||
let wasm_avg: f64 = results.iter()
|
||||
.sum::<f64>()
|
||||
/ 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::<f64>() / results.iter().filter(|r| r.backend == "WASM").count() as f64;
|
||||
|
||||
.sum::<f64>()
|
||||
/ results.iter().filter(|r| r.backend == "WASM").count() as f64;
|
||||
|
||||
println!(" 📈 Average across all benchmarks:");
|
||||
println!(" 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<BidHandle, BidError>;
|
||||
|
||||
|
||||
/// Get the BID type representation
|
||||
fn bid_type(&self) -> BidType;
|
||||
}
|
||||
@ -17,10 +17,10 @@ pub trait BidBridge {
|
||||
pub struct BoxRegistry {
|
||||
/// Maps handle to Arc<dyn NyashBox>
|
||||
handle_to_box: HashMap<BidHandle, Arc<dyn NyashBox>>,
|
||||
|
||||
|
||||
/// Next instance ID for each type
|
||||
next_instance_id: HashMap<u32, u32>,
|
||||
|
||||
|
||||
/// Reverse lookup: Arc pointer to handle
|
||||
box_to_handle: HashMap<usize, BidHandle>,
|
||||
}
|
||||
@ -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<dyn NyashBox>) -> 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<Arc<dyn NyashBox>> {
|
||||
self.handle_to_box.get(&handle).cloned()
|
||||
}
|
||||
|
||||
|
||||
/// Remove a Box from the registry
|
||||
pub fn unregister(&mut self, handle: BidHandle) -> Option<Arc<dyn NyashBox>> {
|
||||
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::<crate::boxes::string_box::StringBox>() {
|
||||
if let Some(_string_box) = arc_box
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::string_box::StringBox>()
|
||||
{
|
||||
let handle = registry.register_box(
|
||||
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::<crate::boxes::integer_box::IntegerBox>() {
|
||||
Ok((
|
||||
BidType::Handle {
|
||||
type_id: 1,
|
||||
instance_id: handle.instance_id,
|
||||
},
|
||||
handle,
|
||||
))
|
||||
} else if let Some(_integer_box) = arc_box
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
|
||||
{
|
||||
let handle = registry.register_box(
|
||||
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::<crate::boxes::future::NyashFutureBox>() {
|
||||
Ok((
|
||||
BidType::Handle {
|
||||
type_id: 2,
|
||||
instance_id: handle.instance_id,
|
||||
},
|
||||
handle,
|
||||
))
|
||||
} else if let Some(_future_box) = arc_box
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::future::NyashFutureBox>()
|
||||
{
|
||||
let handle = registry.register_box(
|
||||
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<Arc<dyn NyashBox>, 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<dyn NyashBox>) -> Result<String, BidError> {
|
||||
if let Some(string_box) = arc_box.as_any().downcast_ref::<crate::boxes::string_box::StringBox>() {
|
||||
if let Some(string_box) = arc_box
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::string_box::StringBox>()
|
||||
{
|
||||
Ok(string_box.value.clone())
|
||||
} else {
|
||||
Err(BidError::InvalidType)
|
||||
@ -121,7 +150,10 @@ pub fn extract_string_value(arc_box: &Arc<dyn NyashBox>) -> Result<String, BidEr
|
||||
|
||||
/// Extract integer value from a Box for TLV encoding
|
||||
pub fn extract_integer_value(arc_box: &Arc<dyn NyashBox>) -> Result<i64, BidError> {
|
||||
if let Some(integer_box) = arc_box.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>() {
|
||||
if let Some(integer_box) = arc_box
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
|
||||
{
|
||||
Ok(integer_box.value)
|
||||
} else {
|
||||
Err(BidError::InvalidType)
|
||||
@ -131,37 +163,37 @@ pub fn extract_integer_value(arc_box: &Arc<dyn NyashBox>) -> Result<i64, BidErro
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_box_registry() {
|
||||
let mut registry = BoxRegistry::new();
|
||||
|
||||
|
||||
// Create a mock box
|
||||
let string_box = crate::boxes::string_box::StringBox::new("Hello");
|
||||
let arc_box: Arc<dyn NyashBox> = 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<dyn NyashBox> = 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<dyn NyashBox> = 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<dyn NyashBox> = 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::<crate::boxes::future::NyashFutureBox>().is_some());
|
||||
|
||||
assert!(retrieved
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::future::NyashFutureBox>()
|
||||
.is_some());
|
||||
|
||||
// Test with result set
|
||||
if let Some(future) = arc_box.as_any().downcast_ref::<crate::boxes::future::NyashFutureBox>() {
|
||||
if let Some(future) = arc_box
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::future::NyashFutureBox>()
|
||||
{
|
||||
let string_result = crate::boxes::string_box::StringBox::new("Future Result");
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<T> = Result<T, BidError>;
|
||||
pub type BidResult<T> = Result<T, BidError>;
|
||||
|
||||
@ -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<Self> {
|
||||
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<Vec<u8>> {
|
||||
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<Self> {
|
||||
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<Vec<u8>> {
|
||||
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<std::any::TypeId> { 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<std::any::TypeId> {
|
||||
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::<GenericPluginBox>() {
|
||||
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<dyn NyashBox> {
|
||||
// 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<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_plugin) = other.as_any().downcast_ref::<GenericPluginBox>() {
|
||||
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<dyn NyashBox> {
|
||||
// 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<dyn NyashBox> {
|
||||
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")))]
|
||||
|
||||
@ -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<Self> {
|
||||
// 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<unsafe extern "C" fn() -> u32> = library
|
||||
.get(PLUGIN_ABI_SYMBOL.as_bytes())
|
||||
.map_err(|_| BidError::PluginError)?;
|
||||
let init: Symbol<unsafe extern "C" fn(*const NyashHostVtable, *mut NyashPluginInfo) -> i32> = library
|
||||
let init: Symbol<
|
||||
unsafe extern "C" fn(*const NyashHostVtable, *mut NyashPluginInfo) -> i32,
|
||||
> = library
|
||||
.get(PLUGIN_INIT_SYMBOL.as_bytes())
|
||||
.map_err(|_| BidError::PluginError)?;
|
||||
let invoke: Symbol<unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32> = library
|
||||
let invoke: Symbol<
|
||||
unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
||||
> = library
|
||||
.get(PLUGIN_INVOKE_SYMBOL.as_bytes())
|
||||
.map_err(|_| BidError::PluginError)?;
|
||||
let shutdown: Symbol<unsafe extern "C" fn()> = 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<Vec<(u32, String, u32)>> {
|
||||
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<NyashHostVtable> = std::sync::La
|
||||
}
|
||||
}
|
||||
|
||||
NyashHostVtable { alloc: host_alloc, free: host_free, wake: host_wake, log: host_log }
|
||||
NyashHostVtable {
|
||||
alloc: host_alloc,
|
||||
free: host_free,
|
||||
wake: host_wake,
|
||||
log: host_log,
|
||||
}
|
||||
});
|
||||
|
||||
/// Build a minimal host vtable for plugins
|
||||
|
||||
@ -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<CString>,
|
||||
@ -159,16 +169,15 @@ impl PluginMetadata {
|
||||
methods: Vec<(u32, &str, u32)>, // (id, name, hash)
|
||||
) -> BidResult<Self> {
|
||||
// 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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -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::*;
|
||||
|
||||
@ -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<F>(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<F>(self, _f: F) -> Self
|
||||
where
|
||||
F: Fn(*mut std::os::raw::c_void) + 'static,
|
||||
{
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
pub fn with_log<F>(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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<BidHandle, std::io::Error> {
|
||||
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<Vec<u8>, 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<usize, 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();
|
||||
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<Arc<Mutex<FileBoxRegistry>>> =
|
||||
static FILEBOX_REGISTRY: Lazy<Arc<Mutex<FileBoxRegistry>>> =
|
||||
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<BidHandle, String> {
|
||||
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<Vec<u8>, 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<usize, String> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
pub use filebox::FileBoxPlugin;
|
||||
|
||||
127
src/bid/tlv.rs
127
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::<BidTlvHeader>()]);
|
||||
encoder
|
||||
.buffer
|
||||
.extend_from_slice(&[0; mem::size_of::<BidTlvHeader>()]);
|
||||
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<u8> {
|
||||
// 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::<BidTlvHeader>() {
|
||||
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::<BidTlvHeader>(),
|
||||
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<Option<(BidTag, &'a [u8])>> {
|
||||
if self.position >= self.data.len() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
|
||||
// Read entry header
|
||||
if self.position + mem::size_of::<TlvEntry>() > 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::<TlvEntry>();
|
||||
|
||||
|
||||
// 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<bool> {
|
||||
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<i32> {
|
||||
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<i64> {
|
||||
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<BidHandle> {
|
||||
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<f32> {
|
||||
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<f64> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
108
src/bid/types.rs
108
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<BidType>), // TLV tag=21
|
||||
Option(Box<BidType>), // TLV tag=21
|
||||
#[allow(dead_code)]
|
||||
Result(Box<BidType>, Box<BidType>), // TLV tag=20
|
||||
#[allow(dead_code)]
|
||||
Array(Box<BidType>), // TLV tag=22
|
||||
Array(Box<BidType>), // 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<usize> {
|
||||
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<BidTag> {
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<dyn NyashBox>, right: Box<dyn NyashBox>) -> Self {
|
||||
Self {
|
||||
left,
|
||||
right,
|
||||
Self {
|
||||
left,
|
||||
right,
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Execute the addition operation and return the result
|
||||
pub fn execute(&self) -> Box<dyn NyashBox> {
|
||||
use crate::boxes::math_box::FloatBox;
|
||||
|
||||
|
||||
// 1. Integer + Integer
|
||||
if let (Some(left_int), Some(right_int)) = (
|
||||
self.left.as_any().downcast_ref::<IntegerBox>(),
|
||||
self.right.as_any().downcast_ref::<IntegerBox>()
|
||||
self.right.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
let result = left_int.value + right_int.value;
|
||||
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::<FloatBox>(),
|
||||
self.right.as_any().downcast_ref::<FloatBox>()
|
||||
self.right.as_any().downcast_ref::<FloatBox>(),
|
||||
) {
|
||||
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::<IntegerBox>(),
|
||||
self.right.as_any().downcast_ref::<FloatBox>()
|
||||
self.right.as_any().downcast_ref::<FloatBox>(),
|
||||
) {
|
||||
let result = left_int.value as f64 + right_float.value;
|
||||
return Box::new(FloatBox::new(result));
|
||||
}
|
||||
|
||||
|
||||
// 4. Float + Integer
|
||||
if let (Some(left_float), Some(right_int)) = (
|
||||
self.left.as_any().downcast_ref::<FloatBox>(),
|
||||
self.right.as_any().downcast_ref::<IntegerBox>()
|
||||
self.right.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
let result = left_float.value + right_int.value as f64;
|
||||
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::<AddBox>() {
|
||||
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<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
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<std::any::TypeId> {
|
||||
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<dyn NyashBox>, right: Box<dyn NyashBox>) -> Self {
|
||||
Self {
|
||||
left,
|
||||
right,
|
||||
Self {
|
||||
left,
|
||||
right,
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Execute the subtraction operation and return the result
|
||||
pub fn execute(&self) -> Box<dyn NyashBox> {
|
||||
// For now, only handle integer subtraction
|
||||
if let (Some(left_int), Some(right_int)) = (
|
||||
self.left.as_any().downcast_ref::<IntegerBox>(),
|
||||
self.right.as_any().downcast_ref::<IntegerBox>()
|
||||
self.right.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
let result = left_int.value - right_int.value;
|
||||
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::<IntegerBox>() {
|
||||
let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::<IntegerBox>()
|
||||
{
|
||||
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::<SubtractBox>() {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(SubtractBox::new(self.left.clone_box(), self.right.clone_box()))
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"SubtractBox"
|
||||
}
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(SubtractBox::new(
|
||||
self.left.clone_box(),
|
||||
self.right.clone_box(),
|
||||
))
|
||||
}
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<std::any::TypeId> { self.base.parent_type_id }
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
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<dyn NyashBox>, right: Box<dyn NyashBox>) -> Self {
|
||||
Self {
|
||||
left,
|
||||
right,
|
||||
Self {
|
||||
left,
|
||||
right,
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Execute the multiplication operation and return the result
|
||||
pub fn execute(&self) -> Box<dyn NyashBox> {
|
||||
// For now, only handle integer multiplication
|
||||
if let (Some(left_int), Some(right_int)) = (
|
||||
self.left.as_any().downcast_ref::<IntegerBox>(),
|
||||
self.right.as_any().downcast_ref::<IntegerBox>()
|
||||
self.right.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
let result = left_int.value * right_int.value;
|
||||
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::<IntegerBox>() {
|
||||
let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::<IntegerBox>()
|
||||
{
|
||||
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::<MultiplyBox>() {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(MultiplyBox::new(self.left.clone_box(), self.right.clone_box()))
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"MultiplyBox"
|
||||
}
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(MultiplyBox::new(
|
||||
self.left.clone_box(),
|
||||
self.right.clone_box(),
|
||||
))
|
||||
}
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<std::any::TypeId> { self.base.parent_type_id }
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
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<dyn NyashBox>, right: Box<dyn NyashBox>) -> Self {
|
||||
Self {
|
||||
left,
|
||||
right,
|
||||
Self {
|
||||
left,
|
||||
right,
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Execute the division operation and return the result
|
||||
pub fn execute(&self) -> Box<dyn NyashBox> {
|
||||
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::<IntegerBox>(),
|
||||
self.right.as_any().downcast_ref::<IntegerBox>()
|
||||
self.right.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
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::<IntegerBox>() {
|
||||
let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::<IntegerBox>()
|
||||
{
|
||||
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::<DivideBox>() {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(DivideBox::new(self.left.clone_box(), self.right.clone_box()))
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"DivideBox"
|
||||
}
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(DivideBox::new(
|
||||
self.left.clone_box(),
|
||||
self.right.clone_box(),
|
||||
))
|
||||
}
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<std::any::TypeId> { self.base.parent_type_id }
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
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<dyn NyashBox>, right: Box<dyn NyashBox>) -> Self {
|
||||
Self {
|
||||
left,
|
||||
right,
|
||||
Self {
|
||||
left,
|
||||
right,
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Execute the modulo operation and return the result
|
||||
pub fn execute(&self) -> Box<dyn NyashBox> {
|
||||
// Handle integer modulo operation
|
||||
if let (Some(left_int), Some(right_int)) = (
|
||||
self.left.as_any().downcast_ref::<IntegerBox>(),
|
||||
self.right.as_any().downcast_ref::<IntegerBox>()
|
||||
self.right.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
if right_int.value == 0 {
|
||||
// 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::<IntegerBox>() {
|
||||
let right_val = if let Some(int_box) = self.right.as_any().downcast_ref::<IntegerBox>()
|
||||
{
|
||||
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<std::any::TypeId> { self.base.parent_type_id }
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
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::<ModuloBox>() {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(ModuloBox::new(self.left.clone_box(), self.right.clone_box()))
|
||||
Box::new(ModuloBox::new(
|
||||
self.left.clone_box(),
|
||||
self.right.clone_box(),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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::<IntegerBox>(),
|
||||
right.as_any().downcast_ref::<IntegerBox>()
|
||||
right.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
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::<IntegerBox>(),
|
||||
right.as_any().downcast_ref::<IntegerBox>()
|
||||
right.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
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::<IntegerBox>(),
|
||||
right.as_any().downcast_ref::<IntegerBox>()
|
||||
right.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
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::<IntegerBox>(),
|
||||
right.as_any().downcast_ref::<IntegerBox>()
|
||||
right.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
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<dyn NyashBox>;
|
||||
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<dyn NyashBox>;
|
||||
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<dyn NyashBox>;
|
||||
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<dyn NyashBox>;
|
||||
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<dyn NyashBox>;
|
||||
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<dyn NyashBox>;
|
||||
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<dyn NyashBox>;
|
||||
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<dyn NyashBox>;
|
||||
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<dyn NyashBox>; // 0x1000
|
||||
let left = Box::new(IntegerBox::new(4096)) as Box<dyn NyashBox>; // 0x1000
|
||||
let right = Box::new(IntegerBox::new(4096)) as Box<dyn NyashBox>; // 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<dyn NyashBox>],
|
||||
) -> Result<Box<dyn NyashBox>, 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<Arc<dyn BoxFactory>>,
|
||||
|
||||
|
||||
/// Quick lookup cache for performance
|
||||
type_cache: RwLock<HashMap<String, usize>>, // 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<dyn BoxFactory>) {
|
||||
// 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<String> = if let Ok(list) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES") {
|
||||
list.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect()
|
||||
let allow: Vec<String> = if let Ok(list) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES")
|
||||
{
|
||||
list.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect()
|
||||
} 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<String> {
|
||||
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();
|
||||
|
||||
@ -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<Box<dyn NyashBox>, 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<IntegerBox> for IntegerBox {
|
||||
type Output = IntegerBox;
|
||||
|
||||
|
||||
fn add(self, rhs: IntegerBox) -> Self::Output {
|
||||
IntegerBox::new(self.value + rhs.value)
|
||||
}
|
||||
@ -31,7 +30,7 @@ impl NyashAdd<IntegerBox> for IntegerBox {
|
||||
/// IntegerBox - IntegerBox -> IntegerBox
|
||||
impl NyashSub<IntegerBox> for IntegerBox {
|
||||
type Output = IntegerBox;
|
||||
|
||||
|
||||
fn sub(self, rhs: IntegerBox) -> Self::Output {
|
||||
IntegerBox::new(self.value - rhs.value)
|
||||
}
|
||||
@ -40,7 +39,7 @@ impl NyashSub<IntegerBox> for IntegerBox {
|
||||
/// IntegerBox * IntegerBox -> IntegerBox
|
||||
impl NyashMul<IntegerBox> for IntegerBox {
|
||||
type Output = IntegerBox;
|
||||
|
||||
|
||||
fn mul(self, rhs: IntegerBox) -> Self::Output {
|
||||
IntegerBox::new(self.value * rhs.value)
|
||||
}
|
||||
@ -49,7 +48,7 @@ impl NyashMul<IntegerBox> for IntegerBox {
|
||||
/// IntegerBox / IntegerBox -> IntegerBox (with zero check)
|
||||
impl NyashDiv<IntegerBox> for IntegerBox {
|
||||
type Output = Result<IntegerBox, OperatorError>;
|
||||
|
||||
|
||||
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::<IntegerBox>() {
|
||||
return Some(Box::new(IntegerBox::new(self.value + other_int.value)));
|
||||
}
|
||||
|
||||
|
||||
// IntegerBox + FloatBox -> FloatBox
|
||||
if let Some(other_float) = other.as_any().downcast_ref::<FloatBox>() {
|
||||
return Some(Box::new(FloatBox::new(self.value as f64 + other_float.value)));
|
||||
return Some(Box::new(FloatBox::new(
|
||||
self.value as f64 + other_float.value,
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
// Fallback: Convert both to strings and concatenate
|
||||
// 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::<IntegerBox>() {
|
||||
return Some(Box::new(IntegerBox::new(self.value - other_int.value)));
|
||||
}
|
||||
|
||||
|
||||
// IntegerBox - FloatBox -> FloatBox
|
||||
if let Some(other_float) = other.as_any().downcast_ref::<FloatBox>() {
|
||||
return Some(Box::new(FloatBox::new(self.value as f64 - other_float.value)));
|
||||
return Some(Box::new(FloatBox::new(
|
||||
self.value as f64 - other_float.value,
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
None // Subtraction not supported for other types
|
||||
}
|
||||
|
||||
|
||||
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::<IntegerBox>() {
|
||||
return Some(Box::new(IntegerBox::new(self.value * other_int.value)));
|
||||
}
|
||||
|
||||
|
||||
// IntegerBox * FloatBox -> FloatBox
|
||||
if let Some(other_float) = other.as_any().downcast_ref::<FloatBox>() {
|
||||
return Some(Box::new(FloatBox::new(self.value as f64 * other_float.value)));
|
||||
return Some(Box::new(FloatBox::new(
|
||||
self.value as f64 * other_float.value,
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
// IntegerBox * StringBox -> Repeated string
|
||||
if let Some(other_str) = other.as_any().downcast_ref::<StringBox>() {
|
||||
if self.value >= 0 && self.value <= 10000 { // Safety limit
|
||||
if self.value >= 0 && self.value <= 10000 {
|
||||
// Safety limit
|
||||
let repeated = other_str.value.repeat(self.value as usize);
|
||||
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::<FloatBox>() {
|
||||
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<FloatBox> for FloatBox {
|
||||
type Output = FloatBox;
|
||||
|
||||
|
||||
fn add(self, rhs: FloatBox) -> Self::Output {
|
||||
FloatBox::new(self.value + rhs.value)
|
||||
}
|
||||
@ -172,7 +185,7 @@ impl NyashAdd<FloatBox> for FloatBox {
|
||||
/// FloatBox - FloatBox -> FloatBox
|
||||
impl NyashSub<FloatBox> for FloatBox {
|
||||
type Output = FloatBox;
|
||||
|
||||
|
||||
fn sub(self, rhs: FloatBox) -> Self::Output {
|
||||
FloatBox::new(self.value - rhs.value)
|
||||
}
|
||||
@ -181,7 +194,7 @@ impl NyashSub<FloatBox> for FloatBox {
|
||||
/// FloatBox * FloatBox -> FloatBox
|
||||
impl NyashMul<FloatBox> for FloatBox {
|
||||
type Output = FloatBox;
|
||||
|
||||
|
||||
fn mul(self, rhs: FloatBox) -> Self::Output {
|
||||
FloatBox::new(self.value * rhs.value)
|
||||
}
|
||||
@ -190,7 +203,7 @@ impl NyashMul<FloatBox> for FloatBox {
|
||||
/// FloatBox / FloatBox -> FloatBox (with zero check)
|
||||
impl NyashDiv<FloatBox> for FloatBox {
|
||||
type Output = Result<FloatBox, OperatorError>;
|
||||
|
||||
|
||||
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::<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::<IntegerBox>() {
|
||||
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::<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::<IntegerBox>() {
|
||||
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::<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::<IntegerBox>() {
|
||||
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::<IntegerBox>() {
|
||||
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<StringBox> 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<StringBox> for StringBox {
|
||||
/// StringBox * IntegerBox -> StringBox (repetition)
|
||||
impl NyashMul<IntegerBox> 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<Box<dyn NyashBox>> {
|
||||
// StringBox + StringBox
|
||||
if let Some(other_str) = other.as_any().downcast_ref::<StringBox>() {
|
||||
return Some(Box::new(StringBox::new(format!("{}{}", self.value, other_str.value))));
|
||||
return Some(Box::new(StringBox::new(format!(
|
||||
"{}{}",
|
||||
self.value, other_str.value
|
||||
))));
|
||||
}
|
||||
|
||||
|
||||
// StringBox + any other type -> Convert to string and concatenate
|
||||
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<Box<dyn NyashBox>> {
|
||||
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<Box<dyn NyashBox>> {
|
||||
// StringBox * IntegerBox -> Repeated string
|
||||
if let Some(other_int) = other.as_any().downcast_ref::<IntegerBox>() {
|
||||
if other_int.value >= 0 && other_int.value <= 10000 { // Safety limit
|
||||
if other_int.value >= 0 && other_int.value <= 10000 {
|
||||
// Safety limit
|
||||
let repeated = self.value.repeat(other_int.value as usize);
|
||||
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<Box<dyn NyashBox>> {
|
||||
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<BoolBox> 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::<IntegerBox>() {
|
||||
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::<IntegerBox>() {
|
||||
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::<IntegerBox>() {
|
||||
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<Box<dyn NyashBox>, 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::<crate::box_trait::IntegerBox>() {
|
||||
if let Some(result) = int_box.try_add(right) {
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let Some(str_box) = left.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||
if let Some(result) = str_box.try_add(right) {
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(float_box) = right.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>() {
|
||||
|
||||
if let Some(float_box) = right
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::math_box::FloatBox>()
|
||||
{
|
||||
if let Some(result) = float_box.try_add(right) {
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let Some(bool_box) = left.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
|
||||
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::<crate::boxes::math_box::FloatBox>() {
|
||||
|
||||
if let Some(float_box) = left
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::math_box::FloatBox>()
|
||||
{
|
||||
if let Some(result) = float_box.try_sub(right) {
|
||||
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::<crate::box_trait::StringBox>() {
|
||||
if let Some(result) = str_box.try_mul(right) {
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(float_box) = left.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>() {
|
||||
|
||||
if let Some(float_box) = left
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::math_box::FloatBox>()
|
||||
{
|
||||
if let Some(result) = float_box.try_mul(right) {
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let Some(bool_box) = left.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
|
||||
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::<crate::boxes::math_box::FloatBox>() {
|
||||
|
||||
if let Some(float_box) = left
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::math_box::FloatBox>()
|
||||
{
|
||||
if let Some(result) = float_box.try_div(right) {
|
||||
return Ok(result);
|
||||
} else {
|
||||
@ -603,7 +642,7 @@ impl OperatorResolver {
|
||||
return Err(OperatorError::DivisionByZero);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let Some(bool_box) = left.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
287
src/box_trait.rs
287
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<dyn NyashBox>を全て置き換える
|
||||
pub type SharedNyashBox = Arc<dyn NyashBox>;
|
||||
@ -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<std::any::TypeId>;
|
||||
|
||||
|
||||
/// 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::<Self>()
|
||||
}
|
||||
|
||||
|
||||
/// Clone this box (equivalent to Python's copy())
|
||||
fn clone_box(&self) -> Box<dyn NyashBox>;
|
||||
|
||||
|
||||
/// Share this box (state-preserving reference sharing)
|
||||
fn share_box(&self) -> Box<dyn NyashBox>;
|
||||
|
||||
|
||||
/// 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<dyn NyashBox> {
|
||||
if self.is_identity() { self.share_box() } else { self.clone_box() }
|
||||
if self.is_identity() {
|
||||
self.share_box()
|
||||
} else {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Arc参照を返す新しいcloneメソッド(参照共有)
|
||||
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<dyn NyashBox> {
|
||||
let parts: Vec<String> = self.value.split(delimiter).map(|s| s.to_string()).collect();
|
||||
let array_elements: Vec<Box<dyn NyashBox>> = parts.into_iter()
|
||||
let array_elements: Vec<Box<dyn NyashBox>> = parts
|
||||
.into_iter()
|
||||
.map(|s| Box::new(StringBox::new(s)) as Box<dyn NyashBox>)
|
||||
.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<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(StringBox::new(self.value.replace(old, new)))
|
||||
}
|
||||
|
||||
|
||||
/// Trim whitespace from both ends
|
||||
pub fn trim(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(self.value.trim()))
|
||||
}
|
||||
|
||||
|
||||
/// Convert to uppercase
|
||||
pub fn to_upper(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(self.value.to_uppercase()))
|
||||
}
|
||||
|
||||
|
||||
/// Convert to lowercase
|
||||
pub fn to_lower(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(self.value.to_lowercase()))
|
||||
}
|
||||
|
||||
|
||||
/// Check if string contains substring
|
||||
pub fn contains(&self, search: &str) -> Box<dyn NyashBox> {
|
||||
Box::new(BoolBox::new(self.value.contains(search)))
|
||||
}
|
||||
|
||||
|
||||
/// Check if string starts with prefix
|
||||
pub fn starts_with(&self, prefix: &str) -> Box<dyn NyashBox> {
|
||||
Box::new(BoolBox::new(self.value.starts_with(prefix)))
|
||||
}
|
||||
|
||||
|
||||
/// Check if string ends with suffix
|
||||
pub fn ends_with(&self, suffix: &str) -> Box<dyn NyashBox> {
|
||||
Box::new(BoolBox::new(self.value.ends_with(suffix)))
|
||||
}
|
||||
|
||||
|
||||
/// Join array elements using this string as delimiter
|
||||
pub fn join(&self, array_box: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(array) = array_box.as_any().downcast_ref::<ArrayBox>() {
|
||||
let strings: Vec<String> = array.items.read().unwrap()
|
||||
let strings: Vec<String> = array
|
||||
.items
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.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<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.value.len() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// Convert string to integer (parse as i64)
|
||||
pub fn to_integer(&self) -> Box<dyn NyashBox> {
|
||||
match self.value.trim().parse::<i64>() {
|
||||
@ -232,7 +266,7 @@ impl StringBox {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Get character at index
|
||||
pub fn get(&self, index: usize) -> Option<Box<dyn NyashBox>> {
|
||||
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<dyn NyashBox> {
|
||||
let chars: Vec<char> = 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<std::any::TypeId> {
|
||||
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::<StringBox>() {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<std::any::TypeId> {
|
||||
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::<IntegerBox>() {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<std::any::TypeId> {
|
||||
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>() {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<std::any::TypeId> {
|
||||
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::<VoidBox>())
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"VoidBox"
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(BoolBox::new(Path::new(&self.path).exists()))
|
||||
}
|
||||
|
||||
|
||||
/// Delete file
|
||||
pub fn delete(&self) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
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<std::any::TypeId> {
|
||||
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
|
||||
}
|
||||
@ -617,7 +651,7 @@ impl NyashBox for FileBox {
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(format!("<FileBox: {}>", self.path))
|
||||
}
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_file) = other.as_any().downcast_ref::<FileBox>() {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<std::any::TypeId> {
|
||||
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::<ErrorBox>() {
|
||||
BoolBox::new(self.error_type == other_error.error_type && self.message == other_error.message)
|
||||
BoolBox::new(
|
||||
self.error_type == other_error.error_type && self.message == other_error.message,
|
||||
)
|
||||
} else {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"ErrorBox"
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox>;
|
||||
let right = Box::new(IntegerBox::new(3)) as Box<dyn NyashBox>;
|
||||
let add = AddBox::new(left, right);
|
||||
|
||||
|
||||
let result = add.execute();
|
||||
let result_int = result.as_any().downcast_ref::<IntegerBox>().unwrap();
|
||||
assert_eq!(result_int.value, 8);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_add_box_strings() {
|
||||
let left = Box::new(StringBox::new("Hello, ")) as Box<dyn NyashBox>;
|
||||
let right = Box::new(StringBox::new("Rust!")) as Box<dyn NyashBox>;
|
||||
let add = AddBox::new(left, right);
|
||||
|
||||
|
||||
let result = add.execute();
|
||||
let result_str = result.as_any().downcast_ref::<StringBox>().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();
|
||||
|
||||
@ -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<std::any::TypeId> { 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<std::any::TypeId> {
|
||||
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::<AotCompilerBox>()) }
|
||||
fn type_name(&self) -> &'static str { "AotCompilerBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("AotCompilerBox".to_string())
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<AotCompilerBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"AotCompilerBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl AotCompilerBox {
|
||||
@ -28,26 +58,32 @@ impl AotCompilerBox {
|
||||
pub fn compile(&self, file: &str, out: &str) -> Box<dyn NyashBox> {
|
||||
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))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<String>,
|
||||
}
|
||||
|
||||
impl AotConfigBox { pub fn new() -> Self { Self { base: BoxBase::new(), output_file: None, emit_obj_out: None, plugin_paths: None } } }
|
||||
impl AotConfigBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
output_file: None,
|
||||
emit_obj_out: None,
|
||||
plugin_paths: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for AotConfigBox {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 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<std::any::TypeId> {
|
||||
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::<AotConfigBox>()) }
|
||||
fn type_name(&self) -> &'static str { "AotConfigBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone(), output_file: self.output_file.clone(), emit_obj_out: self.emit_obj_out.clone(), plugin_paths: self.plugin_paths.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
self.summary()
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<AotConfigBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"AotConfigBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
output_file: self.output_file.clone(),
|
||||
emit_obj_out: self.emit_obj_out.clone(),
|
||||
plugin_paths: self.plugin_paths.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl AotConfigBox {
|
||||
pub fn set_output(&mut self, path: &str) -> Box<dyn NyashBox> { self.output_file = Some(path.to_string()); Box::new(VoidBox::new()) }
|
||||
pub fn set_obj_out(&mut self, path: &str) -> Box<dyn NyashBox> { self.emit_obj_out = Some(path.to_string()); Box::new(VoidBox::new()) }
|
||||
pub fn set_plugin_paths(&mut self, paths: &str) -> Box<dyn NyashBox> { self.plugin_paths = Some(paths.to_string()); Box::new(VoidBox::new()) }
|
||||
pub fn clear(&mut self) -> Box<dyn NyashBox> { self.output_file = None; self.emit_obj_out = None; self.plugin_paths = None; Box::new(VoidBox::new()) }
|
||||
pub fn set_output(&mut self, path: &str) -> Box<dyn NyashBox> {
|
||||
self.output_file = Some(path.to_string());
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn set_obj_out(&mut self, path: &str) -> Box<dyn NyashBox> {
|
||||
self.emit_obj_out = Some(path.to_string());
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn set_plugin_paths(&mut self, paths: &str) -> Box<dyn NyashBox> {
|
||||
self.plugin_paths = Some(paths.to_string());
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn clear(&mut self) -> Box<dyn NyashBox> {
|
||||
self.output_file = None;
|
||||
self.emit_obj_out = None;
|
||||
self.plugin_paths = None;
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
|
||||
/// Apply staged config to environment for CLI/runner consumption
|
||||
pub fn apply(&self) -> Box<dyn NyashBox> {
|
||||
if let Some(p) = &self.output_file { std::env::set_var("NYASH_AOT_OUT", p); }
|
||||
if let Some(p) = &self.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(|| "<none>".to_string()),
|
||||
self.emit_obj_out.clone().unwrap_or_else(|| "<none>".to_string()),
|
||||
self.plugin_paths.clone().unwrap_or_else(|| "<none>".to_string()),
|
||||
self.output_file
|
||||
.clone()
|
||||
.unwrap_or_else(|| "<none>".to_string()),
|
||||
self.emit_obj_out
|
||||
.clone()
|
||||
.unwrap_or_else(|| "<none>".to_string()),
|
||||
self.plugin_paths
|
||||
.clone()
|
||||
.unwrap_or_else(|| "<none>".to_string()),
|
||||
);
|
||||
StringBox::new(s)
|
||||
}
|
||||
|
||||
@ -2,39 +2,39 @@
|
||||
// Nyashの箱システムによる配列・リスト操作を提供します。
|
||||
// RwLockパターンで内部可変性を実現(Phase 9.75-B Arc<Mutex>削除)
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, BoxCore, BoxBase};
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::fmt::Display;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
pub struct ArrayBox {
|
||||
pub items: Arc<RwLock<Vec<Box<dyn NyashBox>>>>, // Arc追加
|
||||
pub items: Arc<RwLock<Vec<Box<dyn NyashBox>>>>, // 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<Box<dyn NyashBox>>) -> 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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
self.items.write().unwrap().push(item);
|
||||
Box::new(StringBox::new("ok"))
|
||||
}
|
||||
|
||||
|
||||
/// 最後の要素を取り出す
|
||||
pub fn pop(&self) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(idx_box) = index.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -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::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
|
||||
if item
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
.is_some()
|
||||
{
|
||||
return item.share_box();
|
||||
}
|
||||
item.clone_box()
|
||||
@ -72,7 +76,7 @@ impl ArrayBox {
|
||||
Box::new(StringBox::new("Error: get() requires integer index"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// インデックスで要素を設定
|
||||
pub fn set(&self, index: Box<dyn NyashBox>, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(idx_box) = index.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -92,7 +96,7 @@ impl ArrayBox {
|
||||
Box::new(StringBox::new("Error: set() requires integer index"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 要素を削除
|
||||
pub fn remove(&self, index: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(idx_box) = index.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -107,7 +111,7 @@ impl ArrayBox {
|
||||
Box::new(StringBox::new("Error: remove() requires integer index"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 指定された値のインデックスを検索
|
||||
pub fn indexOf(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let items = self.items.read().unwrap();
|
||||
@ -118,7 +122,7 @@ impl ArrayBox {
|
||||
}
|
||||
Box::new(IntegerBox::new(-1))
|
||||
}
|
||||
|
||||
|
||||
/// 指定された値が含まれているか確認
|
||||
pub fn contains(&self, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let items = self.items.read().unwrap();
|
||||
@ -129,13 +133,13 @@ impl ArrayBox {
|
||||
}
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
|
||||
|
||||
/// 配列を空にする
|
||||
pub fn clear(&self) -> Box<dyn NyashBox> {
|
||||
self.items.write().unwrap().clear();
|
||||
Box::new(StringBox::new("ok"))
|
||||
}
|
||||
|
||||
|
||||
/// 文字列結合
|
||||
pub fn join(&self, delimiter: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(sep_box) = delimiter.as_any().downcast_ref::<StringBox>() {
|
||||
@ -149,66 +153,78 @@ impl ArrayBox {
|
||||
Box::new(StringBox::new("Error: join() requires string separator"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 配列をソート(昇順)
|
||||
pub fn sort(&self) -> Box<dyn NyashBox> {
|
||||
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::<IntegerBox>(),
|
||||
b.as_any().downcast_ref::<IntegerBox>()
|
||||
b.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
return a_int.value.cmp(&b_int.value);
|
||||
}
|
||||
|
||||
|
||||
// Try FloatBox comparison
|
||||
if let (Some(a_float), Some(b_float)) = (
|
||||
a.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>(),
|
||||
b.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>()
|
||||
a.as_any()
|
||||
.downcast_ref::<crate::boxes::math_box::FloatBox>(),
|
||||
b.as_any()
|
||||
.downcast_ref::<crate::boxes::math_box::FloatBox>(),
|
||||
) {
|
||||
return a_float.value.partial_cmp(&b_float.value).unwrap_or(Ordering::Equal);
|
||||
return a_float
|
||||
.value
|
||||
.partial_cmp(&b_float.value)
|
||||
.unwrap_or(Ordering::Equal);
|
||||
}
|
||||
|
||||
|
||||
// Mixed numeric types
|
||||
if let (Some(a_int), Some(b_float)) = (
|
||||
a.as_any().downcast_ref::<IntegerBox>(),
|
||||
b.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>()
|
||||
b.as_any()
|
||||
.downcast_ref::<crate::boxes::math_box::FloatBox>(),
|
||||
) {
|
||||
return (a_int.value as f64).partial_cmp(&b_float.value).unwrap_or(Ordering::Equal);
|
||||
return (a_int.value as f64)
|
||||
.partial_cmp(&b_float.value)
|
||||
.unwrap_or(Ordering::Equal);
|
||||
}
|
||||
|
||||
|
||||
if let (Some(a_float), Some(b_int)) = (
|
||||
a.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>(),
|
||||
b.as_any().downcast_ref::<IntegerBox>()
|
||||
a.as_any()
|
||||
.downcast_ref::<crate::boxes::math_box::FloatBox>(),
|
||||
b.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
return a_float.value.partial_cmp(&(b_int.value as f64)).unwrap_or(Ordering::Equal);
|
||||
return a_float
|
||||
.value
|
||||
.partial_cmp(&(b_int.value as f64))
|
||||
.unwrap_or(Ordering::Equal);
|
||||
}
|
||||
|
||||
|
||||
// Fall back to string comparison
|
||||
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<dyn NyashBox> {
|
||||
let mut items = self.items.write().unwrap();
|
||||
items.reverse();
|
||||
Box::new(StringBox::new("ok"))
|
||||
}
|
||||
|
||||
|
||||
/// 部分配列を取得
|
||||
pub fn slice(&self, start: Box<dyn NyashBox>, end: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let items = self.items.read().unwrap();
|
||||
|
||||
|
||||
// Extract start and end indices
|
||||
let start_idx = if let Some(start_int) = start.as_any().downcast_ref::<IntegerBox>() {
|
||||
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::<IntegerBox>() {
|
||||
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<Box<dyn NyashBox>> = items[start_idx..end_idx]
|
||||
.iter()
|
||||
.map(|item| {
|
||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||
if item.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
|
||||
if item
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
.is_some()
|
||||
{
|
||||
return item.share_box();
|
||||
}
|
||||
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<Box<dyn NyashBox>> = items_guard.iter()
|
||||
let cloned_items: Vec<Box<dyn NyashBox>> = items_guard
|
||||
.iter()
|
||||
.map(|item| {
|
||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||
if item.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
|
||||
if item
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
.is_some()
|
||||
{
|
||||
return item.share_box();
|
||||
}
|
||||
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<std::any::TypeId> {
|
||||
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<String> = items.iter()
|
||||
let strings: Vec<String> = 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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 🎯 状態共有の核心実装
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<String> = items.iter()
|
||||
let strings: Vec<String> = 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::<ArrayBox>() {
|
||||
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)
|
||||
|
||||
@ -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<u8> {
|
||||
// シミュレーション用のダミーデータ
|
||||
(0..64).map(|i| ((i as f64 * 4.0).sin() * 128.0 + 128.0) as u8).collect()
|
||||
(0..64)
|
||||
.map(|i| ((i as f64 * 4.0).sin() * 128.0 + 128.0) as u8)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn get_waveform_data(&self) -> Vec<u8> {
|
||||
// シミュレーション用のダミーデータ
|
||||
(0..128).map(|i| ((i as f64 * 0.1).sin() * 64.0 + 128.0) as u8).collect()
|
||||
(0..128)
|
||||
.map(|i| ((i as f64 * 0.1).sin() * 64.0 + 128.0) as u8)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// オーディオコンテキストの状態を確認
|
||||
@ -260,19 +271,23 @@ impl BoxCore for AudioBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
|
||||
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
|
||||
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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::<AudioBox>() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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::<BoolBox>() {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<std::any::TypeId> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<RwLock<Vec<u8>>>, // Arc追加
|
||||
data: Arc<RwLock<Vec<u8>>>, // 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<u8>) -> 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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
// ArrayBoxから変換 - use crate::boxes::array::ArrayBox directly
|
||||
if let Some(array_box) = data.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
if let Some(array_box) = data
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::array::ArrayBox>()
|
||||
{
|
||||
let mut buffer = self.data.write().unwrap();
|
||||
let 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<dyn NyashBox> {
|
||||
let buffer = self.data.read().unwrap();
|
||||
@ -93,14 +99,14 @@ impl BufferBox {
|
||||
}
|
||||
Box::new(array)
|
||||
}
|
||||
|
||||
|
||||
/// 指定バイト数読み取る
|
||||
pub fn read(&self, count: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(count_int) = count.as_any().downcast_ref::<IntegerBox>() {
|
||||
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<u8> = 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<dyn NyashBox> {
|
||||
self.data.write().unwrap().clear();
|
||||
Box::new(StringBox::new("ok"))
|
||||
}
|
||||
|
||||
|
||||
/// データサイズを取得
|
||||
pub fn length(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.data.read().unwrap().len() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// 他のBufferBoxを追加
|
||||
pub fn append(&self, other: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(other_buffer) = other.as_any().downcast_ref::<BufferBox>() {
|
||||
@ -134,17 +140,17 @@ impl BufferBox {
|
||||
Box::new(StringBox::new("Error: append() requires BufferBox"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 部分データ取得
|
||||
pub fn slice(&self, start: Box<dyn NyashBox>, end: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let (Some(start_int), Some(end_int)) = (
|
||||
start.as_any().downcast_ref::<IntegerBox>(),
|
||||
end.as_any().downcast_ref::<IntegerBox>()
|
||||
end.as_any().downcast_ref::<IntegerBox>(),
|
||||
) {
|
||||
let data = self.data.read().unwrap();
|
||||
let 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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(other_buffer) = other.as_any().downcast_ref::<BufferBox>() {
|
||||
@ -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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
// 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<dyn NyashBox> {
|
||||
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<std::any::TypeId> {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
/// 🎯 状态共享的核心实现
|
||||
|
||||
/// 🎯 状态共享的核心实现
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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::<BufferBox>() {
|
||||
// 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();
|
||||
|
||||
@ -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<dyn FnMut(MouseEvent)>);
|
||||
|
||||
canvas.add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())
|
||||
canvas
|
||||
.add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())
|
||||
.unwrap_or_default();
|
||||
closure.forget(); // メモリリークを防ぐため通常は適切な管理が必要
|
||||
}
|
||||
@ -154,7 +152,8 @@ impl CanvasEventBox {
|
||||
callback.call0(&JsValue::NULL).unwrap_or_default();
|
||||
}) as Box<dyn FnMut(MouseEvent)>);
|
||||
|
||||
canvas.add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref())
|
||||
canvas
|
||||
.add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref())
|
||||
.unwrap_or_default();
|
||||
closure.forget();
|
||||
}
|
||||
@ -168,7 +167,8 @@ impl CanvasEventBox {
|
||||
callback.call0(&JsValue::NULL).unwrap_or_default();
|
||||
}) as Box<dyn FnMut(MouseEvent)>);
|
||||
|
||||
canvas.add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref())
|
||||
canvas
|
||||
.add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref())
|
||||
.unwrap_or_default();
|
||||
closure.forget();
|
||||
}
|
||||
@ -182,7 +182,8 @@ impl CanvasEventBox {
|
||||
callback.call0(&JsValue::NULL).unwrap_or_default();
|
||||
}) as Box<dyn FnMut(MouseEvent)>);
|
||||
|
||||
canvas.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref())
|
||||
canvas
|
||||
.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref())
|
||||
.unwrap_or_default();
|
||||
closure.forget();
|
||||
}
|
||||
@ -196,7 +197,8 @@ impl CanvasEventBox {
|
||||
callback.call0(&JsValue::NULL).unwrap_or_default();
|
||||
}) as Box<dyn FnMut(TouchEvent)>);
|
||||
|
||||
canvas.add_event_listener_with_callback("touchstart", closure.as_ref().unchecked_ref())
|
||||
canvas
|
||||
.add_event_listener_with_callback("touchstart", closure.as_ref().unchecked_ref())
|
||||
.unwrap_or_default();
|
||||
closure.forget();
|
||||
}
|
||||
@ -210,7 +212,8 @@ impl CanvasEventBox {
|
||||
callback.call0(&JsValue::NULL).unwrap_or_default();
|
||||
}) as Box<dyn FnMut(KeyboardEvent)>);
|
||||
|
||||
window.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())
|
||||
window
|
||||
.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())
|
||||
.unwrap_or_default();
|
||||
closure.forget();
|
||||
}
|
||||
@ -252,19 +255,19 @@ impl BoxCore for CanvasEventBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
|
||||
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
|
||||
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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::<CanvasEventBox>() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<dyn FnMut(f64)>);
|
||||
|
||||
let id = self.timer.request_animation_frame(closure.as_ref().unchecked_ref());
|
||||
let id = self
|
||||
.timer
|
||||
.request_animation_frame(closure.as_ref().unchecked_ref());
|
||||
self.animation_id = Some(id);
|
||||
|
||||
|
||||
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<dyn FnMut(f64)>);
|
||||
|
||||
let id = self.timer.request_animation_frame(closure.as_ref().unchecked_ref());
|
||||
let id = self
|
||||
.timer
|
||||
.request_animation_frame(closure.as_ref().unchecked_ref());
|
||||
self.animation_id = Some(id);
|
||||
|
||||
|
||||
closure.forget();
|
||||
}
|
||||
|
||||
@ -262,19 +270,23 @@ impl BoxCore for CanvasLoopBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
|
||||
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
|
||||
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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::<CanvasLoopBox>() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<std::any::TypeId> {
|
||||
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::<ConsoleBox>())
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"ConsoleBox"
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<std::any::TypeId> {
|
||||
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::<ConsoleBox>())
|
||||
}
|
||||
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"ConsoleBox"
|
||||
}
|
||||
|
||||
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Display implementations for both WASM and non-WASM versions
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
impl Display for ConsoleBox {
|
||||
|
||||
@ -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<Box<dyn NyashBox>, RuntimeError> {
|
||||
pub fn track_box(
|
||||
&self,
|
||||
box_value: &dyn NyashBox,
|
||||
name: &str,
|
||||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let enabled = self.tracking_enabled.read().unwrap();
|
||||
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<Box<dyn NyashBox>, 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<Box<dyn NyashBox>, 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<Box<dyn NyashBox>, RuntimeError> {
|
||||
pub fn watch(
|
||||
&self,
|
||||
box_value: &dyn NyashBox,
|
||||
name: &str,
|
||||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let value_str = box_value.to_string_box().value;
|
||||
let 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<String, usize> = 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<String>) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
pub fn trace_call(
|
||||
&self,
|
||||
function_name: &str,
|
||||
args: Vec<String>,
|
||||
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let mut stack = self.call_stack.write().unwrap();
|
||||
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<Box<dyn NyashBox>, 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<Box<dyn NyashBox>, 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<Box<dyn NyashBox>, 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<Box<dyn NyashBox>, 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<std::any::TypeId> {
|
||||
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::<DebugBox>() {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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<dyn NyashBox> {
|
||||
let setb = |k: &str, v: bool| { if v { std::env::set_var(k, "1"); } else { std::env::remove_var(k); } };
|
||||
let setb = |k: &str, v: bool| {
|
||||
if v {
|
||||
std::env::set_var(k, "1");
|
||||
} else {
|
||||
std::env::remove_var(k);
|
||||
}
|
||||
};
|
||||
setb("NYASH_JIT_EVENTS", self.jit_events);
|
||||
setb("NYASH_JIT_EVENTS_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<std::any::TypeId> { 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<std::any::TypeId> {
|
||||
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::<DebugConfigBox>()) }
|
||||
fn type_name(&self) -> &'static str { "DebugConfigBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone(), ..self.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new("DebugConfigBox".to_string()) }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<DebugConfigBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"DebugConfigBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("DebugConfigBox".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<T: Any + Send + Sync + 'static>(&mut self, state: T) {
|
||||
*self.app_state.write().unwrap() = Box::new(state);
|
||||
}
|
||||
|
||||
|
||||
/// 更新関数を設定
|
||||
pub fn set_update_fn<F>(&mut self, f: F)
|
||||
where
|
||||
F: Fn(&mut Box<dyn Any + Send + Sync>, &egui::Context) + Send + Sync + 'static
|
||||
pub fn set_update_fn<F>(&mut self, f: F)
|
||||
where
|
||||
F: Fn(&mut Box<dyn Any + Send + Sync>, &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<std::any::TypeId> {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
if let Some(other_egui) = other.as_any().downcast_ref::<EguiBox>() {
|
||||
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<dyn Any + Send + Sync>));
|
||||
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)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Self> {
|
||||
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<String> {
|
||||
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<dyn NyashBox> {
|
||||
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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
use std::path::Path;
|
||||
Box::new(BoolBox::new(Path::new(&self.path).exists()))
|
||||
}
|
||||
|
||||
|
||||
/// ファイルを削除
|
||||
pub fn delete(&self) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
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<std::any::TypeId> {
|
||||
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<dyn NyashBox> {
|
||||
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::<FileBox>() {
|
||||
BoolBox::new(self.path == other_file.path)
|
||||
|
||||
@ -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<String>, body: Vec<ASTNode>) -> Self {
|
||||
Self { params, body, env: ClosureEnv::new(), base: BoxBase::new() }
|
||||
Self {
|
||||
params,
|
||||
body,
|
||||
env: ClosureEnv::new(),
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
pub fn with_env(params: Vec<String>, body: Vec<ASTNode>, env: ClosureEnv) -> Self {
|
||||
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<std::any::TypeId> { self.base.parent_type_id }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "FunctionBox(params={}, body={})", self.params.len(), self.body.len())
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
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<dyn NyashBox> { Box::new(self.clone()) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new(format!("FunctionBox(params={}, captures={}, body={})", self.params.len(), self.env.captures.len(), self.body.len())) }
|
||||
fn type_name(&self) -> &'static str { "FunctionBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(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::<FunctionBox>() {
|
||||
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<String, Box<dyn NyashBox>> = HashMap::new();
|
||||
for (k, v) in &self.captures { captures.insert(k.clone(), v.clone_box()); }
|
||||
for (k, v) in &self.captures {
|
||||
captures.insert(k.clone(), v.clone_box());
|
||||
}
|
||||
Self { me_value, captures }
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<dyn NyashBox>) {
|
||||
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<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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::<NyashFutureBox>() {
|
||||
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<std::any::TypeId> {
|
||||
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<bool> {
|
||||
self.inner.upgrade().map(|arc| arc.state.lock().unwrap().ready)
|
||||
self.inner
|
||||
.upgrade()
|
||||
.map(|arc| arc.state.lock().unwrap().ready)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<dyn NyashBox> {
|
||||
let setb = |k: &str, v: bool| { if v { std::env::set_var(k, "1"); } else { std::env::remove_var(k); } };
|
||||
let setb = |k: &str, v: bool| {
|
||||
if v {
|
||||
std::env::set_var(k, "1");
|
||||
} else {
|
||||
std::env::remove_var(k);
|
||||
}
|
||||
};
|
||||
setb("NYASH_GC_COUNTING", self.counting);
|
||||
setb("NYASH_GC_TRACE", self.trace);
|
||||
setb("NYASH_GC_BARRIER_STRICT", self.barrier_strict);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn summary(&self) -> Box<dyn NyashBox> {
|
||||
let s = format!("counting={} trace={} barrier_strict={}", self.counting, self.trace, self.barrier_strict);
|
||||
let s = format!(
|
||||
"counting={} trace={} barrier_strict={}",
|
||||
self.counting, self.trace, self.barrier_strict
|
||||
);
|
||||
Box::new(StringBox::new(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for GcConfigBox {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 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<std::any::TypeId> {
|
||||
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::<GcConfigBox>()) }
|
||||
fn type_name(&self) -> &'static str { "GcConfigBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new(self.summary().to_string_box().value) }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<GcConfigBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"GcConfigBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(self.summary().to_string_box().value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new("HTTP support is currently disabled"))
|
||||
}
|
||||
|
||||
|
||||
/// HTTP POSTリクエスト(スタブ)
|
||||
pub fn post(&self, _url: Box<dyn NyashBox>, _body: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new("HTTP support is currently disabled"))
|
||||
}
|
||||
|
||||
|
||||
/// HTTP PUT リクエスト(スタブ)
|
||||
pub fn put(&self, _url: Box<dyn NyashBox>, _body: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new("HTTP support is currently disabled"))
|
||||
}
|
||||
|
||||
|
||||
/// HTTP DELETE リクエスト(スタブ)
|
||||
pub fn delete(&self, _url: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new("HTTP support is currently disabled"))
|
||||
}
|
||||
|
||||
|
||||
/// ヘッダー付きHTTPリクエスト(スタブ)
|
||||
pub fn request(&self, _method: Box<dyn NyashBox>, _url: Box<dyn NyashBox>, _options: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
pub fn request(
|
||||
&self,
|
||||
_method: Box<dyn NyashBox>,
|
||||
_url: Box<dyn NyashBox>,
|
||||
_options: Box<dyn NyashBox>,
|
||||
) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new("HTTP support is currently disabled"))
|
||||
}
|
||||
}
|
||||
@ -50,7 +55,7 @@ impl NyashBox for HttpClientBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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::<HttpClientBox>() {
|
||||
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<std::any::TypeId> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<dyn NyashBox>) -> 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<dyn NyashBox> {
|
||||
Box::new(StringBox::new(self.method.clone()))
|
||||
}
|
||||
|
||||
|
||||
/// URL パス取得
|
||||
pub fn get_path(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(self.path.clone()))
|
||||
}
|
||||
|
||||
|
||||
/// クエリ文字列取得
|
||||
pub fn get_query_string(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(StringBox::new(self.query_string.clone()))
|
||||
}
|
||||
|
||||
|
||||
/// 特定ヘッダー取得
|
||||
pub fn get_header(&self, name: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
let headers_map = MapBox::new();
|
||||
@ -160,31 +160,29 @@ impl HTTPRequestBox {
|
||||
}
|
||||
Box::new(headers_map)
|
||||
}
|
||||
|
||||
|
||||
/// ヘッダー存在確認
|
||||
pub fn has_header(&self, name: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(StringBox::new(self.body.clone()))
|
||||
}
|
||||
|
||||
|
||||
/// Content-Type取得
|
||||
pub fn get_content_type(&self) -> Box<dyn NyashBox> {
|
||||
self.get_header(Box::new(StringBox::new("content-type".to_string())))
|
||||
}
|
||||
|
||||
|
||||
/// Content-Length取得
|
||||
pub fn get_content_length(&self) -> Box<dyn NyashBox> {
|
||||
match self.headers.get("content-length") {
|
||||
Some(length_str) => {
|
||||
match length_str.parse::<i64>() {
|
||||
Ok(length) => Box::new(IntegerBox::new(length)),
|
||||
Err(_) => Box::new(IntegerBox::new(0)),
|
||||
}
|
||||
Some(length_str) => match length_str.parse::<i64>() {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<std::any::TypeId> {
|
||||
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<dyn NyashBox>, message: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
pub fn set_status(
|
||||
&self,
|
||||
code: Box<dyn NyashBox>,
|
||||
message: Box<dyn NyashBox>,
|
||||
) -> Box<dyn NyashBox> {
|
||||
// Note: This would need interior mutability for actual mutation
|
||||
// For now, this is a placeholder for the API structure
|
||||
let _code_val = code.to_string_box().value.parse::<i32>().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<dyn NyashBox>, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
pub fn set_header(
|
||||
&self,
|
||||
name: Box<dyn NyashBox>,
|
||||
value: Box<dyn NyashBox>,
|
||||
) -> Box<dyn NyashBox> {
|
||||
let _name_str = name.to_string_box().value;
|
||||
let _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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
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<dyn NyashBox>) -> 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<dyn NyashBox>) -> 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 = "<html><body><h1>404 - Not Found</h1></body></html>".to_string();
|
||||
response
|
||||
}
|
||||
@ -378,15 +403,19 @@ impl NyashBox for HTTPResponseBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<std::any::TypeId> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<String, Box<dyn NyashBox>> = routes_guard.iter()
|
||||
let routes_val: HashMap<String, Box<dyn NyashBox>> = routes_guard
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone_box()))
|
||||
.collect();
|
||||
|
||||
|
||||
let middleware_guard = self.middleware.read().unwrap();
|
||||
let middleware_val: Vec<Box<dyn NyashBox>> = middleware_guard.iter()
|
||||
let middleware_val: Vec<Box<dyn NyashBox>> = middleware_guard
|
||||
.iter()
|
||||
.map(|item| item.clone_box())
|
||||
.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<Box<dyn NyashBox>> = connections_guard.iter()
|
||||
let connections_val: Vec<Box<dyn NyashBox>> = 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<dyn NyashBox>, port: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
// 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::<SocketBox>() {
|
||||
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<String, Box<dyn NyashBox>> = routes_guard.iter()
|
||||
let routes_clone: HashMap<String, Box<dyn NyashBox>> = routes_guard
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone_box()))
|
||||
.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<dyn NyashBox> {
|
||||
*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<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
let timeout_val = seconds.to_string_box().value.parse::<u64>().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<String, Box<dyn NyashBox>>
|
||||
client_socket: SocketBox,
|
||||
routes: HashMap<String, Box<dyn NyashBox>>,
|
||||
) {
|
||||
// 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<dyn NyashBox> {
|
||||
let connections = self.active_connections.read().unwrap();
|
||||
Box::new(IntegerBox::new(connections.len() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// サーバー状態取得
|
||||
pub fn is_running(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(BoolBox::new(*self.running.read().unwrap()))
|
||||
@ -367,7 +383,7 @@ impl NyashBox for HTTPServerBox {
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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<std::any::TypeId> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,29 +1,29 @@
|
||||
/*! 📦 IntentBox - Structured Message Box
|
||||
*
|
||||
*
|
||||
* ## 📝 概要
|
||||
* IntentBoxは構造化メッセージを表現するBoxです。
|
||||
* P2P通信において、メッセージの種類(name)と内容(payload)を
|
||||
* 明確に分離して管理します。
|
||||
*
|
||||
*
|
||||
* ## 🏗️ 設計
|
||||
* - **name**: メッセージの種類 ("chat.message", "file.share"等)
|
||||
* - **payload**: JSON形式の任意データ
|
||||
* - **Arc<Mutex>**: 他の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<dyn NyashBox> {
|
||||
let name = self.name.read().unwrap().clone();
|
||||
Box::new(StringBox::new(name))
|
||||
}
|
||||
|
||||
|
||||
/// ペイロードを取得
|
||||
pub fn get_payload(&self) -> Box<dyn NyashBox> {
|
||||
let payload = self.payload.read().unwrap().clone();
|
||||
Box::new(StringBox::new(payload.to_string()))
|
||||
}
|
||||
|
||||
|
||||
/// ペイロードを更新
|
||||
pub fn set_payload(&self, payload: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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::<IntentBox>() {
|
||||
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<std::any::TypeId> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<dyn NyashBox> {
|
||||
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<Box<dyn NyashBox>, 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<Box<dyn NyashBox>, 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<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap();
|
||||
Box::new(IntegerBox::new(cfg.threshold.map(|v| v as i64).unwrap_or(0)))
|
||||
Box::new(IntegerBox::new(
|
||||
cfg.threshold.map(|v| v as i64).unwrap_or(0),
|
||||
))
|
||||
}
|
||||
pub fn to_json(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap();
|
||||
@ -88,20 +108,49 @@ impl JitConfigBox {
|
||||
}
|
||||
pub fn from_json(&self, s: &str) -> Result<Box<dyn NyashBox>, 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<dyn NyashBox> {
|
||||
@ -124,20 +173,41 @@ impl JitConfigBox {
|
||||
}
|
||||
|
||||
impl BoxCore for JitConfigBox {
|
||||
fn box_id(&self) -> u64 { self.base.id }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 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<std::any::TypeId> {
|
||||
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::<JitConfigBox>()) }
|
||||
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::<JitConfigBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitConfigBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap().clone();
|
||||
Box::new(JitConfigBox { base: self.base.clone(), config: RwLock::new(cfg) })
|
||||
Box::new(JitConfigBox {
|
||||
base: self.base.clone(),
|
||||
config: RwLock::new(cfg),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
}
|
||||
|
||||
@ -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<std::any::TypeId> { 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<std::any::TypeId> {
|
||||
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::<JitEventsBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitEventsBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("JitEventsBox")
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitEventsBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitEventsBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl JitEventsBox {
|
||||
@ -28,14 +58,17 @@ impl JitEventsBox {
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn enable(&self, on: bool) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
let extra = serde_json::from_str::<serde_json::Value>(note_json).unwrap_or_else(|_| serde_json::json!({"note": note_json}));
|
||||
let extra = serde_json::from_str::<serde_json::Value>(note_json)
|
||||
.unwrap_or_else(|_| serde_json::json!({"note": note_json}));
|
||||
crate::jit::events::emit(kind, function, None, None, extra);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<std::any::TypeId> { 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<std::any::TypeId> {
|
||||
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::<JitHostcallRegistryBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitHostcallRegistryBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitHostcallRegistryBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitHostcallRegistryBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl JitHostcallRegistryBox {
|
||||
pub fn add_readonly(&self, sym: &str) -> Box<dyn NyashBox> { crate::jit::hostcall_registry::add_readonly(sym); Box::new(VoidBox::new()) }
|
||||
pub fn add_mutating(&self, sym: &str) -> Box<dyn NyashBox> { crate::jit::hostcall_registry::add_mutating(sym); Box::new(VoidBox::new()) }
|
||||
pub fn set_from_csv(&self, ro_csv: &str, mu_csv: &str) -> Box<dyn NyashBox> { crate::jit::hostcall_registry::set_from_csv(ro_csv, mu_csv); Box::new(VoidBox::new()) }
|
||||
pub fn add_readonly(&self, sym: &str) -> Box<dyn NyashBox> {
|
||||
crate::jit::hostcall_registry::add_readonly(sym);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn add_mutating(&self, sym: &str) -> Box<dyn NyashBox> {
|
||||
crate::jit::hostcall_registry::add_mutating(sym);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn set_from_csv(&self, ro_csv: &str, mu_csv: &str) -> Box<dyn NyashBox> {
|
||||
crate::jit::hostcall_registry::set_from_csv(ro_csv, mu_csv);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn add_signature(&self, sym: &str, args_csv: &str, ret_str: &str) -> Box<dyn NyashBox> {
|
||||
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"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<std::any::TypeId> { 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<std::any::TypeId> {
|
||||
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::<JitPolicyBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitPolicyBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitPolicyBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitPolicyBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
// Methods (exposed via VM dispatch):
|
||||
@ -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<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
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())
|
||||
|
||||
@ -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<dyn NyashBox> {
|
||||
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<std::any::TypeId> { 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<std::any::TypeId> {
|
||||
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::<JitStatsBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitStatsBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(self.clone()) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
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::<JitStatsBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitStatsBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<std::any::TypeId> { 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<std::any::TypeId> {
|
||||
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::<JitStrictBox>()) }
|
||||
fn type_name(&self) -> &'static str { "JitStrictBox" }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> { Box::new(Self { base: self.base.clone() }) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("JitStrictBox".to_string())
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitStrictBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitStrictBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl JitStrictBox {
|
||||
@ -27,8 +57,12 @@ impl JitStrictBox {
|
||||
pub fn enable(&self, on: bool) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
if on { std::env::set_var("NYASH_JIT_ONLY", "1"); } else { std::env::remove_var("NYASH_JIT_ONLY"); }
|
||||
if on {
|
||||
std::env::set_var("NYASH_JIT_ONLY", "1");
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JIT_ONLY");
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn set_handle_only(&self, on: bool) -> Box<dyn NyashBox> {
|
||||
if on { std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1"); } else { std::env::remove_var("NYASH_JIT_ARGS_HANDLE_ONLY"); }
|
||||
if on {
|
||||
std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1");
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JIT_ARGS_HANDLE_ONLY");
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
|
||||
@ -60,4 +102,3 @@ impl JitStrictBox {
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<Self, Error> {
|
||||
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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(StringBox::new(&self.to_string()))
|
||||
}
|
||||
|
||||
|
||||
/// 値取得
|
||||
pub fn get(&self, key: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox>, new_value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
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<std::any::TypeId> {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 仮実装: clone_boxと同じ(後で修正)
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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::<JSONBox>() {
|
||||
let self_value = self.value.read().unwrap();
|
||||
@ -230,10 +228,7 @@ fn json_value_to_nyash_box(value: &Value) -> Box<dyn NyashBox> {
|
||||
Value::Object(obj) => {
|
||||
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<dyn NyashBox> {
|
||||
|
||||
/// NyashBox を JSON Value に変換
|
||||
fn nyash_box_to_json_value(value: Box<dyn NyashBox>) -> Value {
|
||||
if value.as_any().downcast_ref::<crate::boxes::null_box::NullBox>().is_some() {
|
||||
if value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::null_box::NullBox>()
|
||||
.is_some()
|
||||
{
|
||||
Value::Null
|
||||
} else if let Some(bool_box) = value.as_any().downcast_ref::<BoolBox>() {
|
||||
Value::Bool(bool_box.value)
|
||||
@ -259,7 +258,8 @@ fn nyash_box_to_json_value(value: Box<dyn NyashBox>) -> Value {
|
||||
Value::String(string_box.value.clone())
|
||||
} else if let Some(array_box) = value.as_any().downcast_ref::<ArrayBox>() {
|
||||
let items = array_box.items.read().unwrap();
|
||||
let arr: Vec<Value> = items.iter()
|
||||
let arr: Vec<Value> = items
|
||||
.iter()
|
||||
.map(|item| nyash_box_to_json_value(item.clone_box()))
|
||||
.collect();
|
||||
Value::Array(arr)
|
||||
|
||||
@ -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<RwLock>使用)
|
||||
@ -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<RwLock<HashMap<String, Box<dyn NyashBox>>>>, // Arc追加
|
||||
data: Arc<RwLock<HashMap<String, Box<dyn NyashBox>>>>, // 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<dyn NyashBox>, value: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
|
||||
if value
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
.is_some()
|
||||
{
|
||||
return value.share_box();
|
||||
}
|
||||
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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
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<dyn NyashBox> {
|
||||
let keys: Vec<String> = self.data.read().unwrap().keys().cloned().collect();
|
||||
@ -170,10 +176,13 @@ impl MapBox {
|
||||
}
|
||||
Box::new(array)
|
||||
}
|
||||
|
||||
|
||||
/// 全ての値を取得
|
||||
pub fn values(&self) -> Box<dyn NyashBox> {
|
||||
let values: Vec<Box<dyn NyashBox>> = self.data.read().unwrap()
|
||||
let values: Vec<Box<dyn NyashBox>> = self
|
||||
.data
|
||||
.read()
|
||||
.unwrap()
|
||||
.values()
|
||||
.map(|v| v.clone_box())
|
||||
.collect();
|
||||
@ -183,45 +192,46 @@ impl MapBox {
|
||||
}
|
||||
Box::new(array)
|
||||
}
|
||||
|
||||
|
||||
/// サイズを取得
|
||||
pub fn size(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(IntegerBox::new(self.data.read().unwrap().len() as i64))
|
||||
}
|
||||
|
||||
|
||||
/// 全てクリア
|
||||
pub fn clear(&self) -> Box<dyn NyashBox> {
|
||||
self.data.write().unwrap().clear();
|
||||
Box::new(StringBox::new("Map cleared"))
|
||||
}
|
||||
|
||||
|
||||
/// 各要素に対して関数を実行
|
||||
pub fn forEach(&self, _callback: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
// 簡易実装:callbackの実行はスキップ
|
||||
let count = self.data.read().unwrap().len();
|
||||
Box::new(StringBox::new(&format!("Iterated over {} items", count)))
|
||||
}
|
||||
|
||||
|
||||
/// JSON文字列に変換
|
||||
pub fn toJSON(&self) -> Box<dyn NyashBox> {
|
||||
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::<IntegerBox>().is_some()
|
||||
|| value.as_any().downcast_ref::<BoolBox>().is_some() {
|
||||
let formatted_value = if value.as_any().downcast_ref::<IntegerBox>().is_some()
|
||||
|| value.as_any().downcast_ref::<BoolBox>().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<HashMap<String, Box<dyn NyashBox>>> {
|
||||
&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<String, Box<dyn NyashBox>> = data_guard.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone_box())) // 要素もディープコピー
|
||||
let cloned_data: HashMap<String, Box<dyn NyashBox>> = 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<std::any::TypeId> {
|
||||
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<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
|
||||
/// 🎯 状態共有の核心実装
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
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::<MapBox>() {
|
||||
// 同じインスタンスかチェック(データの共有を考慮)
|
||||
@ -299,7 +311,6 @@ impl NyashBox for MapBox {
|
||||
BoolBox::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Display for MapBox {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user