use crate::{obj::{reserved::*, prelude::*}, vm::{consts::ConstPool, inst::Inst, signal::*, Vm}}; use once_cell::sync::Lazy; use shredder::{GcSafeWrapper, Scan}; use std::{fmt::{Debug, Formatter, self}, io::{self, Write}}; pub type FunLocals = Vec; // // struct UserFun // pub type UserFunRef = ObjRef; #[derive(Scan)] pub struct UserFun { vtable: Vtable, attrs: Attrs, // Safe because Vec doesn't need to be scanned #[shredder(unsafe_skip)] code: Vec, // Safe because this is just an interner that points to symbols, which aren't GC'd #[shredder(unsafe_skip)] locals: FunLocals, } impl UserFun { pub fn new_obj(code: Vec, locals: FunLocals) -> UserFunRef { let obj_ref = ObjRef::new(UserFun { vtable: Default::default(), // this is a placeholder for the real vtable attrs: Default::default(), code, locals, }); // XXX replace the vtable before returning, since __call__ member points to the object // itself { write_obj!(let obj = obj_ref); obj.vtable = vtable! { TY_MEMBER_NAME.sym => USER_FUN_TY.clone(), CALL_MEMBER_NAME.sym => obj_ref.clone(), }; } obj_ref } pub fn code(&self) -> &Vec { &self.code } pub fn locals(&self) -> &FunLocals { &self.locals } pub fn dump(&self, writer: &mut dyn Write, const_pool: &ConstPool, globals: &FunLocals) -> io::Result<()> { fn num_digits(n: usize, radix: usize) -> usize { ((n as f64) + 1.0).log(radix as f64).ceil() as usize } // column widths let addr_w = num_digits(self.code().len(), 16).max(4); let inst_col = 16 - addr_w; let inst_w = 16; for (addr, inst) in self.code().iter().enumerate() { let (param_val, mut param_name) = match inst { Inst::PushSym(sym) | Inst::GetAttr(sym) | Inst::SetAttr(sym) => ( sym.index().to_string(), global_sym_lookup(*sym).unwrap().to_string(), ), Inst::PushConst(hdl) => ( hdl.index().to_string(), { let obj_ref = const_pool.get(*hdl); read_obj!(let obj = obj_ref); format!("{:?}", obj) }, ), Inst::LoadLocal(local) => ( local.index().to_string(), global_sym_lookup(self.locals()[local.index()]).unwrap().to_string(), ), Inst::PopLocal(local) => { if let Some(local) = local { let index = local.index(); let sym = self.locals()[index]; let name = global_sym_lookup(sym).unwrap().to_string(); (index.to_string(), name) } else { ("(discarded)".to_string(), String::new()) } } Inst::PopGlobal(global) => { if let Some(global) = global{ let index = global.index(); let sym = globals[index]; let name = global_sym_lookup(sym).unwrap().to_string(); (index.to_string(), name) } else { ("(discarded)".to_string(), String::new()) } } Inst::Jump(addr) | Inst::JumpTrue(addr) => ( addr.to_string(), String::new(), ), Inst::Call(argc) => ( argc.to_string(), String::new(), ), _ => (String::new(), String::new()), }; if !param_name.is_empty() { param_name = format!("({})", param_name); } writeln!( writer, "{:0addr_w$x}{space: >inst_col$}{: fmt::Result { fmt.debug_struct("UserFun") .field("attrs", &self.attrs) .field("code", &self.code) .field("locals", &self.locals) .finish() } } pub static USER_FUN_TY: Lazy> = Lazy::new(|| Ty::new_obj(USER_FUN_NAME.sym_ref())); // // struct NativeFun // pub type NativeFunPtr = Box) -> Signal) + Send + Sync>; pub type NativeFunRef = ObjRef; #[derive(Scan)] pub struct NativeFun { vtable: Vtable, attrs: Attrs, #[shredder(skip)] fun: GcSafeWrapper, } // // impl NativeFun // impl NativeFun { pub fn new_obj(fun: NativeFunPtr) -> ObjRef { let obj_ref = ObjRef::new(Self { vtable: Default::default(), attrs: Default::default(), fun: GcSafeWrapper::new(fun), }); // XXX replace the vtable before returning, since __call__ member points to the object // itself { write_obj!(let obj = obj_ref); obj.vtable = vtable! { TY_MEMBER_NAME.sym => NATIVE_FUN_TY.clone(), CALL_MEMBER_NAME.sym => obj_ref.clone(), }; } obj_ref } /// Gets the native function that can be invoked. pub fn fun(&self) -> &NativeFunPtr { &self.fun } } // // impl Debug for NativeFun // impl Debug for NativeFun { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { fmt.debug_struct("NativeFun") .field("fun", &format!("(function at {:x})", &self.fun as *const _ as usize)) .field("attrs", &self.attrs) .finish() } } impl_obj!(NativeFun); pub static NATIVE_FUN_TY: Lazy> = Lazy::new(|| Ty::new_obj(NATIVE_FUN_NAME.sym_ref())); // // Native function defs // // __access__ is what the "dot" operator calls // __get_attr__ *should* always bypass the __access__ function and get an attribute directly pub static GET_ATTR_MEMBER_FUN: Lazy> = Lazy::new(|| { NativeFun::new_obj(Box::new(|_caller, _vm, _args| { /* let sym_ref = vm.pop(); let obj_ref = vm.pop(); obj_ref.access() */ todo!("__get_attr__ function") })) });