Files
not-python/src/obj/fun.rs

231 lines
6.7 KiB
Rust
Raw Normal View History

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<Sym>;
//
// struct UserFun
//
pub type UserFunRef = ObjRef<UserFun>;
#[derive(Scan)]
pub struct UserFun {
vtable: Vtable,
attrs: Attrs,
// Safe because Vec<Inst> doesn't need to be scanned
#[shredder(unsafe_skip)]
code: Vec<Inst>,
// 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<Inst>, 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<Inst> {
&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$}{: <inst_w$}{: <4} {}",
addr,
inst.name(),
param_val,
param_name,
space = "",
addr_w = addr_w,
inst_w = inst_w,
inst_col = inst_col,
)?;
}
Ok(())
}
}
impl_obj!(UserFun);
impl Debug for UserFun {
fn fmt(&self, fmt: &mut Formatter) -> 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<ObjRef<Ty>> = Lazy::new(|| Ty::new_obj(USER_FUN_NAME.sym_ref()));
//
// struct NativeFun
//
pub type NativeFunPtr = Box<dyn (Fn(ObjRef, &mut Vm, Vec<ObjRef>) -> Signal) + Send + Sync>;
pub type NativeFunRef = ObjRef<NativeFun>;
#[derive(Scan)]
pub struct NativeFun {
vtable: Vtable,
attrs: Attrs,
#[shredder(skip)]
fun: GcSafeWrapper<NativeFunPtr>,
}
//
// impl NativeFun
//
impl NativeFun {
pub fn new_obj(fun: NativeFunPtr) -> ObjRef<Self> {
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<ObjRef<Ty>> = 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<ObjRef<NativeFun>> = 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")
}))
});