feat: add managed types, source classes and scoped native calls
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
use std::{fmt, sync::Arc};
|
||||
|
||||
use crate::{ObjectPayload, ObjectRef, ObjectStore, Value, VmError, WeakObjectRef};
|
||||
|
||||
/// Borrowed results produced inside a native call. Invoke delegates to another
|
||||
/// callable through the VM's existing frame loop, without recursively entering it.
|
||||
#[derive(Debug)]
|
||||
pub enum NativeResult {
|
||||
Return(Value),
|
||||
Invoke {
|
||||
function: ObjectRef,
|
||||
arguments: Vec<Value>,
|
||||
},
|
||||
}
|
||||
|
||||
pub trait NativeCallable: fmt::Debug + Send + Sync + 'static {
|
||||
fn invoke(
|
||||
&self,
|
||||
context: &mut NativeContext<'_>,
|
||||
arguments: &[Value],
|
||||
) -> Result<NativeResult, VmError>;
|
||||
}
|
||||
|
||||
/// Native allocation/mutation scope. Allocations and upgraded weak references
|
||||
/// are temporary owned roots. Return/Invoke promotes only its output roots;
|
||||
/// Drop releases everything else, including on error or unwinding.
|
||||
pub struct NativeContext<'a> {
|
||||
store: &'a mut ObjectStore,
|
||||
roots: Vec<ObjectRef>,
|
||||
callee: ObjectRef,
|
||||
}
|
||||
|
||||
impl<'a> NativeContext<'a> {
|
||||
/// Callable currently dispatched by the VM, borrowed for this scope.
|
||||
pub fn callee(&self) -> ObjectRef {
|
||||
self.callee
|
||||
}
|
||||
|
||||
pub fn attach_type(
|
||||
&mut self,
|
||||
object: ObjectRef,
|
||||
type_object: ObjectRef,
|
||||
) -> Result<(), VmError> {
|
||||
self.store.attach_type(object, type_object)
|
||||
}
|
||||
|
||||
pub fn store(&self) -> &ObjectStore {
|
||||
self.store
|
||||
}
|
||||
|
||||
pub fn allocate(&mut self, payload: impl ObjectPayload) -> Result<ObjectRef, VmError> {
|
||||
let object = self.store.allocate(payload)?;
|
||||
self.roots.push(object);
|
||||
Ok(object)
|
||||
}
|
||||
|
||||
pub fn new_object(&mut self) -> Result<ObjectRef, VmError> {
|
||||
self.allocate(crate::Record)
|
||||
}
|
||||
|
||||
pub fn new_dict(&mut self) -> Result<ObjectRef, VmError> {
|
||||
let object = self.store.new_dict()?;
|
||||
self.roots.push(object);
|
||||
Ok(object)
|
||||
}
|
||||
|
||||
pub fn new_cell(&mut self, value: Option<Value>) -> Result<ObjectRef, VmError> {
|
||||
let object = self.store.new_cell(value)?;
|
||||
self.roots.push(object);
|
||||
Ok(object)
|
||||
}
|
||||
|
||||
pub fn set_attr(
|
||||
&mut self,
|
||||
object: ObjectRef,
|
||||
name: impl Into<String>,
|
||||
value: Value,
|
||||
) -> Result<(), VmError> {
|
||||
self.store.set_attr(object, name, value)
|
||||
}
|
||||
|
||||
pub fn del_attr(&mut self, object: ObjectRef, name: &str) -> Result<(), VmError> {
|
||||
self.store.del_attr(object, name)
|
||||
}
|
||||
|
||||
pub fn dict_set(&mut self, object: ObjectRef, key: Value, value: Value) -> Result<(), VmError> {
|
||||
self.store.dict_set(object, key, value)
|
||||
}
|
||||
|
||||
pub fn dict_delete(&mut self, object: ObjectRef, key: &Value) -> Result<bool, VmError> {
|
||||
self.store.dict_delete(object, key)
|
||||
}
|
||||
|
||||
pub fn cell_set(&mut self, object: ObjectRef, value: Value) -> Result<(), VmError> {
|
||||
self.store.cell_set(object, value)
|
||||
}
|
||||
|
||||
pub fn replace_payload(
|
||||
&mut self,
|
||||
object: ObjectRef,
|
||||
payload: impl ObjectPayload,
|
||||
) -> Result<(), VmError> {
|
||||
self.store.replace_payload(object, payload)
|
||||
}
|
||||
|
||||
pub fn upgrade(&mut self, weak: WeakObjectRef) -> Result<Option<ObjectRef>, VmError> {
|
||||
let object = self.store.upgrade(weak)?;
|
||||
if let Some(object) = object {
|
||||
self.roots.push(object);
|
||||
}
|
||||
Ok(object)
|
||||
}
|
||||
|
||||
fn protect(&mut self, value: &Value) -> Result<(), VmError> {
|
||||
if let Value::Object(object) = value {
|
||||
self.store.retain(*object)?;
|
||||
self.roots.push(*object);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Internal execution bridge. Every returned object reference owns a root;
|
||||
/// consumers must release Return values or all Invoke arguments/function.
|
||||
pub fn execute(
|
||||
store: &'a mut ObjectStore,
|
||||
function: ObjectRef,
|
||||
arguments: &[Value],
|
||||
callable: Arc<dyn NativeCallable>,
|
||||
) -> Result<NativeResult, VmError> {
|
||||
let mut context = Self {
|
||||
store,
|
||||
roots: Vec::new(),
|
||||
callee: function,
|
||||
};
|
||||
context.protect(&Value::Object(function))?;
|
||||
for argument in arguments {
|
||||
context.protect(argument)?;
|
||||
}
|
||||
let result = callable.invoke(&mut context, arguments)?;
|
||||
let transferred_from = context.roots.len();
|
||||
match &result {
|
||||
NativeResult::Return(value) => context.protect(value)?,
|
||||
NativeResult::Invoke {
|
||||
function,
|
||||
arguments,
|
||||
} => {
|
||||
context.protect(&Value::Object(*function))?;
|
||||
for argument in arguments {
|
||||
context.protect(argument)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Transfer exactly the output roots; all other roots remain scoped.
|
||||
context.roots.truncate(transferred_from);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for NativeContext<'_> {
|
||||
fn drop(&mut self) {
|
||||
for root in self.roots.drain(..).rev() {
|
||||
self.store
|
||||
.release(root)
|
||||
.expect("native scope owns its roots");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A context-aware function with explicit, immutable captured object edges.
|
||||
#[derive(Debug)]
|
||||
pub struct ContextNativeFunction {
|
||||
name: &'static str,
|
||||
callable: Arc<dyn NativeCallable>,
|
||||
captures: Vec<ObjectRef>,
|
||||
}
|
||||
|
||||
impl ContextNativeFunction {
|
||||
pub fn new(
|
||||
name: &'static str,
|
||||
callable: impl NativeCallable,
|
||||
captures: Vec<ObjectRef>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
callable: Arc::new(callable),
|
||||
captures,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectPayload for ContextNativeFunction {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn kind_name(&self) -> &'static str {
|
||||
self.name
|
||||
}
|
||||
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
|
||||
for capture in &self.captures {
|
||||
visit(*capture);
|
||||
}
|
||||
}
|
||||
fn native_callable(&self) -> Option<Arc<dyn NativeCallable>> {
|
||||
Some(self.callable.clone())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user