chore(fmt): add legacy stubs and strip trailing whitespace to unblock cargo fmt
This commit is contained in:
@ -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");
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
// JIT(host-bridge on)
|
||||
std::env::set_var("NYASH_JIT_HOST_BRIDGE", "1");
|
||||
let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_person").expect("JIT exec");
|
||||
let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_person")
|
||||
.expect("JIT exec");
|
||||
let jit_s = jit_out.to_string_box().value;
|
||||
|
||||
assert_eq!(vm_s, jit_s);
|
||||
|
||||
@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)));
|
||||
|
||||
@ -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
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -27,4 +27,3 @@ fn tokenizer_has_basic_sugar_tokens() {
|
||||
assert!(has(|k| matches!(k, TokenType::DivAssign)));
|
||||
assert!(has(|k| matches!(k, TokenType::RANGE)));
|
||||
}
|
||||
|
||||
|
||||
@ -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),
|
||||
}
|
||||
|
||||
@ -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),
|
||||
}
|
||||
|
||||
@ -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),
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user