use std::rc::Rc;
use crate::{yamrb::{helpers::{mrb_define_cmethod, mrb_funcall}, value::*, vm::VM}, Error};
pub(crate) fn initialize_object(vm: &mut VM) {
let object_class = vm.object_class.clone();
let klass: RObject = object_class.clone().into();
vm.consts.insert("Object".to_string(), klass.to_refcount_assigned());
vm.builtin_class_table.insert("Object", object_class.clone());
#[cfg(feature = "wasi")]
{
mrb_define_cmethod(vm, object_class.clone(), "puts", Box::new(mrb_kernel_puts));
mrb_define_cmethod(vm, object_class.clone(), "p", Box::new(mrb_kernel_p));
mrb_define_cmethod(vm, object_class.clone(), "debug", Box::new(mrb_kernel_debug));
}
mrb_define_cmethod(vm, object_class.clone(), "initialize", Box::new(mrb_object_initialize));
mrb_define_cmethod(vm, object_class.clone(), "==", Box::new(mrb_object_double_eq));
mrb_define_cmethod(vm, object_class.clone(), "===", Box::new(mrb_object_triple_eq));
mrb_define_cmethod(vm, object_class.clone(), "object_id", Box::new(mrb_object_object_id));
mrb_define_cmethod(vm, object_class.clone(), "__id__", Box::new(mrb_object_object_id));
mrb_define_cmethod(vm, object_class.clone(), "to_s", Box::new(mrb_object_to_s));
mrb_define_cmethod(vm, object_class.clone(), "inspect", Box::new(mrb_object_to_s));
mrb_define_cmethod(vm, object_class.clone(), "raise", Box::new(mrb_object_raise));
vm.consts.insert("RUBY_VERSION".to_string(), Rc::new(RObject::string(crate::yamrb::vm::VERSION.to_string())));
vm.consts.insert("MRUBY_VERSION".to_string(), Rc::new(RObject::string(crate::yamrb::vm::VERSION.to_string())));
vm.consts.insert("MRUBY_EDGE_VERSION".to_string(), Rc::new(RObject::string(crate::yamrb::vm::VERSION.to_string())));
vm.consts.insert("RUBY_ENGINE".to_string(), Rc::new(RObject::string(crate::yamrb::vm::ENGINE.to_string())));
}
#[cfg(feature = "wasi")]
pub fn mrb_kernel_puts(vm: &mut VM, args: &[Rc<RObject>]) -> Result<Rc<RObject>, Error> {
let msg = args[0].clone();
match &msg.value {
RValue::String(s) => {
println!("{}", String::from_utf8_lossy(&s.borrow()));
}
RValue::Integer(i) => {
println!("{}", i);
}
_ => {
let inspect = mrb_funcall(vm, Some(msg), "to_s", &[])?;
let inspect: String = inspect.as_ref().try_into()?;
println!("{}", inspect);
}
}
Ok(Rc::new(RObject::nil()))
}
#[cfg(feature = "wasi")]
pub fn mrb_kernel_p(vm: &mut VM, args: &[Rc<RObject>]) -> Result<Rc<RObject>, Error> {
let msg = args[0].clone();
let inspect = mrb_funcall(vm, Some(msg), "inspect", &[])?;
let inspect: String = inspect.as_ref().try_into()?;
println!("{}", inspect);
Ok(Rc::new(RObject::nil()))
}
#[cfg(feature = "wasi")]
pub fn mrb_kernel_debug(_vm: &mut VM, args: &[Rc<RObject>]) -> Result<Rc<RObject>, Error> {
for (i, obj) in args.iter().enumerate() {
dbg!(i, obj.clone());
}
Ok(Rc::new(RObject::nil()))
}
pub fn mrb_object_is_equal(_vm: &mut VM, lhs: Rc<RObject>, rhs: Rc<RObject>) -> Rc<RObject> {
RObject::boolean(lhs.as_eq_value() == rhs.as_eq_value()).to_refcount_assigned()
}
pub fn mrb_object_is_not_equal(_vm: &mut VM, lhs: Rc<RObject>, rhs: Rc<RObject>) -> Rc<RObject> {
RObject::boolean(lhs.as_eq_value() != rhs.as_eq_value()).to_refcount_assigned()
}
pub fn mrb_object_double_eq(vm: &mut VM, args: &[Rc<RObject>]) -> Result<Rc<RObject>, Error> {
let lhs = vm.getself();
let rhs = args[0].clone();
Ok(mrb_object_is_equal(vm, lhs, rhs))
}
pub fn mrb_object_triple_eq(vm: &mut VM, args: &[Rc<RObject>]) -> Result<Rc<RObject>, Error> {
let lhs = vm.getself();
let rhs = args[0].clone();
match (&lhs.value, &rhs.value) {
(RValue::Integer(i1), RValue::Integer(i2)) => {
Ok(Rc::new(RObject::boolean(*i1 == *i2)))
}
(RValue::Float(f1), RValue::Float(f2)) => {
Ok(Rc::new(RObject::boolean(*f1 == *f2)))
}
(RValue::Symbol(sym1), RValue::Symbol(sym2)) => {
Ok(Rc::new(RObject::boolean(sym1 == sym2)))
}
(RValue::String(s1), RValue::String(s2)) => {
Ok(Rc::new(RObject::boolean(s1 == s2)))
}
(RValue::Class(c1), _) => {
match &lhs.value {
RValue::Class(c2) => {
Ok(Rc::new(RObject::boolean(c1.sym_id == c2.sym_id)))
}
_ => {
let c2 = lhs.get_class(vm);
Ok(Rc::new(RObject::boolean(c1.sym_id == c2.sym_id)))
}
}
}
(RValue::Range(_s, _e, _v), _) => {
let arg = vec![rhs];
mrb_funcall(vm, Some(lhs), "include?", &arg)
}
_ => {
Ok(Rc::new(RObject::boolean(false)))
}
}
}
pub fn mrb_object_object_id(vm: &mut VM, _args: &[Rc<RObject>]) -> Result<Rc<RObject>, Error> {
let x = vm.getself().as_ref().object_id.get();
let to_i64 = ((x as i64) ^ (1 << 63)) & (1 << 63) | (x & (u64::MAX >> 1)) as i64;
Ok(Rc::new(RObject::integer(to_i64)))
}
pub fn mrb_object_to_s(vm: &mut VM, _args: &[Rc<RObject>]) -> Result<Rc<RObject>, Error> {
let obj = vm.getself();
Ok(Rc::new(RObject::string(format!("{:?}", obj))))
}
pub fn mrb_object_raise(_vm: &mut VM, args: &[Rc<RObject>]) -> Result<Rc<RObject>, Error> {
let msg = args[0].as_ref().try_into()?;
let err = Error::RuntimeError(msg);
Err(err)
}
pub fn mrb_object_initialize(_vm: &mut VM, _args: &[Rc<RObject>]) -> Result<Rc<RObject>, Error> {
Ok(Rc::new(RObject::nil()))
}
#[test]
fn test_mrb_object_is_equal() {
let mut vm = VM::empty();
let lhs = RObject::integer(1).to_refcount_assigned();
let rhs = RObject::integer(1).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let lhs = RObject::integer(1).to_refcount_assigned();
let rhs = RObject::integer(3).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
let lhs = RObject::string("mruby/edge is Ruby".into()).to_refcount_assigned();
let rhs = RObject::string("mruby/edge is Ruby".into()).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let lhs = RObject::string("mruby/edge is Ruby".into()).to_refcount_assigned();
let rhs = RObject::string("mruby/edge is not Ruby".into()).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
let lhs = RObject::symbol("some".into()).to_refcount_assigned();
let rhs = RObject::symbol("some".into()).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let lhs = RObject::symbol("some".into()).to_refcount_assigned();
let rhs = RObject::symbol("other".into()).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
let lhs = RObject::boolean(true).to_refcount_assigned();
let rhs = RObject::boolean(true).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let lhs = RObject::boolean(false).to_refcount_assigned();
let rhs = RObject::boolean(false).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let lhs = RObject::boolean(true).to_refcount_assigned();
let rhs = RObject::boolean(false).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
let lhs = RObject::float(0.1).to_refcount_assigned();
let rhs = RObject::float(0.1).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let lhs = RObject::float(0.2).to_refcount_assigned();
let rhs = RObject::float(0.1).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
let lhs = RObject::nil().to_refcount_assigned();
let rhs = RObject::nil().to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let lhs = RObject::integer(100).to_refcount_assigned();
let rhs = RObject::nil().to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
let lhs = RObject::integer(100).to_refcount_assigned();
let rhs = RObject::nil().to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
}
#[test]
fn test_mrb_object_is_equal_range() {
let mut vm = VM::empty();
let s = RObject::integer(1).to_refcount_assigned();
let e = RObject::integer(10).to_refcount_assigned();
let lhs = RObject::range(s.clone(), e.clone(), true).to_refcount_assigned();
let rhs = RObject::range(s.clone(), e.clone(), true).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let s = RObject::integer(1).to_refcount_assigned();
let e = RObject::integer(10).to_refcount_assigned();
let lhs = RObject::range(s.clone(), e.clone(), true).to_refcount_assigned();
let rhs = RObject::range(s.clone(), e.clone(), false).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
let s = RObject::integer(1).to_refcount_assigned();
let e = RObject::integer(10).to_refcount_assigned();
let e2 = RObject::integer(11).to_refcount_assigned();
let lhs = RObject::range(s.clone(), e.clone(), true).to_refcount_assigned();
let rhs = RObject::range(s.clone(), e2.clone(), true).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
let s = RObject::string("a".into()).to_refcount_assigned();
let e = RObject::string("z".into()).to_refcount_assigned();
let lhs = RObject::range(s.clone(), e.clone(), true).to_refcount_assigned();
let rhs = RObject::range(s.clone(), e.clone(), true).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let s = RObject::string("a".into()).to_refcount_assigned();
let e = RObject::string("z".into()).to_refcount_assigned();
let e2 = RObject::string("A".into()).to_refcount_assigned();
let lhs = RObject::range(s.clone(), e.clone(), true).to_refcount_assigned();
let rhs = RObject::range(s.clone(), e2.clone(), true).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
}
#[test]
fn test_mrb_object_is_equal_array() {
let mut vm = VM::empty();
let lhs = RObject::array(vec![]).to_refcount_assigned();
let rhs = RObject::array(vec![]).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let lhs = vec![
RObject::integer(1).to_refcount_assigned(),
RObject::integer(2).to_refcount_assigned(),
RObject::integer(3).to_refcount_assigned(),
];
let rhs = vec![
RObject::integer(1).to_refcount_assigned(),
RObject::integer(2).to_refcount_assigned(),
RObject::integer(3).to_refcount_assigned(),
];
let lhs = RObject::array(lhs).to_refcount_assigned();
let rhs = RObject::array(rhs).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let lhs = vec![
RObject::integer(1).to_refcount_assigned(),
RObject::integer(2).to_refcount_assigned(),
RObject::integer(3).to_refcount_assigned(),
];
let rhs = vec![
RObject::integer(1).to_refcount_assigned(),
RObject::integer(2).to_refcount_assigned(),
RObject::integer(4).to_refcount_assigned(),
];
let lhs = RObject::array(lhs).to_refcount_assigned();
let rhs = RObject::array(rhs).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
}
#[test]
fn test_mrb_object_is_equal_hash() {
use std::collections::HashMap;
use crate::yamrb::prelude::hash::*;
let mut vm = VM::empty();
let lhs = RObject::hash(HashMap::new()).to_refcount_assigned();
let rhs = RObject::hash(HashMap::new()).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let lhs = RObject::hash(HashMap::new()).to_refcount_assigned();
mrb_hash_set_index(lhs.clone(), RObject::symbol("key1".into()).to_refcount_assigned(), RObject::integer(1).to_refcount_assigned()).expect("set index failed");
mrb_hash_set_index(lhs.clone(), RObject::symbol("key2".into()).to_refcount_assigned(), RObject::integer(2).to_refcount_assigned()).expect("set index failed");
let rhs = RObject::hash(HashMap::new()).to_refcount_assigned();
mrb_hash_set_index(rhs.clone(), RObject::symbol("key2".into()).to_refcount_assigned(), RObject::integer(2).to_refcount_assigned()).expect("set index failed");
mrb_hash_set_index(rhs.clone(), RObject::symbol("key1".into()).to_refcount_assigned(), RObject::integer(1).to_refcount_assigned()).expect("set index failed");
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let lhs = RObject::hash(HashMap::new()).to_refcount_assigned();
mrb_hash_set_index(lhs.clone(), RObject::symbol("key1".into()).to_refcount_assigned(), RObject::integer(1).to_refcount_assigned()).expect("set index failed");
mrb_hash_set_index(lhs.clone(), RObject::symbol("key2".into()).to_refcount_assigned(), RObject::integer(2).to_refcount_assigned()).expect("set index failed");
let rhs = RObject::hash(HashMap::new()).to_refcount_assigned();
mrb_hash_set_index(rhs.clone(), RObject::symbol("key2".into()).to_refcount_assigned(), RObject::integer(2).to_refcount_assigned()).expect("set index failed");
mrb_hash_set_index(rhs.clone(), RObject::symbol("key1".into()).to_refcount_assigned(), RObject::integer(3).to_refcount_assigned()).expect("set index failed");
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
let lhs = RObject::hash(HashMap::new()).to_refcount_assigned();
mrb_hash_set_index(lhs.clone(), RObject::symbol("key1".into()).to_refcount_assigned(), RObject::integer(1).to_refcount_assigned()).expect("set index failed");
mrb_hash_set_index(lhs.clone(), RObject::symbol("key2".into()).to_refcount_assigned(), RObject::integer(2).to_refcount_assigned()).expect("set index failed");
let rhs = RObject::hash(HashMap::new()).to_refcount_assigned();
mrb_hash_set_index(rhs.clone(), RObject::symbol("key2".into()).to_refcount_assigned(), RObject::integer(2).to_refcount_assigned()).expect("set index failed");
mrb_hash_set_index(rhs.clone(), RObject::symbol("key1-b".into()).to_refcount_assigned(), RObject::integer(1).to_refcount_assigned()).expect("set index failed");
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
}
#[test]
fn test_mrb_object_is_equal_klass() {
let mut vm = VM::empty();
let lhs: RObject = vm.get_class_by_name("String").into();
let rhs: RObject = RObject::string("String".into()).get_class(&mut vm).into();
let lhs = lhs.to_refcount_assigned();
let rhs = rhs.to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let lhs: RObject = RObject::integer(5471).get_class(&mut vm).into();
let rhs: RObject = RObject::string("String".into()).get_class(&mut vm).into();
let lhs = lhs.to_refcount_assigned();
let rhs = rhs.to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
}
#[test]
fn test_mrb_object_is_equal_instance() {
let mut vm = VM::empty();
let lhs = RObject::instance(vm.object_class.clone()).to_refcount_assigned();
let rhs = lhs.clone();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(ret);
let lhs = RObject::instance(vm.object_class.clone()).to_refcount_assigned();
let rhs = RObject::instance(vm.object_class.clone()).to_refcount_assigned();
let ret: bool = mrb_object_is_equal(&mut vm, lhs, rhs).as_ref().try_into().expect("must return bool");
assert!(!ret);
}