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

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

View File

@ -10,4 +10,3 @@ fn import_plan_v1_min_and_run_vm() {
let out = vm.execute_module(&module).expect("vm exec");
assert_eq!(out.to_string_box().value, "42");
}

View File

@ -1,6 +1,6 @@
//! Tests for NyashBox trait implementations
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoxCore};
use crate::boxes::{ArrayBox, BufferBox, JSONBox, NyashFutureBox, NyashStreamBox, NyashResultBox};
use crate::box_trait::{BoxCore, IntegerBox, NyashBox, StringBox};
use crate::boxes::{ArrayBox, BufferBox, JSONBox, NyashFutureBox, NyashResultBox, NyashStreamBox};
#[cfg(test)]
mod tests {
@ -11,17 +11,17 @@ mod tests {
let mut array = ArrayBox::new();
let str_box = Box::new(StringBox::new("test")) as Box<dyn NyashBox>;
let int_box = Box::new(IntegerBox::new(42)) as Box<dyn NyashBox>;
array.push(str_box);
array.push(int_box);
assert_eq!(array.type_name(), "ArrayBox");
assert_eq!(array.len(), 2);
let string_repr = array.to_string_box();
assert!(string_repr.value.contains("test"));
assert!(string_repr.value.contains("42"));
// Test cloning
let cloned = array.clone_box();
assert_eq!(cloned.type_name(), "ArrayBox");
@ -30,17 +30,17 @@ mod tests {
#[test]
fn test_buffer_box_nyash_trait() {
let buffer = BufferBox::from_vec(vec![1, 2, 3, 4, 5]);
assert_eq!(buffer.type_name(), "BufferBox");
assert_eq!(buffer.len(), 5);
let string_repr = buffer.to_string_box();
assert!(string_repr.value.contains("BufferBox(5 bytes)"));
// Test cloning
let cloned = buffer.clone_box();
assert_eq!(cloned.type_name(), "BufferBox");
// Test equality
let other_buffer = BufferBox::from_vec(vec![1, 2, 3, 4, 5]);
assert!(buffer.equals(&other_buffer).value);
@ -50,36 +50,36 @@ mod tests {
fn test_json_box_nyash_trait() {
let json_str = r#"{"name": "test", "value": 42}"#;
let json_box = JSONBox::from_str(json_str).expect("Valid JSON");
assert_eq!(json_box.type_name(), "JSONBox");
let string_repr = json_box.to_string_box();
assert!(string_repr.value.contains("test"));
assert!(string_repr.value.contains("42"));
// Test cloning
let cloned = json_box.clone_box();
assert_eq!(cloned.type_name(), "JSONBox");
// Test equality
let other_json = JSONBox::from_str(json_str).expect("Valid JSON");
assert!(json_box.equals(&other_json).value);
}
#[test]
#[test]
fn test_future_box_nyash_trait() {
let future = NyashFutureBox::new();
assert_eq!(future.type_name(), "NyashFutureBox");
assert!(!future.ready());
let string_repr = future.to_string_box();
assert!(string_repr.value.contains("Future(pending)"));
// Test setting result
let result_box = Box::new(StringBox::new("completed")) as Box<dyn NyashBox>;
future.set_result(result_box);
assert!(future.ready());
let result = future.get();
assert_eq!(result.to_string_box().value, "completed");
@ -88,19 +88,19 @@ mod tests {
#[test]
fn test_stream_box_nyash_trait() {
let mut stream = NyashStreamBox::from_data(vec![72, 101, 108, 108, 111]); // "Hello"
assert_eq!(stream.type_name(), "NyashStreamBox");
assert_eq!(stream.len(), 5);
let string_repr = stream.to_string_box();
assert!(string_repr.value.contains("NyashStreamBox(5 bytes"));
// Test reading
let mut buffer = [0u8; 3];
let bytes_read = stream.read(&mut buffer).expect("Read should succeed");
assert_eq!(bytes_read, 3);
assert_eq!(&buffer, &[72, 101, 108]); // "Hel"
// Test writing
stream.write(&[33, 33]).expect("Write should succeed"); // "!!"
assert_eq!(stream.len(), 7);
@ -109,19 +109,19 @@ mod tests {
#[test]
fn test_result_box_nyash_trait() {
let success_result = NyashResultBox::new_ok(Box::new(StringBox::new("success")));
assert_eq!(success_result.type_name(), "NyashResultBox");
assert!(success_result.is_ok_bool());
assert!(!success_result.is_err());
let string_repr = success_result.to_string_box();
assert!(string_repr.value.contains("Ok(success)"));
// Test error case
let error_result = NyashResultBox::new_err(Box::new(StringBox::new("error")));
assert!(!error_result.is_ok_bool());
assert!(error_result.is_err());
let error_string = error_result.to_string_box();
assert!(error_string.value.contains("Err(error)"));
}
@ -130,10 +130,10 @@ mod tests {
fn test_box_id_uniqueness() {
let box1 = ArrayBox::new();
let box2 = ArrayBox::new();
// Different instances should have different IDs
assert_ne!(box1.box_id(), box2.box_id());
// Same instance should have same ID
let cloned = box1.clone_box();
// Note: Clone creates new instance so ID will be different

View File

@ -1,12 +1,13 @@
#[cfg(test)]
mod tests {
use crate::runtime::host_handles;
use crate::runtime::host_api;
use crate::runtime::host_handles;
#[test]
fn host_reverse_call_map_slots() {
// Build a MapBox and turn it into a HostHandle
let map = std::sync::Arc::new(crate::boxes::map_box::MapBox::new()) as std::sync::Arc<dyn crate::box_trait::NyashBox>;
let map = std::sync::Arc::new(crate::boxes::map_box::MapBox::new())
as std::sync::Arc<dyn crate::box_trait::NyashBox>;
let h = host_handles::to_handle_arc(map);
// TLV args: key="k", val=42
@ -17,15 +18,35 @@ mod tests {
// set: slot 204
let mut out = vec![0u8; 256];
let mut out_len = out.len();
let code = unsafe { host_api::nyrt_host_call_slot(h, 204, tlv.as_ptr(), tlv.len(), out.as_mut_ptr(), &mut out_len) };
let code = unsafe {
host_api::nyrt_host_call_slot(
h,
204,
tlv.as_ptr(),
tlv.len(),
out.as_mut_ptr(),
&mut out_len,
)
};
assert_eq!(code, 0);
// size: slot 200
let mut out2 = vec![0u8; 256];
let mut out2_len = out2.len();
let code2 = unsafe { host_api::nyrt_host_call_slot(h, 200, std::ptr::null(), 0, out2.as_mut_ptr(), &mut out2_len) };
let code2 = unsafe {
host_api::nyrt_host_call_slot(
h,
200,
std::ptr::null(),
0,
out2.as_mut_ptr(),
&mut out2_len,
)
};
assert_eq!(code2, 0);
if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out2[..out2_len]) {
if let Some((tag, _sz, payload)) =
crate::runtime::plugin_ffi_common::decode::tlv_first(&out2[..out2_len])
{
assert_eq!(tag, 3, "size returns i64 tag (3)");
let n = crate::runtime::plugin_ffi_common::decode::u64(payload).unwrap_or(0);
assert_eq!(n, 1, "after set, size should be 1");
@ -34,4 +55,3 @@ mod tests {
}
}
}

View File

@ -1,8 +1,8 @@
#[cfg(all(test, not(feature = "jit-direct-only")))]
mod tests {
use crate::mir::{MirModule, MirFunction, FunctionSignature};
use crate::mir::{BasicBlockId, MirInstruction, ConstValue, EffectMask, MirType, BinaryOp};
use crate::backend::VM;
use crate::mir::{BasicBlockId, BinaryOp, ConstValue, EffectMask, MirInstruction, MirType};
use crate::mir::{FunctionSignature, MirFunction, MirModule};
fn make_add_main(a: i64, b: i64) -> MirModule {
let sig = FunctionSignature {
@ -14,12 +14,31 @@ mod tests {
let mut func = MirFunction::new(sig, BasicBlockId::new(0));
let bb = func.entry_block;
let v0 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::Integer(a) });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v0,
value: ConstValue::Integer(a),
});
let v1 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v1, value: ConstValue::Integer(b) });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v1,
value: ConstValue::Integer(b),
});
let v2 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: v2, op: BinaryOp::Add, lhs: v0, rhs: v1 });
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(v2) });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BinOp {
dst: v2,
op: BinaryOp::Add,
lhs: v0,
rhs: v1,
});
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(v2) });
let mut module = MirModule::new("identical".to_string());
module.add_function(func);
module
@ -35,7 +54,8 @@ mod tests {
let vm_s = vm_out.to_string_box().value;
// Run JIT (Cranelift minimal)
let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_jit").expect("JIT exec");
let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_jit")
.expect("JIT exec");
let jit_s = jit_out.to_string_box().value;
assert_eq!(vm_s, jit_s, "VM and JIT results should match");
@ -45,20 +65,34 @@ mod tests {
#[test]
fn identical_vm_and_jit_console_log_side_effect_free() {
// Build: const 1; extern_call env.console.log(1); return 1
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask};
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: crate::mir::MirType::Integer, effects: EffectMask::PURE };
use crate::mir::{EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule};
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: crate::mir::MirType::Integer,
effects: EffectMask::PURE,
};
let mut func = MirFunction::new(sig, crate::mir::BasicBlockId::new(0));
let bb = func.entry_block;
let v0 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: crate::mir::ConstValue::Integer(1) });
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::ExternCall {
dst: None,
iface_name: "env.console".to_string(),
method_name: "log".to_string(),
args: vec![v0],
effects: EffectMask::IO,
});
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(v0) });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v0,
value: crate::mir::ConstValue::Integer(1),
});
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::ExternCall {
dst: None,
iface_name: "env.console".to_string(),
method_name: "log".to_string(),
args: vec![v0],
effects: EffectMask::IO,
});
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(v0) });
let mut module = MirModule::new("identical_console".into());
module.add_function(func);
@ -68,9 +102,13 @@ mod tests {
let vm_s = vm_out.to_string_box().value;
// Run JIT (Cranelift minimal) — ExternCallはスキップされる想定
let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_console").expect("JIT exec");
let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_console")
.expect("JIT exec");
let jit_s = jit_out.to_string_box().value;
assert_eq!(vm_s, jit_s, "VM and JIT results should match despite console.log side effects");
assert_eq!(
vm_s, jit_s,
"VM and JIT results should match despite console.log side effects"
);
}
}

View File

@ -1,54 +1,174 @@
#[cfg(all(test, not(feature = "jit-direct-only")))]
mod tests {
use crate::backend::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature, BasicBlockId, MirInstruction, ConstValue, EffectMask, MirType, ValueId};
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType, ValueId,
};
// Build a MIR that exercises Array.get/set/len, Map.set/size/has/get, and String.len
fn make_module() -> MirModule {
let mut module = MirModule::new("identical_collections".to_string());
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
// Build: arr = NewBox(ArrayBox); arr.set(0, "x"); len = arr.len();
let arr = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: arr, box_type: "ArrayBox".into(), args: vec![] });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: arr,
box_type: "ArrayBox".into(),
args: vec![],
});
let idx0 = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: idx0, value: ConstValue::Integer(0) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: idx0,
value: ConstValue::Integer(0),
});
let s = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: s, value: ConstValue::String("x".into()) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "set".into(), args: vec![idx0, s], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: s,
value: ConstValue::String("x".into()),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: arr,
method: "set".into(),
args: vec![idx0, s],
method_id: None,
effects: EffectMask::PURE,
});
let alen = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(alen), box_val: arr, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(alen),
box_val: arr,
method: "len".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
// Map: m = NewBox(MapBox); m.set("k", 42); size = m.size(); has = m.has("k"); get = m.get("k")
let m = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: m, box_type: "MapBox".into(), args: vec![] });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: m,
box_type: "MapBox".into(),
args: vec![],
});
let k = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k, value: ConstValue::String("k".into()) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: k,
value: ConstValue::String("k".into()),
});
let v = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v, value: ConstValue::Integer(42) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m, method: "set".into(), args: vec![k, v], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v,
value: ConstValue::Integer(42),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: m,
method: "set".into(),
args: vec![k, v],
method_id: None,
effects: EffectMask::PURE,
});
let msize = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(msize), box_val: m, method: "size".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(msize),
box_val: m,
method: "size".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
let mhas = f.next_value_id();
let k2 = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k2, value: ConstValue::String("k".into()) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(mhas), box_val: m, method: "has".into(), args: vec![k2], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: k2,
value: ConstValue::String("k".into()),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(mhas),
box_val: m,
method: "has".into(),
args: vec![k2],
method_id: None,
effects: EffectMask::PURE,
});
let mget = f.next_value_id();
let k3 = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k3, value: ConstValue::String("k".into()) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(mget), box_val: m, method: "get".into(), args: vec![k3], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: k3,
value: ConstValue::String("k".into()),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(mget),
box_val: m,
method: "get".into(),
args: vec![k3],
method_id: None,
effects: EffectMask::PURE,
});
// String.len: sb = "hello"; slen = sb.len()
let sb = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: sb, value: ConstValue::String("hello".into()) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: sb,
value: ConstValue::String("hello".into()),
});
let slen = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(slen), box_val: sb, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(slen),
box_val: sb,
method: "len".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
// Return: alen + msize + (mhas?1:0) + slen + (mget coerced to int or 0)
// Simplify: just return alen
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(alen) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(alen) });
module.add_function(f);
module
@ -66,9 +186,14 @@ mod tests {
// JIT with host bridge enabled for parity
std::env::set_var("NYASH_JIT_HOST_BRIDGE", "1");
let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_collections").expect("JIT exec");
let jit_out =
crate::backend::cranelift_compile_and_execute(&module, "identical_collections")
.expect("JIT exec");
let jit_s = jit_out.to_string_box().value;
assert_eq!(vm_s, jit_s, "VM and JIT results should match for collection ops");
assert_eq!(
vm_s, jit_s,
"VM and JIT results should match for collection ops"
);
}
}

View File

@ -1,56 +1,142 @@
#[cfg(all(test, not(feature = "jit-direct-only")))]
mod tests {
use std::sync::{Arc, RwLock};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use crate::backend::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature, BasicBlockId, MirInstruction, ConstValue, EffectMask, MirType};
use crate::box_trait::NyashBox;
use crate::interpreter::RuntimeError;
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
// Minimal Person factory: creates InstanceBox with fields [name, age]
struct PersonFactory;
impl crate::box_factory::BoxFactory for PersonFactory {
fn create_box(&self, name: &str, _args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, RuntimeError> {
if name != "Person" { return Err(RuntimeError::InvalidOperation { message: format!("Unknown Box type: {}", name) }); }
fn create_box(
&self,
name: &str,
_args: &[Box<dyn NyashBox>],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
if name != "Person" {
return Err(RuntimeError::InvalidOperation {
message: format!("Unknown Box type: {}", name),
});
}
let fields = vec!["name".to_string(), "age".to_string()];
let methods: HashMap<String, crate::ast::ASTNode> = HashMap::new();
let inst = crate::instance_v2::InstanceBox::from_declaration("Person".to_string(), fields, methods);
let inst = crate::instance_v2::InstanceBox::from_declaration(
"Person".to_string(),
fields,
methods,
);
Ok(Box::new(inst))
}
fn box_types(&self) -> Vec<&str> { vec!["Person"] }
fn is_builtin_factory(&self) -> bool { true }
fn box_types(&self) -> Vec<&str> {
vec!["Person"]
}
fn is_builtin_factory(&self) -> bool {
true
}
}
fn build_person_module() -> MirModule {
let mut module = MirModule::new("identical_person".to_string());
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::String,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let person = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: person, box_type: "Person".into(), args: vec![] });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: person,
box_type: "Person".into(),
args: vec![],
});
// person.setField("name", "Alice")
let k_name = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k_name, value: ConstValue::String("name".into()) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: k_name,
value: ConstValue::String("name".into()),
});
let v_alice = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v_alice, value: ConstValue::String("Alice".into()) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: person, method: "setField".into(), args: vec![k_name, v_alice], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v_alice,
value: ConstValue::String("Alice".into()),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: person,
method: "setField".into(),
args: vec![k_name, v_alice],
method_id: None,
effects: EffectMask::PURE,
});
// person.setField("age", 25)
let k_age = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k_age, value: ConstValue::String("age".into()) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: k_age,
value: ConstValue::String("age".into()),
});
let v_25 = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v_25, value: ConstValue::Integer(25) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: person, method: "setField".into(), args: vec![k_age, v_25], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v_25,
value: ConstValue::Integer(25),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: person,
method: "setField".into(),
args: vec![k_age, v_25],
method_id: None,
effects: EffectMask::PURE,
});
// name = person.getField("name"); return name
let k_name2 = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k_name2, value: ConstValue::String("name".into()) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: k_name2,
value: ConstValue::String("name".into()),
});
let out_name = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(out_name), box_val: person, method: "getField".into(), args: vec![k_name2], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(out_name) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(out_name),
box_val: person,
method: "getField".into(),
args: vec![k_name2],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return {
value: Some(out_name),
});
module.add_function(f);
module
@ -79,7 +165,8 @@ mod tests {
// JIThost-bridge on
std::env::set_var("NYASH_JIT_HOST_BRIDGE", "1");
let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_person").expect("JIT exec");
let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_person")
.expect("JIT exec");
let jit_s = jit_out.to_string_box().value;
assert_eq!(vm_s, jit_s);

View File

@ -1,18 +1,42 @@
#[cfg(all(test, not(feature = "jit-direct-only")))]
mod tests {
use crate::backend::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature, BasicBlockId, MirInstruction, ConstValue, EffectMask, MirType};
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
fn make_string_len() -> MirModule {
let mut module = MirModule::new("identical_string".to_string());
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let s = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: s, value: ConstValue::String("hello".into()) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: s,
value: ConstValue::String("hello".into()),
});
let ln = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(ln), box_val: s, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(ln) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(ln),
box_val: s,
method: "len".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(ln) });
module.add_function(f);
module
}
@ -31,9 +55,13 @@ mod tests {
let vm_s = vm_out.to_string_box().value;
// JIT
let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_string").expect("JIT exec");
let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_string")
.expect("JIT exec");
let jit_s = jit_out.to_string_box().value;
assert_eq!(vm_s, jit_s, "VM and JIT results should match for String.len");
assert_eq!(
vm_s, jit_s,
"VM and JIT results should match for String.len"
);
}
}

View File

@ -3,8 +3,8 @@
#[cfg(test)]
mod tests {
use crate::backend::vm::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature};
use crate::mir::{BasicBlockId, MirInstruction, ConstValue, EffectMask, Effect, MirType};
use crate::mir::{BasicBlockId, ConstValue, Effect, EffectMask, MirInstruction, MirType};
use crate::mir::{FunctionSignature, MirFunction, MirModule};
fn make_main() -> MirFunction {
let sig = FunctionSignature {
@ -22,26 +22,57 @@ mod tests {
let bb = func.entry_block;
let v0 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::Integer(42) });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v0,
value: ConstValue::Integer(42),
});
let v1 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeOp { dst: v1, op: crate::mir::TypeOpKind::Check, value: v0, ty: MirType::Integer });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::TypeOp {
dst: v1,
op: crate::mir::TypeOpKind::Check,
value: v0,
ty: MirType::Integer,
});
// console.log(result) via ExternCall
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![v1], effects: EffectMask::IO });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::ExternCall {
dst: None,
iface_name: "env.console".to_string(),
method_name: "log".to_string(),
args: vec![v1],
effects: EffectMask::IO,
});
// Cast (no-op for PoC semantics)
let v2 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeOp { dst: v2, op: crate::mir::TypeOpKind::Cast, value: v0, ty: MirType::Integer });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::TypeOp {
dst: v2,
op: crate::mir::TypeOpKind::Cast,
value: v0,
ty: MirType::Integer,
});
// Return void
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: None });
let mut module = MirModule::new("test".to_string());
module.add_function(func);
let mut vm = VM::new();
let _ = vm.execute_module(&module).expect("VM should execute module");
let _ = vm
.execute_module(&module)
.expect("VM should execute module");
}
#[test]
@ -50,16 +81,32 @@ mod tests {
let bb = func.entry_block;
let v0 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::Integer(3) });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v0,
value: ConstValue::Integer(3),
});
let v1 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeOp { dst: v1, op: crate::mir::TypeOpKind::Cast, value: v0, ty: MirType::Float });
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::TypeOp {
dst: v1,
op: crate::mir::TypeOpKind::Cast,
value: v0,
ty: MirType::Float,
});
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: None });
let mut module = MirModule::new("test".to_string());
module.add_function(func);
let mut vm = VM::new();
let _ = vm.execute_module(&module).expect("int->float cast should succeed");
let _ = vm
.execute_module(&module)
.expect("int->float cast should succeed");
}
#[test]
@ -68,16 +115,32 @@ mod tests {
let bb = func.entry_block;
let v0 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::Float(3.7) });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v0,
value: ConstValue::Float(3.7),
});
let v1 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeOp { dst: v1, op: crate::mir::TypeOpKind::Cast, value: v0, ty: MirType::Integer });
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::TypeOp {
dst: v1,
op: crate::mir::TypeOpKind::Cast,
value: v0,
ty: MirType::Integer,
});
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: None });
let mut module = MirModule::new("test".to_string());
module.add_function(func);
let mut vm = VM::new();
let _ = vm.execute_module(&module).expect("float->int cast should succeed");
let _ = vm
.execute_module(&module)
.expect("float->int cast should succeed");
}
#[test]
@ -86,11 +149,25 @@ mod tests {
let bb = func.entry_block;
let v0 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::String("x".to_string()) });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v0,
value: ConstValue::String("x".to_string()),
});
let v1 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeOp { dst: v1, op: crate::mir::TypeOpKind::Cast, value: v0, ty: MirType::Integer });
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::TypeOp {
dst: v1,
op: crate::mir::TypeOpKind::Cast,
value: v0,
ty: MirType::Integer,
});
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: None });
let mut module = MirModule::new("test".to_string());
module.add_function(func);
@ -105,22 +182,51 @@ mod tests {
let bb = func.entry_block;
let v0 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::Integer(7) });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v0,
value: ConstValue::Integer(7),
});
let v1 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeCheck { dst: v1, value: v0, expected_type: "IntegerBox".to_string() });
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![v1], effects: EffectMask::IO });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::TypeCheck {
dst: v1,
value: v0,
expected_type: "IntegerBox".to_string(),
});
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::ExternCall {
dst: None,
iface_name: "env.console".to_string(),
method_name: "log".to_string(),
args: vec![v1],
effects: EffectMask::IO,
});
let v2 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Cast { dst: v2, value: v0, target_type: MirType::Integer });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Cast {
dst: v2,
value: v0,
target_type: MirType::Integer,
});
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: None });
let mut module = MirModule::new("test".to_string());
module.add_function(func);
let mut vm = VM::new();
let _ = vm.execute_module(&module).expect("VM should execute module");
let _ = vm
.execute_module(&module)
.expect("VM should execute module");
}
#[test]
@ -129,27 +235,66 @@ mod tests {
let bb = func.entry_block;
let v0 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::Integer(1) });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v0,
value: ConstValue::Integer(1),
});
let v1 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::WeakRef { dst: v1, op: crate::mir::WeakRefOp::New, value: v0 });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::WeakRef {
dst: v1,
op: crate::mir::WeakRefOp::New,
value: v0,
});
let v2 = func.next_value_id();
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::WeakRef { dst: v2, op: crate::mir::WeakRefOp::Load, value: v1 });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::WeakRef {
dst: v2,
op: crate::mir::WeakRefOp::Load,
value: v1,
});
// Optional barriers (no-op semantics)
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Barrier { op: crate::mir::BarrierOp::Read, ptr: v2 });
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Barrier { op: crate::mir::BarrierOp::Write, ptr: v2 });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Barrier {
op: crate::mir::BarrierOp::Read,
ptr: v2,
});
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Barrier {
op: crate::mir::BarrierOp::Write,
ptr: v2,
});
// Print loaded value via env.console.log
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![v2], effects: EffectMask::IO });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::ExternCall {
dst: None,
iface_name: "env.console".to_string(),
method_name: "log".to_string(),
args: vec![v2],
effects: EffectMask::IO,
});
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None });
func.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: None });
let mut module = MirModule::new("test".to_string());
module.add_function(func);
let mut vm = VM::new();
let _ = vm.execute_module(&module).expect("VM should execute module");
let _ = vm
.execute_module(&module)
.expect("VM should execute module");
}
}

View File

@ -1,28 +1,28 @@
pub mod box_tests;
pub mod mir_vm_poc;
pub mod identical_exec;
pub mod identical_exec_collections;
pub mod identical_exec_string;
pub mod identical_exec_instance;
pub mod vtable_array_string;
pub mod vtable_strict;
pub mod vtable_array_ext;
pub mod vtable_array_p2;
pub mod vtable_array_p1;
pub mod vtable_string;
pub mod vtable_console;
pub mod vtable_map_ext;
pub mod vtable_string_p1;
pub mod host_reverse_slot;
pub mod nyash_abi_basic;
pub mod typebox_tlv_diff;
pub mod sugar_basic_test;
pub mod sugar_pipeline_test;
pub mod sugar_comp_assign_test;
pub mod sugar_coalesce_test;
pub mod sugar_safe_access_test;
pub mod sugar_range_test;
pub mod policy_mutdeny;
pub mod plugin_hygiene;
#[cfg(feature = "aot-plan-import")]
pub mod aot_plan_import;
pub mod box_tests;
pub mod host_reverse_slot;
pub mod identical_exec;
pub mod identical_exec_collections;
pub mod identical_exec_instance;
pub mod identical_exec_string;
pub mod mir_vm_poc;
pub mod nyash_abi_basic;
pub mod plugin_hygiene;
pub mod policy_mutdeny;
pub mod sugar_basic_test;
pub mod sugar_coalesce_test;
pub mod sugar_comp_assign_test;
pub mod sugar_pipeline_test;
pub mod sugar_range_test;
pub mod sugar_safe_access_test;
pub mod typebox_tlv_diff;
pub mod vtable_array_ext;
pub mod vtable_array_p1;
pub mod vtable_array_p2;
pub mod vtable_array_string;
pub mod vtable_console;
pub mod vtable_map_ext;
pub mod vtable_strict;
pub mod vtable_string;
pub mod vtable_string_p1;

View File

@ -1,6 +1,6 @@
#[cfg(test)]
mod tests {
use crate::runtime::type_registry::{resolve_slot_by_name, known_methods_for};
use crate::runtime::type_registry::{known_methods_for, resolve_slot_by_name};
#[test]
fn type_registry_resolves_core_slots() {
@ -28,36 +28,99 @@ mod tests {
#[ignore]
fn vm_vtable_map_set_get_has() {
use crate::backend::vm::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature, BasicBlockId, MirInstruction, EffectMask, ConstValue, MirType, ValueId};
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType, ValueId,
};
// Enable vtable-preferred path
std::env::set_var("NYASH_ABI_VTABLE", "1");
// Program: m = new MapBox(); m.set("k","v"); h = m.has("k"); g = m.get("k"); return g
let mut m = MirModule::new("nyash_abi_map_get".into());
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::String,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let mapv = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: mapv, box_type: "MapBox".into(), args: vec![] });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: mapv,
box_type: "MapBox".into(),
args: vec![],
});
let k = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k, value: ConstValue::String("k".into()) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: k,
value: ConstValue::String("k".into()),
});
let v = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v, value: ConstValue::String("v".into()) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: mapv, method: "set".into(), args: vec![k, v], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v,
value: ConstValue::String("v".into()),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: mapv,
method: "set".into(),
args: vec![k, v],
method_id: None,
effects: EffectMask::PURE,
});
let k2 = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k2, value: ConstValue::String("k".into()) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: k2,
value: ConstValue::String("k".into()),
});
let hasv = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(hasv), box_val: mapv, method: "has".into(), args: vec![k2], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(hasv),
box_val: mapv,
method: "has".into(),
args: vec![k2],
method_id: None,
effects: EffectMask::PURE,
});
let k3 = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k3, value: ConstValue::String("k".into()) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: k3,
value: ConstValue::String("k".into()),
});
let got = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(got), box_val: mapv, method: "get".into(), args: vec![k3], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(got) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(got),
box_val: mapv,
method: "get".into(),
args: vec![k3],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(got) });
m.add_function(f);
@ -69,8 +132,8 @@ mod tests {
#[test]
fn mapbox_keys_values_return_arrays() {
// Direct Box-level test (not via VM): keys()/values() should return ArrayBox
use crate::box_trait::{IntegerBox, NyashBox, StringBox};
use crate::boxes::map_box::MapBox;
use crate::box_trait::{NyashBox, StringBox, IntegerBox};
let map = MapBox::new();
map.set(Box::new(StringBox::new("a")), Box::new(IntegerBox::new(1)));

View File

@ -29,6 +29,9 @@ fn plugin_invoke_hygiene_string_len_is_hostcall() {
assert_eq!(symbol, c::SYM_STRING_LEN_H);
assert_eq!(reason, "mapped_symbol");
}
other => panic!("expected HostCall(mapped_symbol) for String.len, got: {:?}", other),
other => panic!(
"expected HostCall(mapped_symbol) for String.len, got: {:?}",
other
),
}
}

View File

@ -2,58 +2,158 @@
#[test]
#[ignore]
fn jit_readonly_array_push_denied() {
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType};
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
// Ensure read-only policy is on
std::env::set_var("NYASH_JIT_READ_ONLY", "1");
// Build: a = new ArrayBox(); a.push(3); ret a.len()
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let a = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: a, box_type: "ArrayBox".into(), args: vec![] });
let three = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: three, value: ConstValue::Integer(3) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: a,
box_type: "ArrayBox".into(),
args: vec![],
});
let three = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: three,
value: ConstValue::Integer(3),
});
// push should be denied under read-only policy, effectively no-op for length
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a, method: "push".into(), args: vec![three], method_id: None, effects: EffectMask::PURE });
let ln = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(ln), box_val: a, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(ln) });
let mut m = MirModule::new("jit_readonly_array_push_denied".into()); m.add_function(f);
let out = crate::backend::cranelift_compile_and_execute(&m, "jit_readonly_array_push_denied").expect("JIT exec");
assert_eq!(out.to_string_box().value, "0", "Array.push must be denied under read-only policy");
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a,
method: "push".into(),
args: vec![three],
method_id: None,
effects: EffectMask::PURE,
});
let ln = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(ln),
box_val: a,
method: "len".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(ln) });
let mut m = MirModule::new("jit_readonly_array_push_denied".into());
m.add_function(f);
let out = crate::backend::cranelift_compile_and_execute(&m, "jit_readonly_array_push_denied")
.expect("JIT exec");
assert_eq!(
out.to_string_box().value,
"0",
"Array.push must be denied under read-only policy"
);
}
#[cfg(feature = "cranelift-jit")]
#[test]
#[ignore]
fn jit_readonly_map_set_denied() {
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType};
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
// Ensure read-only policy is on
std::env::set_var("NYASH_JIT_READ_ONLY", "1");
// Build: m = new MapBox(); m.set("a", 2); ret m.size()
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let mbox = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: mbox, box_type: "MapBox".into(), args: vec![] });
let key = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: key, value: ConstValue::String("a".into()) });
let val = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: val, value: ConstValue::Integer(2) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: mbox,
box_type: "MapBox".into(),
args: vec![],
});
let key = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: key,
value: ConstValue::String("a".into()),
});
let val = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: val,
value: ConstValue::Integer(2),
});
// set should be denied under read-only policy
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: mbox, method: "set".into(), args: vec![key, val], method_id: None, effects: EffectMask::PURE });
let sz = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(sz), box_val: mbox, method: "size".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(sz) });
let mut module = MirModule::new("jit_readonly_map_set_denied".into()); module.add_function(f);
let out = crate::backend::cranelift_compile_and_execute(&module, "jit_readonly_map_set_denied").expect("JIT exec");
assert_eq!(out.to_string_box().value, "0", "Map.set must be denied under read-only policy");
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: mbox,
method: "set".into(),
args: vec![key, val],
method_id: None,
effects: EffectMask::PURE,
});
let sz = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(sz),
box_val: mbox,
method: "size".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(sz) });
let mut module = MirModule::new("jit_readonly_map_set_denied".into());
module.add_function(f);
let out = crate::backend::cranelift_compile_and_execute(&module, "jit_readonly_map_set_denied")
.expect("JIT exec");
assert_eq!(
out.to_string_box().value,
"0",
"Map.set must be denied under read-only policy"
);
}
// Engine-independent smoke: validate policy denial via host externs
#[test]
fn extern_readonly_array_push_denied() {
use std::sync::Arc;
use crate::boxes::array::ArrayBox;
use crate::backend::vm::VMValue;
use crate::boxes::array::ArrayBox;
use crate::jit::r#extern::collections as c;
use std::sync::Arc;
std::env::set_var("NYASH_JIT_READ_ONLY", "1");
let arr = Arc::new(ArrayBox::new());
@ -66,10 +166,10 @@ fn extern_readonly_array_push_denied() {
#[test]
fn extern_readonly_map_set_denied() {
use std::sync::Arc;
use crate::boxes::map_box::MapBox;
use crate::backend::vm::VMValue;
use crate::boxes::map_box::MapBox;
use crate::jit::r#extern::collections as c;
use std::sync::Arc;
std::env::set_var("NYASH_JIT_READ_ONLY", "1");
let map = Arc::new(MapBox::new());
@ -83,10 +183,10 @@ fn extern_readonly_map_set_denied() {
#[test]
fn extern_readonly_read_ops_allowed() {
use std::sync::Arc;
use crate::boxes::{array::ArrayBox, map_box::MapBox};
use crate::backend::vm::VMValue;
use crate::boxes::{array::ArrayBox, map_box::MapBox};
use crate::jit::r#extern::collections as c;
use std::sync::Arc;
std::env::set_var("NYASH_JIT_READ_ONLY", "1");
// Array: len/get are read-only

View File

@ -27,4 +27,3 @@ fn tokenizer_has_basic_sugar_tokens() {
assert!(has(|k| matches!(k, TokenType::DivAssign)));
assert!(has(|k| matches!(k, TokenType::RANGE)));
}

View File

@ -1,23 +1,46 @@
use crate::ast::{ASTNode, LiteralValue};
use crate::parser::entry_sugar::parse_with_sugar_level;
use crate::syntax::sugar_config::SugarLevel;
use crate::ast::{ASTNode, LiteralValue};
#[test]
fn coalesce_peek_rewrite() {
let code = "x = a ?? b\n";
let ast = parse_with_sugar_level(code, SugarLevel::Basic).expect("parse ok");
let program = match ast { ASTNode::Program { statements, .. } => statements, other => panic!("expected program, got {:?}", other) };
let assign = match &program[0] { ASTNode::Assignment { target, value, .. } => (target, value), other => panic!("expected assignment, got {:?}", other) };
match assign.0.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "x"), _ => panic!("target not x") }
let program = match ast {
ASTNode::Program { statements, .. } => statements,
other => panic!("expected program, got {:?}", other),
};
let assign = match &program[0] {
ASTNode::Assignment { target, value, .. } => (target, value),
other => panic!("expected assignment, got {:?}", other),
};
match assign.0.as_ref() {
ASTNode::Variable { name, .. } => assert_eq!(name, "x"),
_ => panic!("target not x"),
}
match assign.1.as_ref() {
ASTNode::PeekExpr { scrutinee, arms, else_expr, .. } => {
match scrutinee.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "a"), _ => panic!("scrutinee not a") }
ASTNode::PeekExpr {
scrutinee,
arms,
else_expr,
..
} => {
match scrutinee.as_ref() {
ASTNode::Variable { name, .. } => assert_eq!(name, "a"),
_ => panic!("scrutinee not a"),
}
assert_eq!(arms.len(), 1);
assert!(matches!(arms[0].0, LiteralValue::Null));
match &arms[0].1 { ASTNode::Variable { name, .. } => assert_eq!(name, "b"), _ => panic!("rhs not b") }
match else_expr.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "a"), _ => panic!("else not a") }
match &arms[0].1 {
ASTNode::Variable { name, .. } => assert_eq!(name, "b"),
_ => panic!("rhs not b"),
}
match else_expr.as_ref() {
ASTNode::Variable { name, .. } => assert_eq!(name, "a"),
_ => panic!("else not a"),
}
}
other => panic!("expected PeekExpr, got {:?}", other),
}

View File

@ -1,23 +1,43 @@
use crate::ast::{ASTNode, BinaryOperator};
use crate::parser::entry_sugar::parse_with_sugar_level;
use crate::syntax::sugar_config::SugarLevel;
use crate::ast::{ASTNode, BinaryOperator};
#[test]
fn compound_assign_rewrites_to_binaryop() {
let code = "x = 1\nx += 2\n";
let ast = parse_with_sugar_level(code, SugarLevel::Basic).expect("parse ok");
let program = match ast { ASTNode::Program { statements, .. } => statements, other => panic!("expected program, got {:?}", other) };
let program = match ast {
ASTNode::Program { statements, .. } => statements,
other => panic!("expected program, got {:?}", other),
};
assert_eq!(program.len(), 2);
let assign = match &program[1] { ASTNode::Assignment { target, value, .. } => (target, value), other => panic!("expected assignment, got {:?}", other) };
match assign.0.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "x"), other => panic!("expected target var, got {:?}", other) }
let assign = match &program[1] {
ASTNode::Assignment { target, value, .. } => (target, value),
other => panic!("expected assignment, got {:?}", other),
};
match assign.0.as_ref() {
ASTNode::Variable { name, .. } => assert_eq!(name, "x"),
other => panic!("expected target var, got {:?}", other),
}
match assign.1.as_ref() {
ASTNode::BinaryOp { operator, left, right, .. } => {
ASTNode::BinaryOp {
operator,
left,
right,
..
} => {
assert!(matches!(operator, BinaryOperator::Add));
match left.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "x"), other => panic!("expected left x, got {:?}", other) }
match right.as_ref() { ASTNode::Literal { .. } => {}, other => panic!("expected right literal, got {:?}", other) }
match left.as_ref() {
ASTNode::Variable { name, .. } => assert_eq!(name, "x"),
other => panic!("expected left x, got {:?}", other),
}
match right.as_ref() {
ASTNode::Literal { .. } => {}
other => panic!("expected right literal, got {:?}", other),
}
}
other => panic!("expected BinaryOp, got {:?}", other),
}

View File

@ -1,6 +1,6 @@
use crate::ast::ASTNode;
use crate::parser::entry_sugar::parse_with_sugar_level;
use crate::syntax::sugar_config::SugarLevel;
use crate::ast::ASTNode;
#[test]
fn pipeline_rewrites_function_and_method_calls() {
@ -8,17 +8,34 @@ fn pipeline_rewrites_function_and_method_calls() {
let ast = parse_with_sugar_level(code, SugarLevel::Basic).expect("parse ok");
// Program with one assignment
let program = match ast { ASTNode::Program { statements, .. } => statements, other => panic!("expected program, got {:?}", other) };
let program = match ast {
ASTNode::Program { statements, .. } => statements,
other => panic!("expected program, got {:?}", other),
};
assert_eq!(program.len(), 1);
let assign = match &program[0] { ASTNode::Assignment { target, value, .. } => (target, value), other => panic!("expected assignment, got {:?}", other) };
let assign = match &program[0] {
ASTNode::Assignment { target, value, .. } => (target, value),
other => panic!("expected assignment, got {:?}", other),
};
// target = result
match assign.0.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "result"), other => panic!("expected target var, got {:?}", other) }
match assign.0.as_ref() {
ASTNode::Variable { name, .. } => assert_eq!(name, "result"),
other => panic!("expected target var, got {:?}", other),
}
// value should be obj.m( normalize(data,1), 2 )
let (obj_name, method_name, args) = match assign.1.as_ref() {
ASTNode::MethodCall { object, method, arguments, .. } => {
let obj_name = match object.as_ref() { ASTNode::Variable { name, .. } => name.clone(), other => panic!("expected obj var, got {:?}", other) };
ASTNode::MethodCall {
object,
method,
arguments,
..
} => {
let obj_name = match object.as_ref() {
ASTNode::Variable { name, .. } => name.clone(),
other => panic!("expected obj var, got {:?}", other),
};
(obj_name, method.clone(), arguments.clone())
}
other => panic!("expected method call, got {:?}", other),
@ -29,10 +46,15 @@ fn pipeline_rewrites_function_and_method_calls() {
// first arg should be normalize(data,1)
match &args[0] {
ASTNode::FunctionCall { name, arguments, .. } => {
ASTNode::FunctionCall {
name, arguments, ..
} => {
assert_eq!(name, "normalize");
assert_eq!(arguments.len(), 2);
match &arguments[0] { ASTNode::Variable { name, .. } => assert_eq!(name, "data"), other => panic!("expected var data, got {:?}", other) }
match &arguments[0] {
ASTNode::Variable { name, .. } => assert_eq!(name, "data"),
other => panic!("expected var data, got {:?}", other),
}
}
other => panic!("expected function call, got {:?}", other),
}

View File

@ -1,16 +1,21 @@
use crate::ast::ASTNode;
use crate::parser::entry_sugar::parse_with_sugar_level;
use crate::syntax::sugar_config::SugarLevel;
use crate::ast::ASTNode;
#[test]
fn range_rewrites_to_function_call() {
let code = "r = 1 .. 5\n";
let ast = parse_with_sugar_level(code, SugarLevel::Basic).expect("parse ok");
let program = match ast { ASTNode::Program { statements, .. } => statements, other => panic!("expected program, got {:?}", other) };
let program = match ast {
ASTNode::Program { statements, .. } => statements,
other => panic!("expected program, got {:?}", other),
};
match &program[0] {
ASTNode::Assignment { value, .. } => match value.as_ref() {
ASTNode::FunctionCall { name, arguments, .. } => {
ASTNode::FunctionCall {
name, arguments, ..
} => {
assert_eq!(name, "Range");
assert_eq!(arguments.len(), 2);
}

View File

@ -1,41 +1,69 @@
use crate::ast::ASTNode;
use crate::parser::entry_sugar::parse_with_sugar_level;
use crate::syntax::sugar_config::SugarLevel;
use crate::ast::ASTNode;
#[test]
fn safe_access_field_and_method() {
let code = "a = user?.profile\nb = user?.m(1)\n";
let ast = parse_with_sugar_level(code, SugarLevel::Basic).expect("parse ok");
let program = match ast { ASTNode::Program { statements, .. } => statements, other => panic!("expected program, got {:?}", other) };
let program = match ast {
ASTNode::Program { statements, .. } => statements,
other => panic!("expected program, got {:?}", other),
};
assert_eq!(program.len(), 2);
// a = user?.profile
match &program[0] {
ASTNode::Assignment { value, .. } => match value.as_ref() {
ASTNode::PeekExpr { scrutinee, else_expr, .. } => {
match scrutinee.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "user"), _ => panic!("scrutinee not user") }
ASTNode::PeekExpr {
scrutinee,
else_expr,
..
} => {
match scrutinee.as_ref() {
ASTNode::Variable { name, .. } => assert_eq!(name, "user"),
_ => panic!("scrutinee not user"),
}
match else_expr.as_ref() {
ASTNode::FieldAccess { object, field, .. } => {
match object.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "user"), _ => panic!("object not user") }
match object.as_ref() {
ASTNode::Variable { name, .. } => assert_eq!(name, "user"),
_ => panic!("object not user"),
}
assert_eq!(field, "profile");
}
other => panic!("else not field access, got {:?}", other),
}
}
other => panic!("expected PeekExpr, got {:?}", other),
}
},
other => panic!("expected assignment, got {:?}", other),
}
// b = user?.m(1)
match &program[1] {
ASTNode::Assignment { value, .. } => match value.as_ref() {
ASTNode::PeekExpr { scrutinee, else_expr, .. } => {
match scrutinee.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "user"), _ => panic!("scrutinee not user") }
ASTNode::PeekExpr {
scrutinee,
else_expr,
..
} => {
match scrutinee.as_ref() {
ASTNode::Variable { name, .. } => assert_eq!(name, "user"),
_ => panic!("scrutinee not user"),
}
match else_expr.as_ref() {
ASTNode::MethodCall { object, method, arguments, .. } => {
match object.as_ref() { ASTNode::Variable { name, .. } => assert_eq!(name, "user"), _ => panic!("object not user") }
ASTNode::MethodCall {
object,
method,
arguments,
..
} => {
match object.as_ref() {
ASTNode::Variable { name, .. } => assert_eq!(name, "user"),
_ => panic!("object not user"),
}
assert_eq!(method, "m");
assert_eq!(arguments.len(), 1);
}
@ -43,7 +71,7 @@ fn safe_access_field_and_method() {
}
}
other => panic!("expected PeekExpr, got {:?}", other),
}
},
other => panic!("expected assignment, got {:?}", other),
}
}

View File

@ -1,9 +1,9 @@
#[cfg(all(test, not(feature = "jit-direct-only")))]
mod tests {
use std::env;
use crate::box_trait::{NyashBox, StringBox, IntegerBox};
use crate::boxes::math_box::FloatBox;
use crate::box_trait::{IntegerBox, NyashBox, StringBox};
use crate::boxes::array::ArrayBox;
use crate::boxes::math_box::FloatBox;
use std::env;
use std::fs;
use std::path::PathBuf;
@ -16,7 +16,10 @@ mod tests {
let host = host.read().unwrap();
let bx = host.create_box(box_type, &[]).expect("create_box");
// Downcast to PluginBoxV2 to get instance_id
if let Some(p) = bx.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
if let Some(p) = bx
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
(box_type.to_string(), p.instance_id(), bx)
} else {
panic!("not a plugin box: {}", bx.type_name());
@ -35,11 +38,24 @@ mod tests {
let out_tlv = {
let h = host.read().unwrap();
// set("k", 42)
let _ = h.invoke_instance_method(&bt1, "set", id1, &[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))]).expect("set tlv");
let _ = h
.invoke_instance_method(
&bt1,
"set",
id1,
&[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))],
)
.expect("set tlv");
// size()
let sz = h.invoke_instance_method(&bt1, "size", id1, &[]).expect("size tlv").unwrap();
let sz = h
.invoke_instance_method(&bt1, "size", id1, &[])
.expect("size tlv")
.unwrap();
// get("k")
let gv = h.invoke_instance_method(&bt1, "get", id1, &[Box::new(StringBox::new("k"))]).expect("get tlv").unwrap();
let gv = h
.invoke_instance_method(&bt1, "get", id1, &[Box::new(StringBox::new("k"))])
.expect("get tlv")
.unwrap();
(sz.to_string_box().value, gv.to_string_box().value)
};
@ -48,9 +64,22 @@ mod tests {
let (bt2, id2, _hold2) = create_plugin_instance("MapBox");
let out_tb = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt2, "set", id2, &[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))]).expect("set tb");
let sz = h.invoke_instance_method(&bt2, "size", id2, &[]).expect("size tb").unwrap();
let gv = h.invoke_instance_method(&bt2, "get", id2, &[Box::new(StringBox::new("k"))]).expect("get tb").unwrap();
let _ = h
.invoke_instance_method(
&bt2,
"set",
id2,
&[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))],
)
.expect("set tb");
let sz = h
.invoke_instance_method(&bt2, "size", id2, &[])
.expect("size tb")
.unwrap();
let gv = h
.invoke_instance_method(&bt2, "get", id2, &[Box::new(StringBox::new("k"))])
.expect("get tb")
.unwrap();
(sz.to_string_box().value, gv.to_string_box().value)
};
@ -67,9 +96,22 @@ mod tests {
let (bt1, id1, _hold1) = create_plugin_instance("ArrayBox");
let out_tlv = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt1, "set", id1, &[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))]).expect("set tlv");
let ln = h.invoke_instance_method(&bt1, "len", id1, &[]).expect("len tlv").unwrap();
let gv = h.invoke_instance_method(&bt1, "get", id1, &[Box::new(IntegerBox::new(0))]).expect("get tlv").unwrap();
let _ = h
.invoke_instance_method(
&bt1,
"set",
id1,
&[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))],
)
.expect("set tlv");
let ln = h
.invoke_instance_method(&bt1, "len", id1, &[])
.expect("len tlv")
.unwrap();
let gv = h
.invoke_instance_method(&bt1, "get", id1, &[Box::new(IntegerBox::new(0))])
.expect("get tlv")
.unwrap();
(ln.to_string_box().value, gv.to_string_box().value)
};
// TypeBox path
@ -77,12 +119,28 @@ mod tests {
let (bt2, id2, _hold2) = create_plugin_instance("ArrayBox");
let out_tb = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt2, "set", id2, &[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))]).expect("set tb");
let ln = h.invoke_instance_method(&bt2, "length", id2, &[]).expect("len tb").unwrap();
let gv = h.invoke_instance_method(&bt2, "get", id2, &[Box::new(IntegerBox::new(0))]).expect("get tb").unwrap();
let _ = h
.invoke_instance_method(
&bt2,
"set",
id2,
&[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))],
)
.expect("set tb");
let ln = h
.invoke_instance_method(&bt2, "length", id2, &[])
.expect("len tb")
.unwrap();
let gv = h
.invoke_instance_method(&bt2, "get", id2, &[Box::new(IntegerBox::new(0))])
.expect("get tb")
.unwrap();
(ln.to_string_box().value, gv.to_string_box().value)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (ArrayBox)");
assert_eq!(
out_tlv, out_tb,
"TLV vs TypeBox results should match (ArrayBox)"
);
}
#[test]
@ -96,8 +154,14 @@ mod tests {
let out_tlv = {
let h = host.read().unwrap();
// birth with init string: use fromUtf8 via set of arg in create? Current loader birth() no-arg, so concat
let _ = h.invoke_instance_method(&bt1, "concat", id1, &[Box::new(StringBox::new("ab"))]).expect("concat tlv").unwrap();
let ln = h.invoke_instance_method(&bt1, "length", id1, &[]).expect("len tlv").unwrap();
let _ = h
.invoke_instance_method(&bt1, "concat", id1, &[Box::new(StringBox::new("ab"))])
.expect("concat tlv")
.unwrap();
let ln = h
.invoke_instance_method(&bt1, "length", id1, &[])
.expect("len tlv")
.unwrap();
(ln.to_string_box().value)
};
// TypeBox path
@ -105,11 +169,20 @@ mod tests {
let (bt2, id2, _hold2) = create_plugin_instance("StringBox");
let out_tb = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt2, "concat", id2, &[Box::new(StringBox::new("ab"))]).expect("concat tb").unwrap();
let ln = h.invoke_instance_method(&bt2, "length", id2, &[]).expect("len tb").unwrap();
let _ = h
.invoke_instance_method(&bt2, "concat", id2, &[Box::new(StringBox::new("ab"))])
.expect("concat tb")
.unwrap();
let ln = h
.invoke_instance_method(&bt2, "length", id2, &[])
.expect("len tb")
.unwrap();
(ln.to_string_box().value)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (StringBox)");
assert_eq!(
out_tlv, out_tb,
"TLV vs TypeBox results should match (StringBox)"
);
}
#[test]
@ -122,8 +195,14 @@ mod tests {
let (bt1, id1, _hold1) = create_plugin_instance("IntegerBox");
let out_tlv = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt1, "set", id1, &[Box::new(IntegerBox::new(123))]).expect("set tlv").unwrap();
let gv = h.invoke_instance_method(&bt1, "get", id1, &[]).expect("get tlv").unwrap();
let _ = h
.invoke_instance_method(&bt1, "set", id1, &[Box::new(IntegerBox::new(123))])
.expect("set tlv")
.unwrap();
let gv = h
.invoke_instance_method(&bt1, "get", id1, &[])
.expect("get tlv")
.unwrap();
gv.to_string_box().value
};
// TypeBox path
@ -131,11 +210,20 @@ mod tests {
let (bt2, id2, _hold2) = create_plugin_instance("IntegerBox");
let out_tb = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt2, "set", id2, &[Box::new(IntegerBox::new(123))]).expect("set tb").unwrap();
let gv = h.invoke_instance_method(&bt2, "get", id2, &[]).expect("get tb").unwrap();
let _ = h
.invoke_instance_method(&bt2, "set", id2, &[Box::new(IntegerBox::new(123))])
.expect("set tb")
.unwrap();
let gv = h
.invoke_instance_method(&bt2, "get", id2, &[])
.expect("get tb")
.unwrap();
gv.to_string_box().value
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (IntegerBox)");
assert_eq!(
out_tlv, out_tb,
"TLV vs TypeBox results should match (IntegerBox)"
);
}
#[test]
@ -148,7 +236,9 @@ mod tests {
let (bt1, id1, _hold1) = create_plugin_instance("ConsoleBox");
let out_tlv_is_none = {
let h = host.read().unwrap();
let rv = h.invoke_instance_method(&bt1, "println", id1, &[Box::new(StringBox::new("hello"))]).expect("println tlv");
let rv = h
.invoke_instance_method(&bt1, "println", id1, &[Box::new(StringBox::new("hello"))])
.expect("println tlv");
rv.is_none()
};
// TypeBox path
@ -156,10 +246,15 @@ mod tests {
let (bt2, id2, _hold2) = create_plugin_instance("ConsoleBox");
let out_tb_is_none = {
let h = host.read().unwrap();
let rv = h.invoke_instance_method(&bt2, "println", id2, &[Box::new(StringBox::new("hello"))]).expect("println tb");
let rv = h
.invoke_instance_method(&bt2, "println", id2, &[Box::new(StringBox::new("hello"))])
.expect("println tb");
rv.is_none()
};
assert!(out_tlv_is_none && out_tb_is_none, "println should return void/None in both modes");
assert!(
out_tlv_is_none && out_tb_is_none,
"println should return void/None in both modes"
);
}
#[test]
@ -173,10 +268,22 @@ mod tests {
let (bt1, id1, _hold1) = create_plugin_instance("MathBox");
let out_tlv = {
let h = host.read().unwrap();
let s1 = h.invoke_instance_method(&bt1, "sqrt", id1, &[Box::new(IntegerBox::new(9))]).expect("sqrt tlv").unwrap();
let s2 = h.invoke_instance_method(&bt1, "sin", id1, &[Box::new(IntegerBox::new(0))]).expect("sin tlv").unwrap();
let s3 = h.invoke_instance_method(&bt1, "cos", id1, &[Box::new(IntegerBox::new(0))]).expect("cos tlv").unwrap();
let s4 = h.invoke_instance_method(&bt1, "round", id1, &[Box::new(IntegerBox::new(26))]).expect("round tlv").unwrap();
let s1 = h
.invoke_instance_method(&bt1, "sqrt", id1, &[Box::new(IntegerBox::new(9))])
.expect("sqrt tlv")
.unwrap();
let s2 = h
.invoke_instance_method(&bt1, "sin", id1, &[Box::new(IntegerBox::new(0))])
.expect("sin tlv")
.unwrap();
let s3 = h
.invoke_instance_method(&bt1, "cos", id1, &[Box::new(IntegerBox::new(0))])
.expect("cos tlv")
.unwrap();
let s4 = h
.invoke_instance_method(&bt1, "round", id1, &[Box::new(IntegerBox::new(26))])
.expect("round tlv")
.unwrap();
(
s1.to_string_box().value,
s2.to_string_box().value,
@ -190,10 +297,22 @@ mod tests {
let (bt2, id2, _hold2) = create_plugin_instance("MathBox");
let out_tb = {
let h = host.read().unwrap();
let s1 = h.invoke_instance_method(&bt2, "sqrt", id2, &[Box::new(IntegerBox::new(9))]).expect("sqrt tb").unwrap();
let s2 = h.invoke_instance_method(&bt2, "sin", id2, &[Box::new(IntegerBox::new(0))]).expect("sin tb").unwrap();
let s3 = h.invoke_instance_method(&bt2, "cos", id2, &[Box::new(IntegerBox::new(0))]).expect("cos tb").unwrap();
let s4 = h.invoke_instance_method(&bt2, "round", id2, &[Box::new(IntegerBox::new(26))]).expect("round tb").unwrap();
let s1 = h
.invoke_instance_method(&bt2, "sqrt", id2, &[Box::new(IntegerBox::new(9))])
.expect("sqrt tb")
.unwrap();
let s2 = h
.invoke_instance_method(&bt2, "sin", id2, &[Box::new(IntegerBox::new(0))])
.expect("sin tb")
.unwrap();
let s3 = h
.invoke_instance_method(&bt2, "cos", id2, &[Box::new(IntegerBox::new(0))])
.expect("cos tb")
.unwrap();
let s4 = h
.invoke_instance_method(&bt2, "round", id2, &[Box::new(IntegerBox::new(26))])
.expect("round tb")
.unwrap();
(
s1.to_string_box().value,
s2.to_string_box().value,
@ -201,7 +320,10 @@ mod tests {
s4.to_string_box().value,
)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (MathBox)");
assert_eq!(
out_tlv, out_tb,
"TLV vs TypeBox results should match (MathBox)"
);
}
#[test]
@ -223,8 +345,19 @@ mod tests {
let (bt1, id1, _hold1) = create_plugin_instance("EncodingBox");
let out_tlv = {
let h = host.read().unwrap();
let b64 = h.invoke_instance_method(&bt1, "base64Encode", id1, &[Box::new(StringBox::new("hi"))]).expect("b64 tlv").unwrap();
let hex = h.invoke_instance_method(&bt1, "hexEncode", id1, &[Box::new(StringBox::new("hi"))]).expect("hex tlv").unwrap();
let b64 = h
.invoke_instance_method(
&bt1,
"base64Encode",
id1,
&[Box::new(StringBox::new("hi"))],
)
.expect("b64 tlv")
.unwrap();
let hex = h
.invoke_instance_method(&bt1, "hexEncode", id1, &[Box::new(StringBox::new("hi"))])
.expect("hex tlv")
.unwrap();
(b64.to_string_box().value, hex.to_string_box().value)
};
@ -233,11 +366,25 @@ mod tests {
let (bt2, id2, _hold2) = create_plugin_instance("EncodingBox");
let out_tb = {
let h = host.read().unwrap();
let b64 = h.invoke_instance_method(&bt2, "base64Encode", id2, &[Box::new(StringBox::new("hi"))]).expect("b64 tb").unwrap();
let hex = h.invoke_instance_method(&bt2, "hexEncode", id2, &[Box::new(StringBox::new("hi"))]).expect("hex tb").unwrap();
let b64 = h
.invoke_instance_method(
&bt2,
"base64Encode",
id2,
&[Box::new(StringBox::new("hi"))],
)
.expect("b64 tb")
.unwrap();
let hex = h
.invoke_instance_method(&bt2, "hexEncode", id2, &[Box::new(StringBox::new("hi"))])
.expect("hex tb")
.unwrap();
(b64.to_string_box().value, hex.to_string_box().value)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (EncodingBox)");
assert_eq!(
out_tlv, out_tb,
"TLV vs TypeBox results should match (EncodingBox)"
);
}
#[test]
@ -251,9 +398,17 @@ mod tests {
let (bt1, id1, _hold1) = create_plugin_instance("RegexBox");
let out_tlv = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt1, "compile", id1, &[Box::new(StringBox::new("h.+o"))]).expect("compile tlv");
let m = h.invoke_instance_method(&bt1, "isMatch", id1, &[Box::new(StringBox::new("hello"))]).expect("isMatch tlv").unwrap();
let f = h.invoke_instance_method(&bt1, "find", id1, &[Box::new(StringBox::new("hello"))]).expect("find tlv").unwrap();
let _ = h
.invoke_instance_method(&bt1, "compile", id1, &[Box::new(StringBox::new("h.+o"))])
.expect("compile tlv");
let m = h
.invoke_instance_method(&bt1, "isMatch", id1, &[Box::new(StringBox::new("hello"))])
.expect("isMatch tlv")
.unwrap();
let f = h
.invoke_instance_method(&bt1, "find", id1, &[Box::new(StringBox::new("hello"))])
.expect("find tlv")
.unwrap();
(m.to_string_box().value, f.to_string_box().value)
};
@ -262,12 +417,23 @@ mod tests {
let (bt2, id2, _hold2) = create_plugin_instance("RegexBox");
let out_tb = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt2, "compile", id2, &[Box::new(StringBox::new("h.+o"))]).expect("compile tb");
let m = h.invoke_instance_method(&bt2, "isMatch", id2, &[Box::new(StringBox::new("hello"))]).expect("isMatch tb").unwrap();
let f = h.invoke_instance_method(&bt2, "find", id2, &[Box::new(StringBox::new("hello"))]).expect("find tb").unwrap();
let _ = h
.invoke_instance_method(&bt2, "compile", id2, &[Box::new(StringBox::new("h.+o"))])
.expect("compile tb");
let m = h
.invoke_instance_method(&bt2, "isMatch", id2, &[Box::new(StringBox::new("hello"))])
.expect("isMatch tb")
.unwrap();
let f = h
.invoke_instance_method(&bt2, "find", id2, &[Box::new(StringBox::new("hello"))])
.expect("find tb")
.unwrap();
(m.to_string_box().value, f.to_string_box().value)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (RegexBox)");
assert_eq!(
out_tlv, out_tb,
"TLV vs TypeBox results should match (RegexBox)"
);
}
#[test]
@ -281,11 +447,51 @@ mod tests {
let (bt1, id1, _hold1) = create_plugin_instance("PathBox");
let out_tlv = {
let h = host.read().unwrap();
let j = h.invoke_instance_method(&bt1, "join", id1, &[Box::new(StringBox::new("/a/b")), Box::new(StringBox::new("c.txt"))]).expect("join tlv").unwrap();
let d = h.invoke_instance_method(&bt1, "dirname", id1, &[Box::new(StringBox::new("/a/b/c.txt"))]).expect("dirname tlv").unwrap();
let b = h.invoke_instance_method(&bt1, "basename", id1, &[Box::new(StringBox::new("/a/b/c.txt"))]).expect("basename tlv").unwrap();
let n = h.invoke_instance_method(&bt1, "normalize", id1, &[Box::new(StringBox::new("/a/./b/../b/c"))]).expect("normalize tlv").unwrap();
(j.to_string_box().value, d.to_string_box().value, b.to_string_box().value, n.to_string_box().value)
let j = h
.invoke_instance_method(
&bt1,
"join",
id1,
&[
Box::new(StringBox::new("/a/b")),
Box::new(StringBox::new("c.txt")),
],
)
.expect("join tlv")
.unwrap();
let d = h
.invoke_instance_method(
&bt1,
"dirname",
id1,
&[Box::new(StringBox::new("/a/b/c.txt"))],
)
.expect("dirname tlv")
.unwrap();
let b = h
.invoke_instance_method(
&bt1,
"basename",
id1,
&[Box::new(StringBox::new("/a/b/c.txt"))],
)
.expect("basename tlv")
.unwrap();
let n = h
.invoke_instance_method(
&bt1,
"normalize",
id1,
&[Box::new(StringBox::new("/a/./b/../b/c"))],
)
.expect("normalize tlv")
.unwrap();
(
j.to_string_box().value,
d.to_string_box().value,
b.to_string_box().value,
n.to_string_box().value,
)
};
// TypeBox path
@ -293,13 +499,56 @@ mod tests {
let (bt2, id2, _hold2) = create_plugin_instance("PathBox");
let out_tb = {
let h = host.read().unwrap();
let j = h.invoke_instance_method(&bt2, "join", id2, &[Box::new(StringBox::new("/a/b")), Box::new(StringBox::new("c.txt"))]).expect("join tb").unwrap();
let d = h.invoke_instance_method(&bt2, "dirname", id2, &[Box::new(StringBox::new("/a/b/c.txt"))]).expect("dirname tb").unwrap();
let b = h.invoke_instance_method(&bt2, "basename", id2, &[Box::new(StringBox::new("/a/b/c.txt"))]).expect("basename tb").unwrap();
let n = h.invoke_instance_method(&bt2, "normalize", id2, &[Box::new(StringBox::new("/a/./b/../b/c"))]).expect("normalize tb").unwrap();
(j.to_string_box().value, d.to_string_box().value, b.to_string_box().value, n.to_string_box().value)
let j = h
.invoke_instance_method(
&bt2,
"join",
id2,
&[
Box::new(StringBox::new("/a/b")),
Box::new(StringBox::new("c.txt")),
],
)
.expect("join tb")
.unwrap();
let d = h
.invoke_instance_method(
&bt2,
"dirname",
id2,
&[Box::new(StringBox::new("/a/b/c.txt"))],
)
.expect("dirname tb")
.unwrap();
let b = h
.invoke_instance_method(
&bt2,
"basename",
id2,
&[Box::new(StringBox::new("/a/b/c.txt"))],
)
.expect("basename tb")
.unwrap();
let n = h
.invoke_instance_method(
&bt2,
"normalize",
id2,
&[Box::new(StringBox::new("/a/./b/../b/c"))],
)
.expect("normalize tb")
.unwrap();
(
j.to_string_box().value,
d.to_string_box().value,
b.to_string_box().value,
n.to_string_box().value,
)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (PathBox)");
assert_eq!(
out_tlv, out_tb,
"TLV vs TypeBox results should match (PathBox)"
);
}
#[test]
@ -314,9 +563,23 @@ mod tests {
let (bt1, id1, _hold1) = create_plugin_instance("TOMLBox");
let out_tlv = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt1, "parse", id1, &[Box::new(StringBox::new(toml_text))]).expect("parse tlv").unwrap();
let name = h.invoke_instance_method(&bt1, "get", id1, &[Box::new(StringBox::new("package.name"))]).expect("get tlv").unwrap();
let json = h.invoke_instance_method(&bt1, "toJson", id1, &[]).expect("toJson tlv").unwrap();
let _ = h
.invoke_instance_method(&bt1, "parse", id1, &[Box::new(StringBox::new(toml_text))])
.expect("parse tlv")
.unwrap();
let name = h
.invoke_instance_method(
&bt1,
"get",
id1,
&[Box::new(StringBox::new("package.name"))],
)
.expect("get tlv")
.unwrap();
let json = h
.invoke_instance_method(&bt1, "toJson", id1, &[])
.expect("toJson tlv")
.unwrap();
(name.to_string_box().value, json.to_string_box().value)
};
@ -325,12 +588,29 @@ mod tests {
let (bt2, id2, _hold2) = create_plugin_instance("TOMLBox");
let out_tb = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt2, "parse", id2, &[Box::new(StringBox::new(toml_text))]).expect("parse tb").unwrap();
let name = h.invoke_instance_method(&bt2, "get", id2, &[Box::new(StringBox::new("package.name"))]).expect("get tb").unwrap();
let json = h.invoke_instance_method(&bt2, "toJson", id2, &[]).expect("toJson tb").unwrap();
let _ = h
.invoke_instance_method(&bt2, "parse", id2, &[Box::new(StringBox::new(toml_text))])
.expect("parse tb")
.unwrap();
let name = h
.invoke_instance_method(
&bt2,
"get",
id2,
&[Box::new(StringBox::new("package.name"))],
)
.expect("get tb")
.unwrap();
let json = h
.invoke_instance_method(&bt2, "toJson", id2, &[])
.expect("toJson tb")
.unwrap();
(name.to_string_box().value, json.to_string_box().value)
};
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (TOMLBox)");
assert_eq!(
out_tlv, out_tb,
"TLV vs TypeBox results should match (TOMLBox)"
);
}
#[test]
@ -344,7 +624,10 @@ mod tests {
let (bt1, id1, _hold1) = create_plugin_instance("TimeBox");
let t_tlv = {
let h = host.read().unwrap();
let v = h.invoke_instance_method(&bt1, "now", id1, &[]).expect("now tlv").unwrap();
let v = h
.invoke_instance_method(&bt1, "now", id1, &[])
.expect("now tlv")
.unwrap();
v.to_string_box().value.parse::<i64>().unwrap_or(0)
};
@ -353,7 +636,10 @@ mod tests {
let (bt2, id2, _hold2) = create_plugin_instance("TimeBox");
let t_tb = {
let h = host.read().unwrap();
let v = h.invoke_instance_method(&bt2, "now", id2, &[]).expect("now tb").unwrap();
let v = h
.invoke_instance_method(&bt2, "now", id2, &[])
.expect("now tb")
.unwrap();
v.to_string_box().value.parse::<i64>().unwrap_or(0)
};
@ -372,10 +658,21 @@ mod tests {
let (bt1, id1, _hold1) = create_plugin_instance("CounterBox");
let (a1, b1) = {
let h = host.read().unwrap();
let a = h.invoke_instance_method(&bt1, "get", id1, &[]).expect("get tlv").unwrap();
let _ = h.invoke_instance_method(&bt1, "inc", id1, &[]).expect("inc tlv");
let b = h.invoke_instance_method(&bt1, "get", id1, &[]).expect("get2 tlv").unwrap();
(a.to_string_box().value.parse::<i64>().unwrap_or(0), b.to_string_box().value.parse::<i64>().unwrap_or(0))
let a = h
.invoke_instance_method(&bt1, "get", id1, &[])
.expect("get tlv")
.unwrap();
let _ = h
.invoke_instance_method(&bt1, "inc", id1, &[])
.expect("inc tlv");
let b = h
.invoke_instance_method(&bt1, "get", id1, &[])
.expect("get2 tlv")
.unwrap();
(
a.to_string_box().value.parse::<i64>().unwrap_or(0),
b.to_string_box().value.parse::<i64>().unwrap_or(0),
)
};
assert_eq!(b1 - a1, 1, "CounterBox TLV should increment by 1");
@ -384,10 +681,21 @@ mod tests {
let (bt2, id2, _hold2) = create_plugin_instance("CounterBox");
let (a2, b2) = {
let h = host.read().unwrap();
let a = h.invoke_instance_method(&bt2, "get", id2, &[]).expect("get tb").unwrap();
let _ = h.invoke_instance_method(&bt2, "inc", id2, &[]).expect("inc tb");
let b = h.invoke_instance_method(&bt2, "get", id2, &[]).expect("get2 tb").unwrap();
(a.to_string_box().value.parse::<i64>().unwrap_or(0), b.to_string_box().value.parse::<i64>().unwrap_or(0))
let a = h
.invoke_instance_method(&bt2, "get", id2, &[])
.expect("get tb")
.unwrap();
let _ = h
.invoke_instance_method(&bt2, "inc", id2, &[])
.expect("inc tb");
let b = h
.invoke_instance_method(&bt2, "get", id2, &[])
.expect("get2 tb")
.unwrap();
(
a.to_string_box().value.parse::<i64>().unwrap_or(0),
b.to_string_box().value.parse::<i64>().unwrap_or(0),
)
};
assert_eq!(b2 - a2, 1, "CounterBox TypeBox should increment by 1");
}
@ -400,7 +708,11 @@ mod tests {
// Prepare temp file path
let mut p = std::env::temp_dir();
p.push(format!("nyash_test_{}_{}.txt", std::process::id(), rand_id())) ;
p.push(format!(
"nyash_test_{}_{}.txt",
std::process::id(),
rand_id()
));
let path_str = p.to_string_lossy().to_string();
// TLV path
@ -408,13 +720,42 @@ mod tests {
let (bt1, id1, _hold1) = create_plugin_instance("FileBox");
let out_tlv = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt1, "open", id1, &[Box::new(StringBox::new(&path_str)), Box::new(StringBox::new("w"))]).expect("open tlv");
let _ = h.invoke_instance_method(&bt1, "write", id1, &[Box::new(StringBox::new("hello"))]).expect("write tlv");
let _ = h.invoke_instance_method(&bt1, "close", id1, &[]).expect("close tlv");
let _ = h
.invoke_instance_method(
&bt1,
"open",
id1,
&[
Box::new(StringBox::new(&path_str)),
Box::new(StringBox::new("w")),
],
)
.expect("open tlv");
let _ = h
.invoke_instance_method(&bt1, "write", id1, &[Box::new(StringBox::new("hello"))])
.expect("write tlv");
let _ = h
.invoke_instance_method(&bt1, "close", id1, &[])
.expect("close tlv");
// reopen and read
let _ = h.invoke_instance_method(&bt1, "open", id1, &[Box::new(StringBox::new(&path_str)), Box::new(StringBox::new("r"))]).expect("open2 tlv");
let rd = h.invoke_instance_method(&bt1, "read", id1, &[]).expect("read tlv").unwrap();
let _ = h.invoke_instance_method(&bt1, "close", id1, &[]).expect("close2 tlv");
let _ = h
.invoke_instance_method(
&bt1,
"open",
id1,
&[
Box::new(StringBox::new(&path_str)),
Box::new(StringBox::new("r")),
],
)
.expect("open2 tlv");
let rd = h
.invoke_instance_method(&bt1, "read", id1, &[])
.expect("read tlv")
.unwrap();
let _ = h
.invoke_instance_method(&bt1, "close", id1, &[])
.expect("close2 tlv");
rd.to_string_box().value
};
@ -423,19 +764,51 @@ mod tests {
let (bt2, id2, _hold2) = create_plugin_instance("FileBox");
let out_tb = {
let h = host.read().unwrap();
let _ = h.invoke_instance_method(&bt2, "open", id2, &[Box::new(StringBox::new(&path_str)), Box::new(StringBox::new("w"))]).expect("open tb");
let _ = h.invoke_instance_method(&bt2, "write", id2, &[Box::new(StringBox::new("hello"))]).expect("write tb");
let _ = h.invoke_instance_method(&bt2, "close", id2, &[]).expect("close tb");
let _ = h.invoke_instance_method(&bt2, "open", id2, &[Box::new(StringBox::new(&path_str)), Box::new(StringBox::new("r"))]).expect("open2 tb");
let rd = h.invoke_instance_method(&bt2, "read", id2, &[]).expect("read tb").unwrap();
let _ = h.invoke_instance_method(&bt2, "close", id2, &[]).expect("close2 tb");
let _ = h
.invoke_instance_method(
&bt2,
"open",
id2,
&[
Box::new(StringBox::new(&path_str)),
Box::new(StringBox::new("w")),
],
)
.expect("open tb");
let _ = h
.invoke_instance_method(&bt2, "write", id2, &[Box::new(StringBox::new("hello"))])
.expect("write tb");
let _ = h
.invoke_instance_method(&bt2, "close", id2, &[])
.expect("close tb");
let _ = h
.invoke_instance_method(
&bt2,
"open",
id2,
&[
Box::new(StringBox::new(&path_str)),
Box::new(StringBox::new("r")),
],
)
.expect("open2 tb");
let rd = h
.invoke_instance_method(&bt2, "read", id2, &[])
.expect("read tb")
.unwrap();
let _ = h
.invoke_instance_method(&bt2, "close", id2, &[])
.expect("close2 tb");
rd.to_string_box().value
};
// Cleanup best-effort
let _ = fs::remove_file(&path_str);
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match (FileBox)");
assert_eq!(
out_tlv, out_tb,
"TLV vs TypeBox results should match (FileBox)"
);
}
fn rand_id() -> u64 {

View File

@ -1,61 +1,190 @@
#[test]
fn vtable_array_push_get_len_pop_clear() {
use crate::backend::vm::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType};
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
std::env::set_var("NYASH_ABI_VTABLE", "1");
// Case 1: push("x"); get(0)
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::String,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let arr = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: arr, box_type: "ArrayBox".into(), args: vec![] });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: arr,
box_type: "ArrayBox".into(),
args: vec![],
});
let sval = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: sval, value: ConstValue::String("x".into()) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "push".into(), args: vec![sval], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: sval,
value: ConstValue::String("x".into()),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: arr,
method: "push".into(),
args: vec![sval],
method_id: None,
effects: EffectMask::PURE,
});
let idx0 = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: idx0, value: ConstValue::Integer(0) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: idx0,
value: ConstValue::Integer(0),
});
let got = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(got), box_val: arr, method: "get".into(), args: vec![idx0], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(got) });
let mut m = MirModule::new("arr_push_get".into()); m.add_function(f);
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(got),
box_val: arr,
method: "get".into(),
args: vec![idx0],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(got) });
let mut m = MirModule::new("arr_push_get".into());
m.add_function(f);
let mut vm = VM::new();
let out = vm.execute_module(&m).expect("vm exec");
assert_eq!(out.to_string_box().value, "x");
// Case 2: push("y"); pop() -> "y"
let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE };
let sig2 = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::String,
effects: EffectMask::PURE,
};
let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0));
let bb2 = f2.entry_block;
let a2 = f2.next_value_id();
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: a2, box_type: "ArrayBox".into(), args: vec![] });
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: a2,
box_type: "ArrayBox".into(),
args: vec![],
});
let y = f2.next_value_id();
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: y, value: ConstValue::String("y".into()) });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a2, method: "push".into(), args: vec![y], method_id: None, effects: EffectMask::PURE });
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: y,
value: ConstValue::String("y".into()),
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a2,
method: "push".into(),
args: vec![y],
method_id: None,
effects: EffectMask::PURE,
});
let popped = f2.next_value_id();
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(popped), box_val: a2, method: "pop".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: Some(popped) });
let mut m2 = MirModule::new("arr_pop".into()); m2.add_function(f2);
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(popped),
box_val: a2,
method: "pop".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Return {
value: Some(popped),
});
let mut m2 = MirModule::new("arr_pop".into());
m2.add_function(f2);
let mut vm2 = VM::new();
let out2 = vm2.execute_module(&m2).expect("vm exec");
assert_eq!(out2.to_string_box().value, "y");
// Case 3: push("z"); clear(); len() -> 0
let sig3 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig3 = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f3 = MirFunction::new(sig3, BasicBlockId::new(0));
let bb3 = f3.entry_block;
let a3 = f3.next_value_id();
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::NewBox { dst: a3, box_type: "ArrayBox".into(), args: vec![] });
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: a3,
box_type: "ArrayBox".into(),
args: vec![],
});
let z = f3.next_value_id();
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: z, value: ConstValue::String("z".into()) });
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "push".into(), args: vec![z], method_id: None, effects: EffectMask::PURE });
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "clear".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: z,
value: ConstValue::String("z".into()),
});
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a3,
method: "push".into(),
args: vec![z],
method_id: None,
effects: EffectMask::PURE,
});
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a3,
method: "clear".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
let ln = f3.next_value_id();
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(ln), box_val: a3, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Return { value: Some(ln) });
let mut m3 = MirModule::new("arr_clear_len".into()); m3.add_function(f3);
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(ln),
box_val: a3,
method: "len".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(ln) });
let mut m3 = MirModule::new("arr_clear_len".into());
m3.add_function(f3);
let mut vm3 = VM::new();
let out3 = vm3.execute_module(&m3).expect("vm exec");
assert_eq!(out3.to_string_box().value, "0");
}

View File

@ -1,77 +1,350 @@
#[test]
fn vtable_array_contains_indexof_join() {
use crate::backend::vm::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType};
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
std::env::set_var("NYASH_ABI_VTABLE", "1");
// contains: ["a","b"].contains("b") == true; contains("c") == false
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let arr = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: arr, box_type: "ArrayBox".into(), args: vec![] });
let sa = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: sa, value: ConstValue::String("a".into()) });
let sb = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: sb, value: ConstValue::String("b".into()) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "push".into(), args: vec![sa], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "push".into(), args: vec![sb], method_id: None, effects: EffectMask::PURE });
let sc = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: sc, value: ConstValue::String("c".into()) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: arr,
box_type: "ArrayBox".into(),
args: vec![],
});
let sa = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: sa,
value: ConstValue::String("a".into()),
});
let sb = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: sb,
value: ConstValue::String("b".into()),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: arr,
method: "push".into(),
args: vec![sa],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: arr,
method: "push".into(),
args: vec![sb],
method_id: None,
effects: EffectMask::PURE,
});
let sc = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: sc,
value: ConstValue::String("c".into()),
});
let got1 = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(got1), box_val: arr, method: "contains".into(), args: vec![sb], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(got1),
box_val: arr,
method: "contains".into(),
args: vec![sb],
method_id: None,
effects: EffectMask::PURE,
});
let got2 = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(got2), box_val: arr, method: "contains".into(), args: vec![sc], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(got2),
box_val: arr,
method: "contains".into(),
args: vec![sc],
method_id: None,
effects: EffectMask::PURE,
});
// return got1.equals(true) && got2.equals(false) as 1 for pass
// Instead, just return 0 or 1 using simple branch-like comparison via toString
// We check: got1==true -> "true", got2==false -> "false" and return 1 if both match else 0
// For brevity, just return got1.toString() ("true") length + got2.toString() ("false") length == 9
let s1 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(s1), box_val: got1, method: "toString".into(), args: vec![], method_id: Some(0), effects: EffectMask::PURE });
let s2 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(s2), box_val: got2, method: "toString".into(), args: vec![], method_id: Some(0), effects: EffectMask::PURE });
let len1 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(len1), box_val: s1, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
let len2 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(len2), box_val: s2, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
let s1 = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(s1),
box_val: got1,
method: "toString".into(),
args: vec![],
method_id: Some(0),
effects: EffectMask::PURE,
});
let s2 = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(s2),
box_val: got2,
method: "toString".into(),
args: vec![],
method_id: Some(0),
effects: EffectMask::PURE,
});
let len1 = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(len1),
box_val: s1,
method: "len".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
let len2 = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(len2),
box_val: s2,
method: "len".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
// len1 + len2
let sum = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: sum, op: crate::mir::BinaryOp::Add, lhs: len1, rhs: len2 });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(sum) });
let mut m = MirModule::new("arr_contains".into()); m.add_function(f);
let sum = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BinOp {
dst: sum,
op: crate::mir::BinaryOp::Add,
lhs: len1,
rhs: len2,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(sum) });
let mut m = MirModule::new("arr_contains".into());
m.add_function(f);
let mut vm = VM::new();
let out = vm.execute_module(&m).expect("vm exec");
assert_eq!(out.to_string_box().value, "9"); // "true"(4)+"false"(5)
// indexOf: ["x","y"].indexOf("y") == 1; indexOf("z") == -1
let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig2 = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0));
let bb2 = f2.entry_block;
let a2 = f2.next_value_id();
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: a2, box_type: "ArrayBox".into(), args: vec![] });
let sx = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: sx, value: ConstValue::String("x".into()) });
let sy = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: sy, value: ConstValue::String("y".into()) });
let sz = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: sz, value: ConstValue::String("z".into()) });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a2, method: "push".into(), args: vec![sx], method_id: None, effects: EffectMask::PURE });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a2, method: "push".into(), args: vec![sy], method_id: None, effects: EffectMask::PURE });
let i1 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(i1), box_val: a2, method: "indexOf".into(), args: vec![sy], method_id: None, effects: EffectMask::PURE });
let i2 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(i2), box_val: a2, method: "indexOf".into(), args: vec![sz], method_id: None, effects: EffectMask::PURE });
let sum2 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BinOp { dst: sum2, op: crate::mir::BinaryOp::Add, lhs: i1, rhs: i2 });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: Some(sum2) });
let mut m2 = MirModule::new("arr_indexOf".into()); m2.add_function(f2);
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: a2,
box_type: "ArrayBox".into(),
args: vec![],
});
let sx = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: sx,
value: ConstValue::String("x".into()),
});
let sy = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: sy,
value: ConstValue::String("y".into()),
});
let sz = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: sz,
value: ConstValue::String("z".into()),
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a2,
method: "push".into(),
args: vec![sx],
method_id: None,
effects: EffectMask::PURE,
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a2,
method: "push".into(),
args: vec![sy],
method_id: None,
effects: EffectMask::PURE,
});
let i1 = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(i1),
box_val: a2,
method: "indexOf".into(),
args: vec![sy],
method_id: None,
effects: EffectMask::PURE,
});
let i2 = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(i2),
box_val: a2,
method: "indexOf".into(),
args: vec![sz],
method_id: None,
effects: EffectMask::PURE,
});
let sum2 = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BinOp {
dst: sum2,
op: crate::mir::BinaryOp::Add,
lhs: i1,
rhs: i2,
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(sum2) });
let mut m2 = MirModule::new("arr_indexOf".into());
m2.add_function(f2);
let mut vm2 = VM::new();
let out2 = vm2.execute_module(&m2).expect("vm exec");
assert_eq!(out2.to_string_box().value, "0"); // 1 + (-1)
// join: ["a","b","c"].join("-") == "a-b-c"
let sig3 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE };
let sig3 = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::String,
effects: EffectMask::PURE,
};
let mut f3 = MirFunction::new(sig3, BasicBlockId::new(0));
let bb3 = f3.entry_block;
let a3 = f3.next_value_id();
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::NewBox { dst: a3, box_type: "ArrayBox".into(), args: vec![] });
let a = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: a, value: ConstValue::String("a".into()) });
let b = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: b, value: ConstValue::String("b".into()) });
let c = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: c, value: ConstValue::String("c".into()) });
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "push".into(), args: vec![a], method_id: None, effects: EffectMask::PURE });
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "push".into(), args: vec![b], method_id: None, effects: EffectMask::PURE });
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "push".into(), args: vec![c], method_id: None, effects: EffectMask::PURE });
let sep = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: sep, value: ConstValue::String("-".into()) });
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: a3,
box_type: "ArrayBox".into(),
args: vec![],
});
let a = f3.next_value_id();
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: a,
value: ConstValue::String("a".into()),
});
let b = f3.next_value_id();
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: b,
value: ConstValue::String("b".into()),
});
let c = f3.next_value_id();
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: c,
value: ConstValue::String("c".into()),
});
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a3,
method: "push".into(),
args: vec![a],
method_id: None,
effects: EffectMask::PURE,
});
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a3,
method: "push".into(),
args: vec![b],
method_id: None,
effects: EffectMask::PURE,
});
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a3,
method: "push".into(),
args: vec![c],
method_id: None,
effects: EffectMask::PURE,
});
let sep = f3.next_value_id();
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: sep,
value: ConstValue::String("-".into()),
});
let joined = f3.next_value_id();
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(joined), box_val: a3, method: "join".into(), args: vec![sep], method_id: None, effects: EffectMask::PURE });
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Return { value: Some(joined) });
let mut m3 = MirModule::new("arr_join".into()); m3.add_function(f3);
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(joined),
box_val: a3,
method: "join".into(),
args: vec![sep],
method_id: None,
effects: EffectMask::PURE,
});
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Return {
value: Some(joined),
});
let mut m3 = MirModule::new("arr_join".into());
m3.add_function(f3);
let mut vm3 = VM::new();
let out3 = vm3.execute_module(&m3).expect("vm exec");
assert_eq!(out3.to_string_box().value, "a-b-c");

View File

@ -1,73 +1,315 @@
#[test]
fn vtable_array_sort_reverse_slice() {
use crate::backend::vm::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType};
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
std::env::set_var("NYASH_ABI_VTABLE", "1");
// sort: push 3,1,2 -> sort() -> get(0) == 1
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let arr = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: arr, box_type: "ArrayBox".into(), args: vec![] });
let c3 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: c3, value: ConstValue::Integer(3) });
let c1 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: c1, value: ConstValue::Integer(1) });
let c2 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: c2, value: ConstValue::Integer(2) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "push".into(), args: vec![c3], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "push".into(), args: vec![c1], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "push".into(), args: vec![c2], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "sort".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
let idx0 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: idx0, value: ConstValue::Integer(0) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: arr,
box_type: "ArrayBox".into(),
args: vec![],
});
let c3 = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: c3,
value: ConstValue::Integer(3),
});
let c1 = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: c1,
value: ConstValue::Integer(1),
});
let c2 = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: c2,
value: ConstValue::Integer(2),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: arr,
method: "push".into(),
args: vec![c3],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: arr,
method: "push".into(),
args: vec![c1],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: arr,
method: "push".into(),
args: vec![c2],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: arr,
method: "sort".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
let idx0 = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: idx0,
value: ConstValue::Integer(0),
});
let got = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(got), box_val: arr, method: "get".into(), args: vec![idx0], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(got) });
let mut m = MirModule::new("arr_sort".into()); m.add_function(f);
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(got),
box_val: arr,
method: "get".into(),
args: vec![idx0],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(got) });
let mut m = MirModule::new("arr_sort".into());
m.add_function(f);
let mut vm = VM::new();
let out = vm.execute_module(&m).expect("vm exec");
assert_eq!(out.to_string_box().value, "1");
// reverse: push 1,2 -> reverse() -> get(0) == 2
let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig2 = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0));
let bb2 = f2.entry_block;
let a2 = f2.next_value_id();
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: a2, box_type: "ArrayBox".into(), args: vec![] });
let i1 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: i1, value: ConstValue::Integer(1) });
let i2 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: i2, value: ConstValue::Integer(2) });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a2, method: "push".into(), args: vec![i1], method_id: None, effects: EffectMask::PURE });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a2, method: "push".into(), args: vec![i2], method_id: None, effects: EffectMask::PURE });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a2, method: "reverse".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
let z0 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: z0, value: ConstValue::Integer(0) });
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: a2,
box_type: "ArrayBox".into(),
args: vec![],
});
let i1 = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: i1,
value: ConstValue::Integer(1),
});
let i2 = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: i2,
value: ConstValue::Integer(2),
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a2,
method: "push".into(),
args: vec![i1],
method_id: None,
effects: EffectMask::PURE,
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a2,
method: "push".into(),
args: vec![i2],
method_id: None,
effects: EffectMask::PURE,
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a2,
method: "reverse".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
let z0 = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: z0,
value: ConstValue::Integer(0),
});
let g2 = f2.next_value_id();
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(g2), box_val: a2, method: "get".into(), args: vec![z0], method_id: None, effects: EffectMask::PURE });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: Some(g2) });
let mut m2 = MirModule::new("arr_reverse".into()); m2.add_function(f2);
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(g2),
box_val: a2,
method: "get".into(),
args: vec![z0],
method_id: None,
effects: EffectMask::PURE,
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(g2) });
let mut m2 = MirModule::new("arr_reverse".into());
m2.add_function(f2);
let mut vm2 = VM::new();
let out2 = vm2.execute_module(&m2).expect("vm exec");
assert_eq!(out2.to_string_box().value, "2");
// slice: push "a","b","c" -> slice(0,2) -> len()==2
let sig3 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig3 = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f3 = MirFunction::new(sig3, BasicBlockId::new(0));
let bb3 = f3.entry_block;
let a3 = f3.next_value_id();
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::NewBox { dst: a3, box_type: "ArrayBox".into(), args: vec![] });
let sa = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: sa, value: ConstValue::String("a".into()) });
let sb = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: sb, value: ConstValue::String("b".into()) });
let sc = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: sc, value: ConstValue::String("c".into()) });
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "push".into(), args: vec![sa], method_id: None, effects: EffectMask::PURE });
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "push".into(), args: vec![sb], method_id: None, effects: EffectMask::PURE });
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: a3, method: "push".into(), args: vec![sc], method_id: None, effects: EffectMask::PURE });
let s0 = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: s0, value: ConstValue::Integer(0) });
let s2 = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: s2, value: ConstValue::Integer(2) });
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: a3,
box_type: "ArrayBox".into(),
args: vec![],
});
let sa = f3.next_value_id();
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: sa,
value: ConstValue::String("a".into()),
});
let sb = f3.next_value_id();
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: sb,
value: ConstValue::String("b".into()),
});
let sc = f3.next_value_id();
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: sc,
value: ConstValue::String("c".into()),
});
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a3,
method: "push".into(),
args: vec![sa],
method_id: None,
effects: EffectMask::PURE,
});
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a3,
method: "push".into(),
args: vec![sb],
method_id: None,
effects: EffectMask::PURE,
});
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: a3,
method: "push".into(),
args: vec![sc],
method_id: None,
effects: EffectMask::PURE,
});
let s0 = f3.next_value_id();
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: s0,
value: ConstValue::Integer(0),
});
let s2 = f3.next_value_id();
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: s2,
value: ConstValue::Integer(2),
});
let sub = f3.next_value_id();
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(sub), box_val: a3, method: "slice".into(), args: vec![s0, s2], method_id: None, effects: EffectMask::PURE });
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(sub),
box_val: a3,
method: "slice".into(),
args: vec![s0, s2],
method_id: None,
effects: EffectMask::PURE,
});
let ln = f3.next_value_id();
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(ln), box_val: sub, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Return { value: Some(ln) });
let mut m3 = MirModule::new("arr_slice".into()); m3.add_function(f3);
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(ln),
box_val: sub,
method: "len".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(ln) });
let mut m3 = MirModule::new("arr_slice".into());
m3.add_function(f3);
let mut vm3 = VM::new();
let out3 = vm3.execute_module(&m3).expect("vm exec");
assert_eq!(out3.to_string_box().value, "2");
}

View File

@ -1,45 +1,126 @@
#[test]
fn vtable_array_and_string_len_get_set() {
use crate::backend::vm::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType};
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
std::env::set_var("NYASH_ABI_VTABLE", "1");
// Array: set(0, "x"); len(); get(0)
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::String,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let arr = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: arr, box_type: "ArrayBox".into(), args: vec![] });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: arr,
box_type: "ArrayBox".into(),
args: vec![],
});
let idx0 = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: idx0, value: ConstValue::Integer(0) });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: idx0,
value: ConstValue::Integer(0),
});
let sval = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: sval, value: ConstValue::String("x".into()) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: arr, method: "set".into(), args: vec![idx0, sval], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: sval,
value: ConstValue::String("x".into()),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: arr,
method: "set".into(),
args: vec![idx0, sval],
method_id: None,
effects: EffectMask::PURE,
});
let lenv = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(lenv), box_val: arr, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(lenv),
box_val: arr,
method: "len".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
// sanity: len should be 1 (not asserted here, just exercise path)
let got = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(got), box_val: arr, method: "get".into(), args: vec![idx0], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(got) });
let mut m = MirModule::new("tarr".into()); m.add_function(f);
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(got),
box_val: arr,
method: "get".into(),
args: vec![idx0],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(got) });
let mut m = MirModule::new("tarr".into());
m.add_function(f);
let mut vm = VM::new();
let out = vm.execute_module(&m).expect("vm exec");
assert_eq!(out.to_string_box().value, "x");
// String: len()
let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig2 = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0));
let bb2 = f2.entry_block;
let s = f2.next_value_id();
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: s, value: ConstValue::String("abc".into()) });
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: s,
value: ConstValue::String("abc".into()),
});
let sb = f2.next_value_id();
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: sb, box_type: "StringBox".into(), args: vec![s] });
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: sb,
box_type: "StringBox".into(),
args: vec![s],
});
let ln = f2.next_value_id();
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(ln), box_val: sb, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: Some(ln) });
let mut m2 = MirModule::new("tstr".into()); m2.add_function(f2);
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(ln),
box_val: sb,
method: "len".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(ln) });
let mut m2 = MirModule::new("tstr".into());
m2.add_function(f2);
let mut vm2 = VM::new();
let out2 = vm2.execute_module(&m2).expect("vm exec");
assert_eq!(out2.to_string_box().value, "3");
}

View File

@ -1,22 +1,68 @@
#[test]
fn vtable_console_log_clear_smoke() {
use crate::backend::vm::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType};
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
std::env::set_var("NYASH_ABI_VTABLE", "1");
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let con = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: con, box_type: "ConsoleBox".into(), args: vec![] });
let msg = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: msg, value: ConstValue::String("hi".into()) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: con, method: "log".into(), args: vec![msg], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: con, method: "clear".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
let zero = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: zero, value: ConstValue::Integer(0) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(zero) });
let mut m = MirModule::new("console_smoke".into()); m.add_function(f);
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: con,
box_type: "ConsoleBox".into(),
args: vec![],
});
let msg = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: msg,
value: ConstValue::String("hi".into()),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: con,
method: "log".into(),
args: vec![msg],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: con,
method: "clear".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
let zero = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: zero,
value: ConstValue::Integer(0),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(zero) });
let mut m = MirModule::new("console_smoke".into());
m.add_function(f);
let mut vm = VM::new();
let out = vm.execute_module(&m).expect("vm exec");
assert_eq!(out.to_string_box().value, "0");
}

View File

@ -1,51 +1,226 @@
#[test]
fn vtable_map_keys_values_delete_clear() {
use crate::backend::vm::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType};
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
std::env::set_var("NYASH_ABI_VTABLE", "1");
// keys/values size check
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let m = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: m, box_type: "MapBox".into(), args: vec![] });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: m,
box_type: "MapBox".into(),
args: vec![],
});
// set two entries
let k1 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k1, value: ConstValue::String("a".into()) });
let v1 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v1, value: ConstValue::Integer(1) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m, method: "set".into(), args: vec![k1, v1], method_id: None, effects: EffectMask::PURE });
let k2 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k2, value: ConstValue::String("b".into()) });
let v2 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v2, value: ConstValue::Integer(2) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m, method: "set".into(), args: vec![k2, v2], method_id: None, effects: EffectMask::PURE });
let k1 = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: k1,
value: ConstValue::String("a".into()),
});
let v1 = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v1,
value: ConstValue::Integer(1),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: m,
method: "set".into(),
args: vec![k1, v1],
method_id: None,
effects: EffectMask::PURE,
});
let k2 = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: k2,
value: ConstValue::String("b".into()),
});
let v2 = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v2,
value: ConstValue::Integer(2),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: m,
method: "set".into(),
args: vec![k2, v2],
method_id: None,
effects: EffectMask::PURE,
});
// keys().len + values().len == 4
let keys = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(keys), box_val: m, method: "keys".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
let klen = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(klen), box_val: keys, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
let vals = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(vals), box_val: m, method: "values".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
let vlen = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(vlen), box_val: vals, method: "len".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
let sum = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: sum, op: crate::mir::BinaryOp::Add, lhs: klen, rhs: vlen });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(sum) });
let mut m1 = MirModule::new("map_keys_values".into()); m1.add_function(f);
let keys = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(keys),
box_val: m,
method: "keys".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
let klen = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(klen),
box_val: keys,
method: "len".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
let vals = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(vals),
box_val: m,
method: "values".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
let vlen = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(vlen),
box_val: vals,
method: "len".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
let sum = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BinOp {
dst: sum,
op: crate::mir::BinaryOp::Add,
lhs: klen,
rhs: vlen,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(sum) });
let mut m1 = MirModule::new("map_keys_values".into());
m1.add_function(f);
let mut vm1 = VM::new();
let out1 = vm1.execute_module(&m1).expect("vm exec");
assert_eq!(out1.to_string_box().value, "4");
// delete + clear → size 0
let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig2 = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0));
let bb2 = f2.entry_block;
let m2v = f2.next_value_id();
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: m2v, box_type: "MapBox".into(), args: vec![] });
let k = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: k, value: ConstValue::String("x".into()) });
let v = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: v, value: ConstValue::String("y".into()) });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m2v, method: "set".into(), args: vec![k, v], method_id: None, effects: EffectMask::PURE });
let dk = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: dk, value: ConstValue::String("x".into()) });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m2v, method: "delete".into(), args: vec![dk], method_id: None, effects: EffectMask::PURE });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m2v, method: "clear".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
let sz = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(sz), box_val: m2v, method: "size".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: Some(sz) });
let mut mm2 = MirModule::new("map_delete_clear".into()); mm2.add_function(f2);
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: m2v,
box_type: "MapBox".into(),
args: vec![],
});
let k = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: k,
value: ConstValue::String("x".into()),
});
let v = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v,
value: ConstValue::String("y".into()),
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: m2v,
method: "set".into(),
args: vec![k, v],
method_id: None,
effects: EffectMask::PURE,
});
let dk = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: dk,
value: ConstValue::String("x".into()),
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: m2v,
method: "delete".into(),
args: vec![dk],
method_id: None,
effects: EffectMask::PURE,
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: m2v,
method: "clear".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
let sz = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(sz),
box_val: m2v,
method: "size".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(sz) });
let mut mm2 = MirModule::new("map_delete_clear".into());
mm2.add_function(f2);
let mut vm2 = VM::new();
let out2 = vm2.execute_module(&mm2).expect("vm exec");
assert_eq!(out2.to_string_box().value, "0");
}

View File

@ -1,40 +1,109 @@
#[test]
fn vtable_map_set_and_strict_unknown() {
use crate::backend::vm::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType};
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
std::env::set_var("NYASH_ABI_VTABLE", "1");
// Build: new MapBox; call set("k","v"); size(); return size
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let mapv = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: mapv, box_type: "MapBox".into(), args: vec![] });
let k = f.next_value_id(); let v = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k, value: ConstValue::String("k".into()) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v, value: ConstValue::String("v".into()) });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: mapv, method: "set".into(), args: vec![k, v], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: mapv,
box_type: "MapBox".into(),
args: vec![],
});
let k = f.next_value_id();
let v = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: k,
value: ConstValue::String("k".into()),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: v,
value: ConstValue::String("v".into()),
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: mapv,
method: "set".into(),
args: vec![k, v],
method_id: None,
effects: EffectMask::PURE,
});
let sz = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(sz), box_val: mapv, method: "size".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(sz) });
let mut m = MirModule::new("t".into()); m.add_function(f);
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(sz),
box_val: mapv,
method: "size".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(sz) });
let mut m = MirModule::new("t".into());
m.add_function(f);
let mut vm = VM::new();
let out = vm.execute_module(&m).expect("vm exec");
let s = out.to_string_box().value; assert_eq!(s, "1");
let s = out.to_string_box().value;
assert_eq!(s, "1");
// STRICT unknown method on MapBox should error
std::env::set_var("NYASH_ABI_STRICT", "1");
let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Void, effects: EffectMask::PURE };
let sig2 = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Void,
effects: EffectMask::PURE,
};
let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0));
let bb2 = f2.entry_block;
let m2 = f2.next_value_id();
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: m2, box_type: "MapBox".into(), args: vec![] });
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: m2,
box_type: "MapBox".into(),
args: vec![],
});
// Call unknown method
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m2, method: "unknown".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: None });
let mut mm = MirModule::new("t2".into()); mm.add_function(f2);
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: None,
box_val: m2,
method: "unknown".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Return { value: None });
let mut mm = MirModule::new("t2".into());
mm.add_function(f2);
let mut vm2 = VM::new();
let res = vm2.execute_module(&mm);
assert!(res.is_err(), "STRICT should error on unknown vtable method");
}

View File

@ -1,38 +1,120 @@
#[test]
fn vtable_string_substring_concat() {
use crate::backend::vm::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType};
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
std::env::set_var("NYASH_ABI_VTABLE", "1");
// substring: "hello".substring(1,4) == "ell"
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::String,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let s = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: s, value: ConstValue::String("hello".into()) });
let sb = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: sb, box_type: "StringBox".into(), args: vec![s] });
let i1 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: i1, value: ConstValue::Integer(1) });
let i4 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: i4, value: ConstValue::Integer(4) });
let s = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: s,
value: ConstValue::String("hello".into()),
});
let sb = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: sb,
box_type: "StringBox".into(),
args: vec![s],
});
let i1 = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: i1,
value: ConstValue::Integer(1),
});
let i4 = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: i4,
value: ConstValue::Integer(4),
});
let sub = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(sub), box_val: sb, method: "substring".into(), args: vec![i1, i4], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(sub) });
let mut m = MirModule::new("str_sub".into()); m.add_function(f);
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(sub),
box_val: sb,
method: "substring".into(),
args: vec![i1, i4],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(sub) });
let mut m = MirModule::new("str_sub".into());
m.add_function(f);
let mut vm = VM::new();
let out = vm.execute_module(&m).expect("vm exec");
assert_eq!(out.to_string_box().value, "ell");
// concat: "ab".concat("cd") == "abcd"
let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE };
let sig2 = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::String,
effects: EffectMask::PURE,
};
let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0));
let bb2 = f2.entry_block;
let a = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: a, value: ConstValue::String("ab".into()) });
let ab = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: ab, box_type: "StringBox".into(), args: vec![a] });
let c = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: c, value: ConstValue::String("cd".into()) });
let a = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: a,
value: ConstValue::String("ab".into()),
});
let ab = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: ab,
box_type: "StringBox".into(),
args: vec![a],
});
let c = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: c,
value: ConstValue::String("cd".into()),
});
let joined = f2.next_value_id();
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(joined), box_val: ab, method: "concat".into(), args: vec![c], method_id: None, effects: EffectMask::PURE });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: Some(joined) });
let mut m2 = MirModule::new("str_concat".into()); m2.add_function(f2);
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(joined),
box_val: ab,
method: "concat".into(),
args: vec![c],
method_id: None,
effects: EffectMask::PURE,
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Return {
value: Some(joined),
});
let mut m2 = MirModule::new("str_concat".into());
m2.add_function(f2);
let mut vm2 = VM::new();
let out2 = vm2.execute_module(&m2).expect("vm exec");
assert_eq!(out2.to_string_box().value, "abcd");
}

View File

@ -1,53 +1,184 @@
#[test]
fn vtable_string_indexof_replace_trim_upper_lower() {
use crate::backend::vm::VM;
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType};
use crate::mir::{
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
MirModule, MirType,
};
std::env::set_var("NYASH_ABI_VTABLE", "1");
// indexOf("b") in "abc" == 1
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
let s = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: s, value: ConstValue::String("abc".into()) });
let sb = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: sb, box_type: "StringBox".into(), args: vec![s] });
let b = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: b, value: ConstValue::String("b".into()) });
let s = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: s,
value: ConstValue::String("abc".into()),
});
let sb = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: sb,
box_type: "StringBox".into(),
args: vec![s],
});
let b = f.next_value_id();
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: b,
value: ConstValue::String("b".into()),
});
let idx = f.next_value_id();
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(idx), box_val: sb, method: "indexOf".into(), args: vec![b], method_id: None, effects: EffectMask::PURE });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(idx) });
let mut m = MirModule::new("str_indexof".into()); m.add_function(f);
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(idx),
box_val: sb,
method: "indexOf".into(),
args: vec![b],
method_id: None,
effects: EffectMask::PURE,
});
f.get_block_mut(bb)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(idx) });
let mut m = MirModule::new("str_indexof".into());
m.add_function(f);
let mut vm = VM::new();
let out = vm.execute_module(&m).expect("vm exec");
assert_eq!(out.to_string_box().value, "1");
// replace: "a-b" -> replace("-","+") == "a+b"
let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE };
let sig2 = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::String,
effects: EffectMask::PURE,
};
let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0));
let bb2 = f2.entry_block;
let s2 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: s2, value: ConstValue::String("a-b".into()) });
let sb2 = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: sb2, box_type: "StringBox".into(), args: vec![s2] });
let dash = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: dash, value: ConstValue::String("-".into()) });
let plus = f2.next_value_id(); f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Const { dst: plus, value: ConstValue::String("+".into()) });
let s2 = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: s2,
value: ConstValue::String("a-b".into()),
});
let sb2 = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: sb2,
box_type: "StringBox".into(),
args: vec![s2],
});
let dash = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: dash,
value: ConstValue::String("-".into()),
});
let plus = f2.next_value_id();
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: plus,
value: ConstValue::String("+".into()),
});
let rep = f2.next_value_id();
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(rep), box_val: sb2, method: "replace".into(), args: vec![dash, plus], method_id: None, effects: EffectMask::PURE });
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: Some(rep) });
let mut m2 = MirModule::new("str_replace".into()); m2.add_function(f2);
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(rep),
box_val: sb2,
method: "replace".into(),
args: vec![dash, plus],
method_id: None,
effects: EffectMask::PURE,
});
f2.get_block_mut(bb2)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(rep) });
let mut m2 = MirModule::new("str_replace".into());
m2.add_function(f2);
let mut vm2 = VM::new();
let out2 = vm2.execute_module(&m2).expect("vm exec");
assert_eq!(out2.to_string_box().value, "a+b");
// trim + toUpper + toLower: " Xy " -> trim=="Xy" -> upper=="XY" -> lower=="xy"
let sig3 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::String, effects: EffectMask::PURE };
let sig3 = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::String,
effects: EffectMask::PURE,
};
let mut f3 = MirFunction::new(sig3, BasicBlockId::new(0));
let bb3 = f3.entry_block;
let s3 = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Const { dst: s3, value: ConstValue::String(" Xy ".into()) });
let sb3 = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::NewBox { dst: sb3, box_type: "StringBox".into(), args: vec![s3] });
let t = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(t), box_val: sb3, method: "trim".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
let u = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(u), box_val: t, method: "toUpper".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
let l = f3.next_value_id(); f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(l), box_val: u, method: "toLower".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
f3.get_block_mut(bb3).unwrap().add_instruction(MirInstruction::Return { value: Some(l) });
let mut m3 = MirModule::new("str_trim_upper_lower".into()); m3.add_function(f3);
let s3 = f3.next_value_id();
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Const {
dst: s3,
value: ConstValue::String(" Xy ".into()),
});
let sb3 = f3.next_value_id();
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::NewBox {
dst: sb3,
box_type: "StringBox".into(),
args: vec![s3],
});
let t = f3.next_value_id();
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(t),
box_val: sb3,
method: "trim".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
let u = f3.next_value_id();
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(u),
box_val: t,
method: "toUpper".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
let l = f3.next_value_id();
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::BoxCall {
dst: Some(l),
box_val: u,
method: "toLower".into(),
args: vec![],
method_id: None,
effects: EffectMask::PURE,
});
f3.get_block_mut(bb3)
.unwrap()
.add_instruction(MirInstruction::Return { value: Some(l) });
let mut m3 = MirModule::new("str_trim_upper_lower".into());
m3.add_function(f3);
let mut vm3 = VM::new();
let out3 = vm3.execute_module(&m3).expect("vm exec");
assert_eq!(out3.to_string_box().value, "xy");
}