2025-09-06 13:07:57 +09:00
|
|
|
|
#[cfg(all(test, not(feature = "jit-direct-only")))]
|
2025-09-03 09:12:39 +09:00
|
|
|
|
mod tests {
|
|
|
|
|
|
use std::collections::HashMap;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
use std::sync::{Arc, RwLock};
|
2025-09-03 09:12:39 +09:00
|
|
|
|
|
|
|
|
|
|
use crate::backend::VM;
|
2025-09-24 09:30:42 +09:00
|
|
|
|
use crate::box_factory::RuntimeError;
|
2025-11-21 06:25:17 +09:00
|
|
|
|
use crate::box_trait::NyashBox;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
use crate::mir::{
|
|
|
|
|
|
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
|
|
|
|
|
|
MirModule, MirType,
|
|
|
|
|
|
};
|
2025-09-03 09:12:39 +09:00
|
|
|
|
|
|
|
|
|
|
// Minimal Person factory: creates InstanceBox with fields [name, age]
|
|
|
|
|
|
struct PersonFactory;
|
|
|
|
|
|
impl crate::box_factory::BoxFactory for PersonFactory {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
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),
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-09-03 09:12:39 +09:00
|
|
|
|
let fields = vec!["name".to_string(), "age".to_string()];
|
|
|
|
|
|
let methods: HashMap<String, crate::ast::ASTNode> = HashMap::new();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
let inst = crate::instance_v2::InstanceBox::from_declaration(
|
|
|
|
|
|
"Person".to_string(),
|
|
|
|
|
|
fields,
|
|
|
|
|
|
methods,
|
|
|
|
|
|
);
|
2025-09-03 09:12:39 +09:00
|
|
|
|
Ok(Box::new(inst))
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
fn box_types(&self) -> Vec<&str> {
|
|
|
|
|
|
vec!["Person"]
|
|
|
|
|
|
}
|
|
|
|
|
|
fn is_builtin_factory(&self) -> bool {
|
|
|
|
|
|
true
|
|
|
|
|
|
}
|
2025-09-03 09:12:39 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn build_person_module() -> MirModule {
|
|
|
|
|
|
let mut module = MirModule::new("identical_person".to_string());
|
2025-09-17 07:43:07 +09:00
|
|
|
|
let sig = FunctionSignature {
|
|
|
|
|
|
name: "main".into(),
|
|
|
|
|
|
params: vec![],
|
|
|
|
|
|
return_type: MirType::String,
|
|
|
|
|
|
effects: EffectMask::PURE,
|
|
|
|
|
|
};
|
2025-09-03 09:12:39 +09:00
|
|
|
|
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
|
|
|
|
|
|
let bb = f.entry_block;
|
|
|
|
|
|
|
|
|
|
|
|
let person = f.next_value_id();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
f.get_block_mut(bb)
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.add_instruction(MirInstruction::NewBox {
|
|
|
|
|
|
dst: person,
|
|
|
|
|
|
box_type: "Person".into(),
|
|
|
|
|
|
args: vec![],
|
|
|
|
|
|
});
|
2025-09-03 09:12:39 +09:00
|
|
|
|
|
|
|
|
|
|
// person.setField("name", "Alice")
|
|
|
|
|
|
let k_name = f.next_value_id();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
f.get_block_mut(bb)
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.add_instruction(MirInstruction::Const {
|
|
|
|
|
|
dst: k_name,
|
|
|
|
|
|
value: ConstValue::String("name".into()),
|
|
|
|
|
|
});
|
2025-09-03 09:12:39 +09:00
|
|
|
|
let v_alice = f.next_value_id();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
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,
|
|
|
|
|
|
});
|
2025-09-03 09:12:39 +09:00
|
|
|
|
|
|
|
|
|
|
// person.setField("age", 25)
|
|
|
|
|
|
let k_age = f.next_value_id();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
f.get_block_mut(bb)
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.add_instruction(MirInstruction::Const {
|
|
|
|
|
|
dst: k_age,
|
|
|
|
|
|
value: ConstValue::String("age".into()),
|
|
|
|
|
|
});
|
2025-09-03 09:12:39 +09:00
|
|
|
|
let v_25 = f.next_value_id();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
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,
|
|
|
|
|
|
});
|
2025-09-03 09:12:39 +09:00
|
|
|
|
|
|
|
|
|
|
// name = person.getField("name"); return name
|
|
|
|
|
|
let k_name2 = f.next_value_id();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
f.get_block_mut(bb)
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.add_instruction(MirInstruction::Const {
|
|
|
|
|
|
dst: k_name2,
|
|
|
|
|
|
value: ConstValue::String("name".into()),
|
|
|
|
|
|
});
|
2025-09-03 09:12:39 +09:00
|
|
|
|
let out_name = f.next_value_id();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
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),
|
|
|
|
|
|
});
|
2025-09-03 09:12:39 +09:00
|
|
|
|
|
|
|
|
|
|
module.add_function(f);
|
|
|
|
|
|
module
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "cranelift-jit")]
|
|
|
|
|
|
#[test]
|
2025-09-06 21:47:14 +09:00
|
|
|
|
#[ignore = "ABI_STRICT vtable path diverges; JIT host-bridge parity pending"]
|
2025-09-03 09:12:39 +09:00
|
|
|
|
fn identical_vm_and_jit_person_get_set_slots() {
|
|
|
|
|
|
// Build runtime with Person factory
|
|
|
|
|
|
let mut rt_builder = crate::runtime::NyashRuntimeBuilder::new();
|
|
|
|
|
|
rt_builder = rt_builder.with_factory(Arc::new(PersonFactory));
|
|
|
|
|
|
let runtime = rt_builder.build();
|
|
|
|
|
|
|
2025-09-03 09:55:25 +09:00
|
|
|
|
// Also register factory globally for JIT path (host-bridge creates via global registry)
|
|
|
|
|
|
crate::runtime::register_user_defined_factory(Arc::new(PersonFactory));
|
|
|
|
|
|
|
2025-09-03 09:12:39 +09:00
|
|
|
|
// Build module
|
|
|
|
|
|
let module = build_person_module();
|
|
|
|
|
|
|
|
|
|
|
|
// VM (VTABLE on)
|
|
|
|
|
|
std::env::set_var("NYASH_ABI_VTABLE", "1");
|
2025-11-17 11:28:18 +09:00
|
|
|
|
let mut vm = VM::new();
|
2025-09-03 09:12:39 +09:00
|
|
|
|
let vm_out = vm.execute_module(&module).expect("VM exec");
|
|
|
|
|
|
let vm_s = vm_out.to_string_box().value;
|
|
|
|
|
|
|
|
|
|
|
|
// JIT(host-bridge on)
|
|
|
|
|
|
std::env::set_var("NYASH_JIT_HOST_BRIDGE", "1");
|
2025-09-17 07:43:07 +09:00
|
|
|
|
let jit_out = crate::backend::cranelift_compile_and_execute(&module, "identical_person")
|
|
|
|
|
|
.expect("JIT exec");
|
2025-09-03 09:12:39 +09:00
|
|
|
|
let jit_s = jit_out.to_string_box().value;
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(vm_s, jit_s);
|
|
|
|
|
|
assert_eq!(vm_s, "Alice");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|