Refactor (light): move VM value ops to backend/vm_values.rs; add backend/vm_boxcall.rs with call_box_method_impl and rewire call_unified_method. Update CURRENT_TASK for refactor start.
This commit is contained in:
@ -116,6 +116,17 @@ tools/ci_check_golden.sh # 代表ケースのMIR含有チェック
|
|||||||
- 差分検証: `execute_instruction`/`execute_binop` に挿入したデバッグ出力が未出力→実行経路の相違が濃厚。
|
- 差分検証: `execute_instruction`/`execute_binop` に挿入したデバッグ出力が未出力→実行経路の相違が濃厚。
|
||||||
- 対応: 実行バイナリの署名チェックとコードパスの網羅的ログ追加でルート確定→修正。
|
- 対応: 実行バイナリの署名チェックとコードパスの網羅的ログ追加でルート確定→修正。
|
||||||
|
|
||||||
|
### ✅ 小タスク完了(2025-08-25 深夜)
|
||||||
|
- Verifier: Barrierの軽い文脈診断を追加(`NYASH_VERIFY_BARRIER_STRICT=1`で有効)。
|
||||||
|
- ResultBox移行TODOを追加(`docs/development/current/RESULTBOX_MIGRATION_TODO.md`)。
|
||||||
|
- VM: 旧`box_trait::ResultBox`扱いのデプリケーション警告を最小抑制(完全移行までの暫定)。
|
||||||
|
|
||||||
|
### ▶ リファクタリング開始(控えめ)
|
||||||
|
- 方針: 肥大化防止のため`src/backend/vm.rs`を2分割のみ実施。
|
||||||
|
- `vm_values.rs`: `execute_binary_op`/`execute_unary_op`/`execute_compare_op` を移動。
|
||||||
|
- `vm_boxcall.rs`: `call_box_method` を移動(`call_unified_method`は現状のまま)。
|
||||||
|
- 影響最小・挙動非変更で、可読性と責務分離を先行する。
|
||||||
|
|
||||||
### ⚠️ MIRビルダー引き継ぎポイント(ChatGPT5さんへ)
|
### ⚠️ MIRビルダー引き継ぎポイント(ChatGPT5さんへ)
|
||||||
- **状況**: MIRビルダーのモジュール化完了(Phase 1-8コミット済み)
|
- **状況**: MIRビルダーのモジュール化完了(Phase 1-8コミット済み)
|
||||||
- **問題**: MIR命令構造の変更により、expressions.rsでエラー発生
|
- **問題**: MIR命令構造の変更により、expressions.rsでエラー発生
|
||||||
|
|||||||
19
docs/development/current/RESULTBOX_MIGRATION_TODO.md
Normal file
19
docs/development/current/RESULTBOX_MIGRATION_TODO.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
## ResultBox Migration TODO (Phase 9.78h follow-up)
|
||||||
|
|
||||||
|
Goal: fully migrate from legacy `box_trait::ResultBox` to `boxes::result::NyashResultBox` (aka `boxes::ResultBox`).
|
||||||
|
|
||||||
|
### Current usages (grep snapshot)
|
||||||
|
- src/backend/vm.rs
|
||||||
|
- Handles both new `NyashResultBox` and legacy `box_trait::ResultBox` for `.is_ok/.get_value/.get_error` (deprecation suppressed)。
|
||||||
|
|
||||||
|
### Proposed steps (small, incremental)
|
||||||
|
- Step 1: Keep dual handling but gate legacy path with feature flag or cfg for deprecation-only builds(任意)。
|
||||||
|
- Step 2: Audit call sites that construct legacy ResultBox; replace with `boxes::result::NyashResultBox` constructors。
|
||||||
|
- Step 3: Remove legacy path from VM once no legacy constructors remain。
|
||||||
|
- Step 4: Delete/Archive legacy `box_trait::ResultBox`(テスト緑後)。
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
- New API already aliased: `pub type ResultBox = NyashResultBox;` so external references may transparently resolve after migration。
|
||||||
|
- Keep migration scoped: do not mix with unrelated refactors。
|
||||||
|
|
||||||
|
Last updated: 2025-08-25
|
||||||
@ -5,6 +5,8 @@
|
|||||||
pub mod vm;
|
pub mod vm;
|
||||||
pub mod vm_phi;
|
pub mod vm_phi;
|
||||||
pub mod vm_instructions;
|
pub mod vm_instructions;
|
||||||
|
pub mod vm_values;
|
||||||
|
pub mod vm_boxcall;
|
||||||
|
|
||||||
#[cfg(feature = "wasm-backend")]
|
#[cfg(feature = "wasm-backend")]
|
||||||
pub mod wasm;
|
pub mod wasm;
|
||||||
|
|||||||
@ -626,290 +626,6 @@ impl VM {
|
|||||||
self.values[index] = Some(value);
|
self.values[index] = Some(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute binary operation
|
|
||||||
pub(super) fn execute_binary_op(&self, op: &BinaryOp, left: &VMValue, right: &VMValue) -> Result<VMValue, VMError> {
|
|
||||||
// Fast path: logical AND/OR accept any truthy via as_bool (supports BoxRef BoolBox/Void coercions)
|
|
||||||
if matches!(*op, BinaryOp::And | BinaryOp::Or) {
|
|
||||||
let l = left.as_bool()?;
|
|
||||||
let r = right.as_bool()?;
|
|
||||||
return Ok(VMValue::Bool(match *op { BinaryOp::And => l && r, BinaryOp::Or => l || r, _ => unreachable!() }));
|
|
||||||
}
|
|
||||||
|
|
||||||
match (left, right) {
|
|
||||||
// Logical booleans (native)
|
|
||||||
(VMValue::Bool(l), VMValue::Bool(r)) => {
|
|
||||||
let result = match op {
|
|
||||||
BinaryOp::And => *l && *r,
|
|
||||||
BinaryOp::Or => *l || *r,
|
|
||||||
_ => return Err(VMError::TypeError(format!(
|
|
||||||
"Unsupported boolean operation: {:?}", op
|
|
||||||
))),
|
|
||||||
};
|
|
||||||
Ok(VMValue::Bool(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logical booleans (BoxRef BoolBox)
|
|
||||||
(VMValue::BoxRef(lb), VMValue::BoxRef(rb))
|
|
||||||
if lb.as_any().downcast_ref::<BoolBox>().is_some()
|
|
||||||
&& rb.as_any().downcast_ref::<BoolBox>().is_some() =>
|
|
||||||
{
|
|
||||||
let l = lb.as_any().downcast_ref::<BoolBox>().unwrap().value;
|
|
||||||
let r = rb.as_any().downcast_ref::<BoolBox>().unwrap().value;
|
|
||||||
let result = match op {
|
|
||||||
BinaryOp::And => l && r,
|
|
||||||
BinaryOp::Or => l || r,
|
|
||||||
_ => return Err(VMError::TypeError(format!(
|
|
||||||
"Unsupported boolean BoxRef operation: {:?}", op
|
|
||||||
))),
|
|
||||||
};
|
|
||||||
Ok(VMValue::Bool(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mixed boolean forms (BoxRef BoolBox with native Bool)
|
|
||||||
(VMValue::BoxRef(lb), VMValue::Bool(r)) if lb.as_any().downcast_ref::<BoolBox>().is_some() => {
|
|
||||||
let l = lb.as_any().downcast_ref::<BoolBox>().unwrap().value;
|
|
||||||
let result = match op {
|
|
||||||
BinaryOp::And => l && *r,
|
|
||||||
BinaryOp::Or => l || *r,
|
|
||||||
_ => return Err(VMError::TypeError(format!(
|
|
||||||
"Unsupported boolean operation: {:?}", op
|
|
||||||
))),
|
|
||||||
};
|
|
||||||
Ok(VMValue::Bool(result))
|
|
||||||
}
|
|
||||||
(VMValue::Bool(l), VMValue::BoxRef(rb)) if rb.as_any().downcast_ref::<BoolBox>().is_some() => {
|
|
||||||
let r = rb.as_any().downcast_ref::<BoolBox>().unwrap().value;
|
|
||||||
let result = match op {
|
|
||||||
BinaryOp::And => *l && r,
|
|
||||||
BinaryOp::Or => *l || r,
|
|
||||||
_ => return Err(VMError::TypeError(format!(
|
|
||||||
"Unsupported boolean operation: {:?}", op
|
|
||||||
))),
|
|
||||||
};
|
|
||||||
Ok(VMValue::Bool(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arithmetic with BoxRef(IntegerBox)
|
|
||||||
(VMValue::BoxRef(li), VMValue::BoxRef(ri))
|
|
||||||
if li.as_any().downcast_ref::<IntegerBox>().is_some()
|
|
||||||
&& ri.as_any().downcast_ref::<IntegerBox>().is_some() =>
|
|
||||||
{
|
|
||||||
let l = li.as_any().downcast_ref::<IntegerBox>().unwrap().value;
|
|
||||||
let r = ri.as_any().downcast_ref::<IntegerBox>().unwrap().value;
|
|
||||||
let res = match op {
|
|
||||||
BinaryOp::Add => l + r,
|
|
||||||
BinaryOp::Sub => l - r,
|
|
||||||
BinaryOp::Mul => l * r,
|
|
||||||
BinaryOp::Div => {
|
|
||||||
if r == 0 { return Err(VMError::DivisionByZero); }
|
|
||||||
l / r
|
|
||||||
}
|
|
||||||
_ => return Err(VMError::InvalidInstruction(format!(
|
|
||||||
"Unsupported integer BoxRef operation: {:?}", op
|
|
||||||
))),
|
|
||||||
};
|
|
||||||
Ok(VMValue::Integer(res))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mixed Integer forms: BoxRef<IntegerBox> with native Integer
|
|
||||||
(VMValue::BoxRef(li), VMValue::Integer(r)) if li.as_any().downcast_ref::<IntegerBox>().is_some() => {
|
|
||||||
let l = li.as_any().downcast_ref::<IntegerBox>().unwrap().value;
|
|
||||||
let res = match op {
|
|
||||||
BinaryOp::Add => l + *r,
|
|
||||||
BinaryOp::Sub => l - *r,
|
|
||||||
BinaryOp::Mul => l * *r,
|
|
||||||
BinaryOp::Div => {
|
|
||||||
if *r == 0 { return Err(VMError::DivisionByZero); }
|
|
||||||
l / *r
|
|
||||||
}
|
|
||||||
_ => return Err(VMError::InvalidInstruction(format!(
|
|
||||||
"Unsupported integer operation: {:?}", op
|
|
||||||
))),
|
|
||||||
};
|
|
||||||
Ok(VMValue::Integer(res))
|
|
||||||
}
|
|
||||||
(VMValue::Integer(l), VMValue::BoxRef(ri)) if ri.as_any().downcast_ref::<IntegerBox>().is_some() => {
|
|
||||||
let r = ri.as_any().downcast_ref::<IntegerBox>().unwrap().value;
|
|
||||||
let res = match op {
|
|
||||||
BinaryOp::Add => *l + r,
|
|
||||||
BinaryOp::Sub => *l - r,
|
|
||||||
BinaryOp::Mul => *l * r,
|
|
||||||
BinaryOp::Div => {
|
|
||||||
if r == 0 { return Err(VMError::DivisionByZero); }
|
|
||||||
*l / r
|
|
||||||
}
|
|
||||||
_ => return Err(VMError::InvalidInstruction(format!(
|
|
||||||
"Unsupported integer operation: {:?}", op
|
|
||||||
))),
|
|
||||||
};
|
|
||||||
Ok(VMValue::Integer(res))
|
|
||||||
}
|
|
||||||
(VMValue::Integer(l), VMValue::Integer(r)) => {
|
|
||||||
let result = match op {
|
|
||||||
BinaryOp::Add => *l + *r,
|
|
||||||
BinaryOp::Sub => *l - *r,
|
|
||||||
BinaryOp::Mul => *l * *r,
|
|
||||||
BinaryOp::Div => {
|
|
||||||
if *r == 0 {
|
|
||||||
return Err(VMError::DivisionByZero);
|
|
||||||
}
|
|
||||||
*l / *r
|
|
||||||
},
|
|
||||||
_ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))),
|
|
||||||
};
|
|
||||||
Ok(VMValue::Integer(result))
|
|
||||||
},
|
|
||||||
|
|
||||||
(VMValue::String(l), VMValue::Integer(r)) => {
|
|
||||||
// String + Integer concatenation
|
|
||||||
match op {
|
|
||||||
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))),
|
|
||||||
_ => Err(VMError::TypeError("String-integer operations only support addition".to_string())),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
(VMValue::String(l), VMValue::Bool(r)) => {
|
|
||||||
// String + Bool concatenation
|
|
||||||
match op {
|
|
||||||
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))),
|
|
||||||
_ => Err(VMError::TypeError("String-bool operations only support addition".to_string())),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
(VMValue::String(l), VMValue::String(r)) => {
|
|
||||||
// String concatenation
|
|
||||||
match op {
|
|
||||||
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))),
|
|
||||||
_ => Err(VMError::TypeError("String operations only support addition".to_string())),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// String + BoxRef concatenation
|
|
||||||
(VMValue::String(l), VMValue::BoxRef(r)) => {
|
|
||||||
match op {
|
|
||||||
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r.to_string_box().value))),
|
|
||||||
_ => Err(VMError::TypeError("String-BoxRef operations only support addition".to_string())),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// BoxRef + String concatenation
|
|
||||||
(VMValue::BoxRef(l), VMValue::String(r)) => {
|
|
||||||
match op {
|
|
||||||
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l.to_string_box().value, r))),
|
|
||||||
_ => Err(VMError::TypeError("BoxRef-String operations only support addition".to_string())),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_ => Err(VMError::TypeError(format!("Unsupported binary operation [vm.rs updated]: {:?} on {:?} and {:?}", op, left, right))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execute unary operation
|
|
||||||
pub(super) fn execute_unary_op(&self, op: &UnaryOp, operand: &VMValue) -> Result<VMValue, VMError> {
|
|
||||||
match (op, operand) {
|
|
||||||
(UnaryOp::Neg, VMValue::Integer(i)) => Ok(VMValue::Integer(-i)),
|
|
||||||
(UnaryOp::Not, VMValue::Bool(b)) => Ok(VMValue::Bool(!b)),
|
|
||||||
_ => Err(VMError::TypeError(format!("Unsupported unary operation: {:?} on {:?}", op, operand))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execute comparison operation
|
|
||||||
pub(super) fn execute_compare_op(&self, op: &CompareOp, left: &VMValue, right: &VMValue) -> Result<bool, VMError> {
|
|
||||||
match (left, right) {
|
|
||||||
// Numeric mixed comparisons (Integer/Float)
|
|
||||||
(VMValue::Integer(l), VMValue::Float(r)) => {
|
|
||||||
let l = *l as f64;
|
|
||||||
let r = *r;
|
|
||||||
let result = match op {
|
|
||||||
CompareOp::Eq => l == r,
|
|
||||||
CompareOp::Ne => l != r,
|
|
||||||
CompareOp::Lt => l < r,
|
|
||||||
CompareOp::Le => l <= r,
|
|
||||||
CompareOp::Gt => l > r,
|
|
||||||
CompareOp::Ge => l >= r,
|
|
||||||
};
|
|
||||||
Ok(result)
|
|
||||||
},
|
|
||||||
(VMValue::Float(l), VMValue::Integer(r)) => {
|
|
||||||
let l = *l;
|
|
||||||
let r = *r as f64;
|
|
||||||
let result = match op {
|
|
||||||
CompareOp::Eq => l == r,
|
|
||||||
CompareOp::Ne => l != r,
|
|
||||||
CompareOp::Lt => l < r,
|
|
||||||
CompareOp::Le => l <= r,
|
|
||||||
CompareOp::Gt => l > r,
|
|
||||||
CompareOp::Ge => l >= r,
|
|
||||||
};
|
|
||||||
Ok(result)
|
|
||||||
},
|
|
||||||
// Bool comparisons: support Eq/Ne only for now
|
|
||||||
(VMValue::Bool(l), VMValue::Bool(r)) => {
|
|
||||||
let result = match op {
|
|
||||||
CompareOp::Eq => l == r,
|
|
||||||
CompareOp::Ne => l != r,
|
|
||||||
_ => return Err(VMError::TypeError(format!("Unsupported boolean comparison: {:?}", op))),
|
|
||||||
};
|
|
||||||
Ok(result)
|
|
||||||
},
|
|
||||||
|
|
||||||
// Void comparisons: only Eq/Ne are defined
|
|
||||||
(VMValue::Void, VMValue::Void) => {
|
|
||||||
let result = match op {
|
|
||||||
CompareOp::Eq => true,
|
|
||||||
CompareOp::Ne => false,
|
|
||||||
_ => return Err(VMError::TypeError("Cannot order Void".to_string())),
|
|
||||||
};
|
|
||||||
Ok(result)
|
|
||||||
},
|
|
||||||
(VMValue::Void, _) | (_, VMValue::Void) => {
|
|
||||||
let result = match op {
|
|
||||||
CompareOp::Eq => false, // void == X (X != void) is false
|
|
||||||
CompareOp::Ne => true, // void != X is true
|
|
||||||
_ => return Err(VMError::TypeError("Cannot order Void".to_string())),
|
|
||||||
};
|
|
||||||
Ok(result)
|
|
||||||
},
|
|
||||||
|
|
||||||
(VMValue::Integer(l), VMValue::Integer(r)) => {
|
|
||||||
let result = match op {
|
|
||||||
CompareOp::Eq => l == r,
|
|
||||||
CompareOp::Ne => l != r,
|
|
||||||
CompareOp::Lt => l < r,
|
|
||||||
CompareOp::Le => l <= r,
|
|
||||||
CompareOp::Gt => l > r,
|
|
||||||
CompareOp::Ge => l >= r,
|
|
||||||
};
|
|
||||||
Ok(result)
|
|
||||||
},
|
|
||||||
|
|
||||||
(VMValue::Float(l), VMValue::Float(r)) => {
|
|
||||||
let result = match op {
|
|
||||||
CompareOp::Eq => l == r,
|
|
||||||
CompareOp::Ne => l != r,
|
|
||||||
CompareOp::Lt => l < r,
|
|
||||||
CompareOp::Le => l <= r,
|
|
||||||
CompareOp::Gt => l > r,
|
|
||||||
CompareOp::Ge => l >= r,
|
|
||||||
};
|
|
||||||
Ok(result)
|
|
||||||
},
|
|
||||||
|
|
||||||
(VMValue::String(l), VMValue::String(r)) => {
|
|
||||||
let result = match op {
|
|
||||||
CompareOp::Eq => l == r,
|
|
||||||
CompareOp::Ne => l != r,
|
|
||||||
CompareOp::Lt => l < r,
|
|
||||||
CompareOp::Le => l <= r,
|
|
||||||
CompareOp::Gt => l > r,
|
|
||||||
CompareOp::Ge => l >= r,
|
|
||||||
};
|
|
||||||
Ok(result)
|
|
||||||
},
|
|
||||||
|
|
||||||
_ => Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Record an instruction execution for statistics
|
/// Record an instruction execution for statistics
|
||||||
pub(super) fn record_instruction(&mut self, instruction: &MirInstruction) {
|
pub(super) fn record_instruction(&mut self, instruction: &MirInstruction) {
|
||||||
@ -1031,7 +747,7 @@ impl VM {
|
|||||||
fn call_unified_method(&self, box_value: Box<dyn NyashBox>, method: &str, args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, VMError> {
|
fn call_unified_method(&self, box_value: Box<dyn NyashBox>, method: &str, args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, VMError> {
|
||||||
// For now, we use the simplified method dispatch
|
// For now, we use the simplified method dispatch
|
||||||
// In a full implementation, this would check for InstanceBox and dispatch appropriately
|
// In a full implementation, this would check for InstanceBox and dispatch appropriately
|
||||||
self.call_box_method(box_value, method, args)
|
self.call_box_method_impl(box_value, method, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a method on a Box - simplified version of interpreter method dispatch
|
/// Call a method on a Box - simplified version of interpreter method dispatch
|
||||||
|
|||||||
110
src/backend/vm_boxcall.rs
Normal file
110
src/backend/vm_boxcall.rs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*!
|
||||||
|
* VM BoxCall Dispatch - method calls on boxes
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox};
|
||||||
|
use super::vm::{VM, VMError};
|
||||||
|
|
||||||
|
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> {
|
||||||
|
// ResultBox (NyashResultBox - new)
|
||||||
|
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()); }
|
||||||
|
_ => return Ok(Box::new(VoidBox::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResultBox (box_trait::ResultBox - legacy)
|
||||||
|
{
|
||||||
|
#[allow(deprecated)]
|
||||||
|
if let Some(result_box_legacy) = box_value.as_any().downcast_ref::<crate::box_trait::ResultBox>() {
|
||||||
|
match method {
|
||||||
|
"is_ok" | "isOk" => { return Ok(result_box_legacy.is_ok()); }
|
||||||
|
"get_value" | "getValue" => { return Ok(result_box_legacy.get_value()); }
|
||||||
|
"get_error" | "getError" => { return Ok(result_box_legacy.get_error()); }
|
||||||
|
_ => return Ok(Box::new(VoidBox::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic fallback: toString for any Box type
|
||||||
|
if method == "toString" {
|
||||||
|
return Ok(Box::new(StringBox::new(box_value.to_string_box().value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()))); }
|
||||||
|
_ => return Ok(Box::new(VoidBox::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArrayBox methods (minimal set)
|
||||||
|
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"))); }
|
||||||
|
_ => 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>() {
|
||||||
|
match method {
|
||||||
|
"set" => { if _args.len() >= 2 { return Ok(map_box.set(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: set(key, value) requires 2 args"))); }
|
||||||
|
"get" => { if let Some(k) = _args.get(0) { return Ok(map_box.get(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: get(key) requires 1 arg"))); }
|
||||||
|
"has" => { if let Some(k) = _args.get(0) { return Ok(map_box.has(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: has(key) requires 1 arg"))); }
|
||||||
|
"delete" | "remove" => { if let Some(k) = _args.get(0) { return Ok(map_box.delete(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: delete(key) requires 1 arg"))); }
|
||||||
|
"keys" => { return Ok(map_box.keys()); },
|
||||||
|
"values" => { return Ok(map_box.values()); },
|
||||||
|
"size" => { return Ok(map_box.size()); },
|
||||||
|
"clear" => { return Ok(map_box.clear()); },
|
||||||
|
_ => return Ok(Box::new(VoidBox::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SocketBox methods (minimal + timeouts)
|
||||||
|
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()); },
|
||||||
|
_ => 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())), } }
|
||||||
|
|
||||||
|
// 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())), } }
|
||||||
|
|
||||||
|
// Default: return void for any unrecognized box type or method
|
||||||
|
Ok(Box::new(VoidBox::new()))
|
||||||
|
}
|
||||||
|
}
|
||||||
141
src/backend/vm_values.rs
Normal file
141
src/backend/vm_values.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
/*!
|
||||||
|
* VM Value Operations - arithmetic, logical, and comparison helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::mir::{BinaryOp, CompareOp, UnaryOp};
|
||||||
|
use super::vm::{VM, VMError, VMValue};
|
||||||
|
|
||||||
|
impl VM {
|
||||||
|
/// Execute binary operation
|
||||||
|
pub(super) fn execute_binary_op(&self, op: &BinaryOp, left: &VMValue, right: &VMValue) -> Result<VMValue, VMError> {
|
||||||
|
// Fast path: logical AND/OR accept any truthy via as_bool
|
||||||
|
if matches!(*op, BinaryOp::And | BinaryOp::Or) {
|
||||||
|
let l = left.as_bool()?;
|
||||||
|
let r = right.as_bool()?;
|
||||||
|
return Ok(VMValue::Bool(match *op { BinaryOp::And => l && r, BinaryOp::Or => l || r, _ => unreachable!() }));
|
||||||
|
}
|
||||||
|
|
||||||
|
match (left, right) {
|
||||||
|
(VMValue::Integer(l), VMValue::Integer(r)) => {
|
||||||
|
let result = match op {
|
||||||
|
BinaryOp::Add => *l + *r,
|
||||||
|
BinaryOp::Sub => *l - *r,
|
||||||
|
BinaryOp::Mul => *l * *r,
|
||||||
|
BinaryOp::Div => {
|
||||||
|
if *r == 0 { return Err(VMError::DivisionByZero); }
|
||||||
|
*l / *r
|
||||||
|
},
|
||||||
|
_ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))),
|
||||||
|
};
|
||||||
|
Ok(VMValue::Integer(result))
|
||||||
|
},
|
||||||
|
|
||||||
|
(VMValue::String(l), VMValue::Integer(r)) => {
|
||||||
|
match op {
|
||||||
|
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))),
|
||||||
|
_ => Err(VMError::TypeError("String-integer operations only support addition".to_string())),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
(VMValue::String(l), VMValue::Bool(r)) => {
|
||||||
|
match op {
|
||||||
|
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))),
|
||||||
|
_ => Err(VMError::TypeError("String-bool operations only support addition".to_string())),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
(VMValue::String(l), VMValue::String(r)) => {
|
||||||
|
match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), _ => Err(VMError::TypeError("String operations only support addition".to_string())) }
|
||||||
|
},
|
||||||
|
|
||||||
|
// String + BoxRef concatenation
|
||||||
|
(VMValue::String(l), VMValue::BoxRef(r)) => {
|
||||||
|
match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r.to_string_box().value))), _ => Err(VMError::TypeError("String-BoxRef operations only support addition".to_string())) }
|
||||||
|
},
|
||||||
|
|
||||||
|
// BoxRef + String concatenation
|
||||||
|
(VMValue::BoxRef(l), VMValue::String(r)) => {
|
||||||
|
match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l.to_string_box().value, r))), _ => Err(VMError::TypeError("BoxRef-String operations only support addition".to_string())) }
|
||||||
|
},
|
||||||
|
|
||||||
|
// Arithmetic with BoxRef(IntegerBox)
|
||||||
|
(VMValue::BoxRef(li), VMValue::BoxRef(ri)) if li.as_any().downcast_ref::<crate::box_trait::IntegerBox>().is_some() && ri.as_any().downcast_ref::<crate::box_trait::IntegerBox>().is_some() => {
|
||||||
|
let l = li.as_any().downcast_ref::<crate::box_trait::IntegerBox>().unwrap().value;
|
||||||
|
let r = ri.as_any().downcast_ref::<crate::box_trait::IntegerBox>().unwrap().value;
|
||||||
|
let res = match op {
|
||||||
|
BinaryOp::Add => l + r,
|
||||||
|
BinaryOp::Sub => l - r,
|
||||||
|
BinaryOp::Mul => l * r,
|
||||||
|
BinaryOp::Div => { if r == 0 { return Err(VMError::DivisionByZero); } l / r },
|
||||||
|
_ => return Err(VMError::InvalidInstruction(format!("Unsupported integer BoxRef operation: {:?}", op))),
|
||||||
|
};
|
||||||
|
Ok(VMValue::Integer(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mixed Integer forms
|
||||||
|
(VMValue::BoxRef(li), VMValue::Integer(r)) if li.as_any().downcast_ref::<crate::box_trait::IntegerBox>().is_some() => {
|
||||||
|
let l = li.as_any().downcast_ref::<crate::box_trait::IntegerBox>().unwrap().value;
|
||||||
|
let res = match op {
|
||||||
|
BinaryOp::Add => l + *r,
|
||||||
|
BinaryOp::Sub => l - *r,
|
||||||
|
BinaryOp::Mul => l * *r,
|
||||||
|
BinaryOp::Div => { if *r == 0 { return Err(VMError::DivisionByZero); } l / *r },
|
||||||
|
_ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))),
|
||||||
|
};
|
||||||
|
Ok(VMValue::Integer(res))
|
||||||
|
}
|
||||||
|
(VMValue::Integer(l), VMValue::BoxRef(ri)) if ri.as_any().downcast_ref::<crate::box_trait::IntegerBox>().is_some() => {
|
||||||
|
let r = ri.as_any().downcast_ref::<crate::box_trait::IntegerBox>().unwrap().value;
|
||||||
|
let res = match op {
|
||||||
|
BinaryOp::Add => *l + r,
|
||||||
|
BinaryOp::Sub => *l - r,
|
||||||
|
BinaryOp::Mul => *l * r,
|
||||||
|
BinaryOp::Div => { if r == 0 { return Err(VMError::DivisionByZero); } *l / r },
|
||||||
|
_ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))),
|
||||||
|
};
|
||||||
|
Ok(VMValue::Integer(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => Err(VMError::TypeError(format!("Unsupported binary operation: {:?} on {:?} and {:?}", op, left, right))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute unary operation
|
||||||
|
pub(super) fn execute_unary_op(&self, op: &UnaryOp, operand: &VMValue) -> Result<VMValue, VMError> {
|
||||||
|
match (op, operand) {
|
||||||
|
(UnaryOp::Neg, VMValue::Integer(i)) => Ok(VMValue::Integer(-i)),
|
||||||
|
(UnaryOp::Not, VMValue::Bool(b)) => Ok(VMValue::Bool(!b)),
|
||||||
|
_ => Err(VMError::TypeError(format!("Unsupported unary operation: {:?} on {:?}", op, operand))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute comparison operation
|
||||||
|
pub(super) fn execute_compare_op(&self, op: &CompareOp, left: &VMValue, right: &VMValue) -> Result<bool, VMError> {
|
||||||
|
match (left, right) {
|
||||||
|
// Mixed numeric
|
||||||
|
(VMValue::Integer(l), VMValue::Float(r)) => {
|
||||||
|
let l = *l as f64; let r = *r; Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r })
|
||||||
|
}
|
||||||
|
(VMValue::Float(l), VMValue::Integer(r)) => {
|
||||||
|
let l = *l; let r = *r as f64; Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r })
|
||||||
|
}
|
||||||
|
// Bool
|
||||||
|
(VMValue::Bool(l), VMValue::Bool(r)) => {
|
||||||
|
Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, _ => return Err(VMError::TypeError(format!("Unsupported boolean comparison: {:?}", op))) })
|
||||||
|
}
|
||||||
|
// Void
|
||||||
|
(VMValue::Void, VMValue::Void) => {
|
||||||
|
Ok(match op { CompareOp::Eq => true, CompareOp::Ne => false, _ => return Err(VMError::TypeError("Cannot order Void".to_string())) })
|
||||||
|
}
|
||||||
|
(VMValue::Void, _) | (_, VMValue::Void) => {
|
||||||
|
Ok(match op { CompareOp::Eq => false, CompareOp::Ne => true, _ => return Err(VMError::TypeError("Cannot order Void".to_string())) })
|
||||||
|
}
|
||||||
|
// Homogeneous
|
||||||
|
(VMValue::Integer(l), VMValue::Integer(r)) => Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r }),
|
||||||
|
(VMValue::Float(l), VMValue::Float(r)) => Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r }),
|
||||||
|
(VMValue::String(l), VMValue::String(r)) => Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r }),
|
||||||
|
_ => Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -68,6 +68,12 @@ pub enum VerificationError {
|
|||||||
instruction_index: usize,
|
instruction_index: usize,
|
||||||
reason: String,
|
reason: String,
|
||||||
},
|
},
|
||||||
|
/// Barrier appears without nearby memory ops (diagnostic; strict mode only)
|
||||||
|
SuspiciousBarrierContext {
|
||||||
|
block: BasicBlockId,
|
||||||
|
instruction_index: usize,
|
||||||
|
note: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MIR verifier for SSA form and semantic correctness
|
/// MIR verifier for SSA form and semantic correctness
|
||||||
@ -131,6 +137,10 @@ impl MirVerifier {
|
|||||||
if let Err(mut weak_barrier_errors) = self.verify_weakref_and_barrier(function) {
|
if let Err(mut weak_barrier_errors) = self.verify_weakref_and_barrier(function) {
|
||||||
local_errors.append(&mut weak_barrier_errors);
|
local_errors.append(&mut weak_barrier_errors);
|
||||||
}
|
}
|
||||||
|
// 6. Light barrier-context diagnostic (strict mode only)
|
||||||
|
if let Err(mut barrier_ctx) = self.verify_barrier_context(function) {
|
||||||
|
local_errors.append(&mut barrier_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
if local_errors.is_empty() {
|
if local_errors.is_empty() {
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -222,6 +232,59 @@ impl MirVerifier {
|
|||||||
if errors.is_empty() { Ok(()) } else { Err(errors) }
|
if errors.is_empty() { Ok(()) } else { Err(errors) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Light diagnostic: Barrier should be near memory ops in the same block (best-effort)
|
||||||
|
/// Enabled only when NYASH_VERIFY_BARRIER_STRICT=1
|
||||||
|
fn verify_barrier_context(&self, function: &MirFunction) -> Result<(), Vec<VerificationError>> {
|
||||||
|
let strict = std::env::var("NYASH_VERIFY_BARRIER_STRICT").ok().as_deref() == Some("1");
|
||||||
|
if !strict { return Ok(()); }
|
||||||
|
|
||||||
|
use super::MirInstruction;
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
for (bid, block) in &function.blocks {
|
||||||
|
// Build a flat vec of (idx, &inst) including terminator (as last)
|
||||||
|
let mut insts: Vec<(usize, &MirInstruction)> = block.instructions.iter().enumerate().collect();
|
||||||
|
if let Some(term) = &block.terminator {
|
||||||
|
insts.push((usize::MAX, term));
|
||||||
|
}
|
||||||
|
for (idx, inst) in &insts {
|
||||||
|
let is_barrier = matches!(inst,
|
||||||
|
MirInstruction::Barrier { .. } |
|
||||||
|
MirInstruction::BarrierRead { .. } |
|
||||||
|
MirInstruction::BarrierWrite { .. }
|
||||||
|
);
|
||||||
|
if !is_barrier { continue; }
|
||||||
|
|
||||||
|
// Look around +-2 instructions for a memory op hint
|
||||||
|
let mut has_mem_neighbor = false;
|
||||||
|
for (j, other) in &insts {
|
||||||
|
if *j == *idx { continue; }
|
||||||
|
// integer distance (treat usize::MAX as distant)
|
||||||
|
let dist = if *idx == usize::MAX || *j == usize::MAX { 99 } else { idx.max(j) - idx.min(j) };
|
||||||
|
if dist > 2 { continue; }
|
||||||
|
if matches!(other,
|
||||||
|
MirInstruction::Load { .. } |
|
||||||
|
MirInstruction::Store { .. } |
|
||||||
|
MirInstruction::ArrayGet { .. } |
|
||||||
|
MirInstruction::ArraySet { .. } |
|
||||||
|
MirInstruction::RefGet { .. } |
|
||||||
|
MirInstruction::RefSet { .. }
|
||||||
|
) {
|
||||||
|
has_mem_neighbor = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !has_mem_neighbor {
|
||||||
|
errors.push(VerificationError::SuspiciousBarrierContext {
|
||||||
|
block: *bid,
|
||||||
|
instruction_index: *idx,
|
||||||
|
note: "barrier without nearby memory op (±2 inst)".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if errors.is_empty() { Ok(()) } else { Err(errors) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Verify SSA form properties
|
/// Verify SSA form properties
|
||||||
fn verify_ssa_form(&self, function: &MirFunction) -> Result<(), Vec<VerificationError>> {
|
fn verify_ssa_form(&self, function: &MirFunction) -> Result<(), Vec<VerificationError>> {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
|||||||
Reference in New Issue
Block a user