use std::fmt::{self, Debug, Display}; use std::ptr; use std::rc::Rc; use gc::{Finalize, Trace}; use crate::obj::macros::*; use crate::obj::{make_ptr, BaseObjInst, Obj, ObjP}; use crate::vm::{Argc, Chunk, Frame, Vm}; //////////////////////////////////////////////////////////////////////////////// // BuiltinFunctionInst //////////////////////////////////////////////////////////////////////////////// pub type BuiltinFunctionPtr = fn(vm: &mut Vm, args: Vec) -> ObjP; #[derive(Debug, Trace)] pub struct BuiltinFunctionInst { base: BaseObjInst, name: String, #[unsafe_ignore_trace] function: BuiltinFunctionPtr, arity: Argc, } impl BuiltinFunctionInst { pub fn new(name: impl ToString, function: BuiltinFunctionPtr, arity: Argc) -> Self { Self { base: Default::default(), name: name.to_string(), function, arity, } } impl_create!( name: impl ToString, function: BuiltinFunctionPtr, arity: Argc, ); pub fn name(&self) -> &String { &self.name } } impl Finalize for BuiltinFunctionInst { fn finalize(&self) {} } impl Display for BuiltinFunctionInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!( fmt, "", self.name(), self.arity().unwrap(), self.function as *const BuiltinFunctionPtr as usize ) } } impl Obj for BuiltinFunctionInst { fn arity(&self) -> Option { Some(self.arity) } fn call(&self, vm: &mut Vm, argc: Argc) { // args let mut args = Vec::with_capacity(argc as usize); for _ in 0..argc { args.push(vm.pop()); } args.reverse(); // callee (self) vm.pop(); let result = (self.function)(vm, args); vm.push(result); } fn equals(&self, other: &dyn Obj) -> bool { // TODO BuiltinFunctionInst::equals : need something more robust than checking addr_eq, // maybe check the self_binding pointer too? if let Some(other) = other.as_any().downcast_ref::() { ptr::addr_eq(self, other) } else { false } } impl_base_obj!(); } //////////////////////////////////////////////////////////////////////////////// // UserFunctionInst //////////////////////////////////////////////////////////////////////////////// #[derive(Debug, Clone, Trace)] pub struct UserFunctionInst { base: BaseObjInst, #[unsafe_ignore_trace] name: Rc, #[unsafe_ignore_trace] chunk: Rc, arity: Argc, captures: Vec, } impl UserFunctionInst { pub fn new(chunk: Chunk, arity: Argc) -> Self { Self { base: Default::default(), name: Rc::new("(anonymous)".to_string()), chunk: Rc::new(chunk), arity, captures: Default::default(), } } impl_create!(chunk: Chunk, arity: Argc); pub fn name(&self) -> &String { &self.name } pub fn set_name(&mut self, name: Rc) { self.name = name; } pub fn chunk(&self) -> &Chunk { &self.chunk } pub fn push_capture(&mut self, value: ObjP) { self.captures.push(value); } } impl Finalize for UserFunctionInst { fn finalize(&self) {} } impl Display for UserFunctionInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!( fmt, "", self.name(), self.arity().unwrap(), self as *const _ as usize ) } } impl Obj for UserFunctionInst { fn arity(&self) -> Option { Some(self.arity) } fn call(&self, vm: &mut Vm, argc: Argc) { assert_eq!(argc, self.arity, "argc must match arity"); let new_frame = Frame { name: Rc::clone(&self.name), chunk: Rc::clone(&self.chunk), ip: 0, stack_base: vm.stack().len() - (argc as usize), }; vm.push_frame(new_frame); for capture in &self.captures { vm.push(capture.clone()); } } fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { // TODO UserFunctionInst::equals : need something more robust than checking addr_eq. ptr::addr_eq(self, other) } else { false } } impl_base_obj!(); } //////////////////////////////////////////////////////////////////////////////// // MethodInst //////////////////////////////////////////////////////////////////////////////// #[derive(Debug, Trace)] pub struct MethodInst { base: BaseObjInst, self_binding: ObjP, function: ObjP, } impl MethodInst { pub fn new(self_binding: ObjP, function: ObjP) -> Self { Self { base: Default::default(), self_binding, function, } } pub fn create(ty: ObjP, self_binding: ObjP, function: ObjP) -> ObjP { let ptr = make_ptr(Self::new(self_binding, function)); ptr.borrow_mut().instantiate(ty.clone()); ptr } pub fn self_binding(&self) -> &ObjP { &self.self_binding } } impl Finalize for MethodInst { fn finalize(&self) {} } impl Display for MethodInst { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.function.borrow()) } } impl Obj for MethodInst { fn arity(&self) -> Option { // Subtract one from the arity - this is because the VM uses arity() to check against the // number of arguments passed. self.function.borrow().arity().map(|arity| arity - 1) } fn call(&self, vm: &mut Vm, mut argc: Argc) { let self_pos = vm.stack().len() - (argc as usize); vm.stack_mut().insert(self_pos, self.self_binding.clone()); argc += 1; self.function.borrow().call(vm, argc) } fn equals(&self, other: &dyn Obj) -> bool { if let Some(other) = other.as_any().downcast_ref::() { ptr::addr_eq(&*self.self_binding, &*other.self_binding) && ptr::addr_eq(&*self.function, &*other.function) } else { false } } impl_base_obj!(); }