Skip to content

Commit

Permalink
Implement ExternalValue
Browse files Browse the repository at this point in the history
This is initial implementation. It's not well tested.

Signed-off-by: Tin Švagelj <[email protected]>
  • Loading branch information
Caellian committed Oct 15, 2024
1 parent c707c5d commit 8595553
Show file tree
Hide file tree
Showing 4 changed files with 425 additions and 12 deletions.
19 changes: 19 additions & 0 deletions interpreter/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,25 @@ pub enum Context<'a> {
}

impl<'a> Context<'a> {
pub fn serialize_variable<S, V>(
&mut self,
name: S,
value: V,
) -> Result<(), Box<dyn std::error::Error>>
where
S: Into<String>,
V: serde::Serialize,
{
match self {
Context::Root { variables, .. } => {
variables.insert(name.into(), crate::ser::to_value(value)?);
}
Context::Child { variables, .. } => {
variables.insert(name.into(), crate::ser::to_value(value)?);
}
}
Ok(())
}
pub fn add_variable<S, V>(
&mut self,
name: S,
Expand Down
304 changes: 304 additions & 0 deletions interpreter/src/external.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
use std::{
any::TypeId,
cmp::Ordering,
fmt::Debug,
sync::{atomic::AtomicUsize, Arc},
};

use crate::{
objects::{TryIntoValue, ValueType},
ExecutionError, ResolveResult, Value,
};

#[allow(unused_variables)]
pub trait AsValue: Send + Sync {
#[inline]
fn value_field(&self, name: &str) -> ResolveResult {
Err(ExecutionError::NoSuchKey(Arc::new(
name.to_string(),
)))
}

#[inline]
fn value_index(&self, index: &Value) -> ResolveResult {
Err(ExecutionError::UnsupportedIndexExternal(
index.clone(),
std::any::type_name::<Self>(),
))
}

#[inline]
fn to_value(&self, ty: ValueType) -> Option<Value> {
None
}

/// Called when other [`Value`] is added to this type.
#[inline]
fn value_add(&self, second: &Value) -> ResolveResult {
Err(ExecutionError::UnsupportedBinaryOperatorExternal(
"+",
std::any::type_name::<Self>(),
"Value",
))
}

/// Called when this type is added to other [`Value`].
///
/// Override if addition is non-commutative.
#[inline]
fn value_add_other(&self, first: &Value) -> ResolveResult {
AsValue::value_add(self, first).map_err(|err| match err {
ExecutionError::UnsupportedBinaryOperatorExternal(op, a, b) => {
ExecutionError::UnsupportedBinaryOperatorExternal(op, b, a)
}
other => other,
})
}

/// Called when other [`Value`] is subtracted from this type.
#[inline]
fn value_sub(&self, second: &Value) -> ResolveResult {
Err(ExecutionError::UnsupportedBinaryOperatorExternal(
"-",
std::any::type_name::<Self>(),
"Value",
))
}

/// Called when this type is subtracted from other [`Value`].
///
/// Override if subtraction is non-commutative.
#[inline]
fn value_sub_other(&self, first: &Value) -> ResolveResult {
AsValue::value_sub(self, first).map_err(|err| match err {
ExecutionError::UnsupportedBinaryOperatorExternal(op, a, b) => {
ExecutionError::UnsupportedBinaryOperatorExternal(op, b, a)
}
other => other,
})
}

/// Called when this type is multiplied with other [`Value`].
#[inline]
fn value_mul(&self, second: &Value) -> ResolveResult {
Err(ExecutionError::UnsupportedBinaryOperatorExternal(
"*",
std::any::type_name::<Self>(),
"Value",
))
}

/// Called when other [`Value`] is multiplied with this type.
///
/// Override if multiplication is non-commutative.
#[inline]
fn value_mul_other(&self, first: &Value) -> ResolveResult {
AsValue::value_mul(self, first).map_err(|err| match err {
ExecutionError::UnsupportedBinaryOperatorExternal(op, a, b) => {
ExecutionError::UnsupportedBinaryOperatorExternal(op, b, a)
}
other => other,
})
}

/// Called when other [`Value`] is divided by this type.
#[inline]
fn value_div(&self, second: &Value) -> ResolveResult {
Err(ExecutionError::UnsupportedBinaryOperatorExternal(
"/",
std::any::type_name::<Self>(),
"Value",
))
}

/// Called when this type divided by other [`Value`].
#[inline]
fn value_div_other(&self, first: &Value) -> ResolveResult {
Err(ExecutionError::UnsupportedBinaryOperatorExternal(
"/",
"Value",
std::any::type_name::<Self>(),
))
}

/// Called when modulo of other [`Value`] is applied to this type.
#[inline]
fn value_rem(&self, second: &Value) -> ResolveResult {
Err(ExecutionError::UnsupportedBinaryOperatorExternal(
"%",
std::any::type_name::<Self>(),
"Value",
))
}

/// Called when modulo of this type is applied to other [`Value`].
#[inline]
fn value_rem_other(&self, first: &Value) -> ResolveResult {
Err(ExecutionError::UnsupportedBinaryOperatorExternal(
"%",
"Value",
std::any::type_name::<Self>(),
))
}

/// Called when this type is compared to other [`Value`].
#[inline]
fn value_cmp(&self, second: &Value) -> Result<Ordering, ExecutionError> {
Err(ExecutionError::UnsupportedBinaryOperatorExternal(
"cmp",
std::any::type_name::<Self>(),
"Value",
))
}

/// Called when this type is checked for equality to other [`Value`].
#[inline]
fn value_eq(&self, second: &Value) -> bool {
false
}
}

impl<V: AsValue + 'static> TryIntoValue for V {
type Error = Infalliable;

fn try_into_value(self) -> Result<Value, Self::Error> {
unsafe { Ok(Value::External(ExternalValue::new(self))) }
}
}

pub struct ExternalValue {
r#type: TypeId,
data: *mut (),
drop_fn: DropFn,
use_counter: *mut AtomicUsize,
external: Option<*const ()>,
}

impl ExternalValue {
/// # Safety
///
/// Type `T` must be kept alive for as long as returned `ExternalValue`
/// exists.
pub unsafe fn new_opaque<T>(value: T) -> Self
where
T: Sized + Send + Sync + 'static,
{
Self {
r#type: TypeId::of::<T>(),
data: Box::leak(Box::new(value)) as *mut T as *mut (),
drop_fn: drop_fn::<T>(),
use_counter: Box::leak(Box::new(AtomicUsize::new(1))) as *mut AtomicUsize,
external: None,
}
}

/// # Safety
///
/// Type `T` must be kept alive for as long as returned `ExternalValue`
/// exists.
pub unsafe fn new<T>(value: T) -> Self
where
T: AsValue + Sized + Send + Sync + 'static,
{
let vtable = unsafe {
let value = &value as &dyn AsValue;
let (_, vtable): (*const (), *const ()) = std::mem::transmute(value);
vtable
};

Self {
r#type: TypeId::of::<T>(),
data: Box::leak(Box::new(value)) as *mut T as *mut (),
drop_fn: drop_fn::<T>(),
use_counter: Box::leak(Box::new(AtomicUsize::new(1))) as *mut AtomicUsize,
external: Some(vtable),
}
}

pub fn as_ref<T>(&self) -> Option<&T>
where
T: 'static + Sized + Send + Sync,
{
if self.r#type != TypeId::of::<T>() {
return None;
}
let data = self.data as *const T;
Some(unsafe { data.as_ref().unwrap() })
}

pub fn as_value(&self) -> Option<&dyn AsValue> {
let vtable = self.external?;
Some(unsafe { std::mem::transmute((self.data, vtable)) })
}

/// # Safety
///
/// Calling this function is unsafe if `ExternalValue` was constructed from
/// opaque type.
pub unsafe fn as_value_unchecked(&self) -> &dyn AsValue {
let vtable = unsafe {
// SAFETY: This operation
self.external.unwrap_unchecked()
};
unsafe { std::mem::transmute((self.data, vtable)) }
}

pub fn is_opaque(&self) -> bool {
!self.external.is_some()
}
}

impl Clone for ExternalValue {
fn clone(&self) -> Self {
unsafe {
let uses = match self.use_counter.as_mut() {
Some(it) => it,
None => unreachable!("use_counter is null"),
};
uses.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}

Self {
r#type: self.r#type,
data: self.data,
drop_fn: self.drop_fn,
use_counter: self.use_counter,
external: self.external,
}
}
}

impl Debug for ExternalValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// Requires specialization#31844 to show useful information
f.write_str("ExternalValue")
}
}

impl Drop for ExternalValue {
fn drop(&mut self) {
unsafe {
let uses = match self.use_counter.as_mut() {
Some(it) => it,
None => unreachable!("use_counter is null"),
};
let use_count = uses.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
if use_count == 0 {
(self.drop_fn)(self.data);
drop(Box::from_raw(self.use_counter));
}
}
}
}

type DropFn = fn(*mut ());
const fn drop_fn<T>() -> DropFn {
if core::mem::needs_drop::<T>() {
|ptr: *mut ()| unsafe { core::ptr::drop_in_place(ptr as *mut T) }
} else {
|_: *mut ()| {}
}
}

#[derive(Debug, thiserror::Error)]
pub enum Infalliable {}
8 changes: 8 additions & 0 deletions interpreter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub use functions::FunctionContext;
pub use objects::{ResolveResult, Value};
mod duration;
pub mod functions;
pub mod external;
mod magic;
pub mod objects;
mod resolvers;
Expand Down Expand Up @@ -73,6 +74,10 @@ pub enum ExecutionError {
/// where it's unsupported, for example list + map.
#[error("Unsupported binary operator '{0}': {1:?}, {2:?}")]
UnsupportedBinaryOperator(&'static str, Value, Value),
/// Indicates that an unsupported binary operator was applied on an external
/// value
#[error("Unsupported binary operator: '{1}' {0} '{2}'")]
UnsupportedBinaryOperatorExternal(&'static str, &'static str, &'static str),
/// Indicates that an unsupported type was used to index a map
#[error("Cannot use value as map index: {0:?}")]
UnsupportedMapIndex(Value),
Expand All @@ -82,6 +87,9 @@ pub enum ExecutionError {
/// Indicates that an unsupported type was used to index a list
#[error("Cannot use value {0:?} to index {1:?}")]
UnsupportedIndex(Value, Value),
/// Indicates that an unsupported type was used to index an external type
#[error("Cannot use value {0:?} to index {1}")]
UnsupportedIndexExternal(Value, &'static str),
/// Indicates that a function call occurred without an [`Expression::Ident`]
/// as the function identifier.
#[error("Unsupported function call identifier type: {0:?}")]
Expand Down
Loading

0 comments on commit 8595553

Please sign in to comment.