use crate::host::{
ExternalPayloadLease, HostCustomToken, HostExternalToken, HostFunctionToken, HostListToken,
HostScopedValue, HostStoredValueFamily, HostTupleToken, HostValueFamily, HostValueToken,
};
use crate::plan::execution::type_::ListStorageTypeId;
use crate::runtime::evaluated::{
EvaluatedBitArray, EvaluatedCustomValue, EvaluatedExternalValue, EvaluatedFunctionValue,
EvaluatedFunctionValueKind, EvaluatedGenericFunction, EvaluatedValue,
};
use crate::runtime::function::InvocableFunctionValue;
use crate::runtime::graph::RetainedValues;
use crate::runtime::retained_list::RetainedList;
use crate::runtime::state::list::{
CustomListAllocation, ExternalListAllocation, ListValueId, ParameterListValueId,
RuntimeListStorage, StoredListValueId,
};
use ecow::EcoString;
use num_bigint::BigInt;
use std::cell::RefCell;
use std::collections::HashMap;
#[derive(Default)]
pub(super) struct ScopedValues {
ints: Vec<BigInt>,
floats: Vec<f64>,
strings: Vec<EcoString>,
bit_arrays: Vec<EvaluatedBitArray>,
utf_codepoints: Vec<char>,
bools: Vec<bool>,
parameter_lists: Vec<ParameterListValueId>,
lists: Vec<StoredListValueId>,
list_tokens: Vec<HostListToken>,
stored_list_values: HashMap<HostValueToken, StoredListValueId>,
tuples: Vec<Vec<EvaluatedValue>>,
customs: Vec<EvaluatedCustomValue>,
externals: Vec<EvaluatedExternalValue>,
functions: Vec<InvocableFunctionValue>,
symbolic_functions: Vec<EvaluatedGenericFunction>,
function_values: HashMap<HostValueToken, EvaluatedFunctionValue>,
}
pub(crate) struct StoredRuntimeValue {
value: EvaluatedValue,
type_: crate::plan::ValueType,
}
pub(crate) struct StoredRuntimeList {
retained: RetainedList<ListValueId>,
item_values: RefCell<ScopedValues>,
}
pub(crate) struct StoredRuntimeListItem<'value> {
values: &'value mut ScopedValues,
token: HostValueToken,
}
pub(crate) struct StoredRuntimeListTupleItems<'value> {
item_values: &'value mut ScopedValues,
values: Vec<EvaluatedValue>,
}
pub(crate) struct StoredRuntimeListCustomFields<'value> {
constructor: usize,
item_values: &'value mut ScopedValues,
values: Vec<EvaluatedValue>,
}
impl StoredRuntimeValue {
pub(in crate::runtime) fn new(value: EvaluatedValue, type_: crate::plan::ValueType) -> Self {
Self { value, type_ }
}
#[cfg(test)]
pub(crate) fn test_int(value: BigInt) -> Self {
Self::new(EvaluatedValue::Int(value), crate::plan::ValueType::Int)
}
pub(in crate::runtime) fn value(&self) -> &EvaluatedValue {
&self.value
}
pub(crate) fn type_(&self) -> &crate::plan::ValueType {
&self.type_
}
pub(crate) fn family(&self) -> HostStoredValueFamily {
match &self.value {
EvaluatedValue::Int(_) => HostStoredValueFamily::Int,
EvaluatedValue::Float(_) => HostStoredValueFamily::Float,
EvaluatedValue::String(_) => HostStoredValueFamily::String,
EvaluatedValue::BitArray(_) => HostStoredValueFamily::BitArray,
EvaluatedValue::UtfCodepoint(_) => HostStoredValueFamily::UtfCodepoint,
EvaluatedValue::Custom(_) => HostStoredValueFamily::Custom,
EvaluatedValue::External(_) => HostStoredValueFamily::External,
EvaluatedValue::Bool(_) => HostStoredValueFamily::Bool,
EvaluatedValue::Nil => HostStoredValueFamily::Nil,
EvaluatedValue::Tuple(_) => HostStoredValueFamily::Tuple,
EvaluatedValue::ParameterList(_) | EvaluatedValue::List(_) => {
HostStoredValueFamily::List
}
EvaluatedValue::Function(_) => HostStoredValueFamily::Function,
}
}
#[expect(
clippy::result_large_err,
reason = "non-tuples retain the original value without another heap allocation"
)]
pub(crate) fn map_tuple_items<Item>(
self,
mut map: impl FnMut(Self) -> Item,
) -> Result<Box<[Item]>, Self> {
let Self { value, type_ } = self;
match (value, type_) {
(EvaluatedValue::Tuple(values), crate::plan::ValueType::Tuple(types)) => Ok(values
.into_iter()
.zip(types)
.map(|(value, type_)| map(Self::new(value, type_)))
.collect()),
(value, type_) => Err(Self::new(value, type_)),
}
}
}
impl StoredRuntimeList {
pub(in crate::runtime) fn new(value: ListValueId) -> Self {
Self {
retained: RetainedList::new(value),
item_values: RefCell::new(ScopedValues::default()),
}
}
pub(crate) fn len(&self) -> usize {
self.retained.len()
}
pub(crate) fn decode_item<Output>(
&self,
index: usize,
decode: impl FnOnce(StoredRuntimeListItem<'_>) -> Output,
) -> Option<Output> {
let value = self.retained.item(index)?;
let mut item_values = self.item_values.borrow_mut();
let item = StoredRuntimeListItem::new(&mut item_values, value);
Some(decode(item))
}
#[cfg(test)]
pub(crate) fn test_ints(values: Vec<BigInt>) -> Self {
let plan = crate::runtime::plan_src("pub fn main() -> List(Int) { [1] }");
let type_id = plan.int_list_function_id(0).type_id();
let mut storage = RuntimeListStorage::default();
let value = storage.int(type_id, values);
Self::new(value.into())
}
#[cfg(test)]
pub(crate) fn item_reads(&self) -> usize {
self.retained.item_reads()
}
}
impl<'value> StoredRuntimeListItem<'value> {
fn new(values: &'value mut ScopedValues, value: EvaluatedValue) -> Self {
let token = values.push(value);
Self { values, token }
}
pub(crate) fn into_int(self) -> BigInt {
self.values.take_int(self.token)
}
pub(crate) fn into_float(self) -> f64 {
self.values.take_float(self.token)
}
pub(crate) fn into_string(self) -> EcoString {
self.values.take_string(self.token)
}
pub(crate) fn into_bit_array(self) -> crate::BitArrayValue {
self.values.take_bit_array(self.token)
}
pub(crate) fn into_utf_codepoint(self) -> char {
self.values.take_utf_codepoint(self.token)
}
pub(crate) fn into_bool(self) -> bool {
self.values.take_bool(self.token)
}
pub(crate) fn into_nil(self) {
self.values.take_nil(self.token);
}
pub(crate) fn into_external_lease(self) -> ExternalPayloadLease {
self.values.take_external(self.token).into_parts().1
}
pub(crate) fn into_tuple_items(self) -> StoredRuntimeListTupleItems<'value> {
StoredRuntimeListTupleItems {
values: self.values.take_tuple(self.token),
item_values: self.values,
}
}
pub(crate) fn into_list(self) -> StoredRuntimeList {
let value = self
.values
.list_value(self.values.list_tokens[self.token.index]);
StoredRuntimeList::new(value)
}
pub(crate) fn into_custom_fields(self) -> StoredRuntimeListCustomFields<'value> {
let custom = self.values.take_custom(self.token);
let (constructor, fields) = custom.into_fields();
StoredRuntimeListCustomFields {
constructor: constructor.index(),
values: fields.into_vec(),
item_values: self.values,
}
}
}
impl<'value> StoredRuntimeListTupleItems<'value> {
pub(crate) fn take_item(&mut self, index: usize) -> StoredRuntimeListItem<'_> {
StoredRuntimeListItem::new(self.item_values, self.values.swap_remove(index))
}
}
impl<'value> StoredRuntimeListCustomFields<'value> {
pub(crate) fn constructor(&self) -> usize {
self.constructor
}
pub(crate) fn take_field(&mut self, index: usize) -> StoredRuntimeListItem<'_> {
StoredRuntimeListItem::new(self.item_values, self.values.swap_remove(index))
}
}
impl ScopedValues {
pub(super) fn retain(&self, token: HostValueToken, retained: &mut RetainedValues) {
match token.family {
HostValueFamily::Int => retained.push_int(self.ints[token.index].clone()),
HostValueFamily::Float => retained.push_float(self.floats[token.index]),
HostValueFamily::String => retained.push_string(self.strings[token.index].clone()),
HostValueFamily::BitArray => {
retained.push_bit_array(self.bit_arrays[token.index].clone())
}
HostValueFamily::UtfCodepoint => {
retained.push_utf_codepoint(self.utf_codepoints[token.index])
}
HostValueFamily::Bool => retained.push_bool(self.bools[token.index]),
HostValueFamily::Nil => retained.push_nil(),
HostValueFamily::List => {
retained.push_list(self.list_value(self.list_tokens[token.index]))
}
HostValueFamily::Tuple => retained.push_tuple(self.tuples[token.index].clone()),
HostValueFamily::Custom => retained.push_custom(self.customs[token.index].clone()),
HostValueFamily::External => {
retained.push_external(self.externals[token.index].clone())
}
HostValueFamily::Function => {
retained.push_function(self.functions[token.index].clone().into_evaluated())
}
HostValueFamily::SymbolicFunction => {
retained.push_function(self.symbolic_functions[token.index].clone().into())
}
}
}
pub(super) fn push(&mut self, value: EvaluatedValue) -> HostValueToken {
match value {
EvaluatedValue::Int(value) => {
let index = self.ints.len();
self.ints.push(value);
HostValueToken {
family: HostValueFamily::Int,
index,
}
}
EvaluatedValue::Float(value) => {
let index = self.floats.len();
self.floats.push(value);
HostValueToken {
family: HostValueFamily::Float,
index,
}
}
EvaluatedValue::String(value) => {
let index = self.strings.len();
self.strings.push(value);
HostValueToken {
family: HostValueFamily::String,
index,
}
}
EvaluatedValue::BitArray(value) => {
let index = self.bit_arrays.len();
self.bit_arrays.push(value);
HostValueToken {
family: HostValueFamily::BitArray,
index,
}
}
EvaluatedValue::UtfCodepoint(value) => {
let index = self.utf_codepoints.len();
self.utf_codepoints.push(value);
HostValueToken {
family: HostValueFamily::UtfCodepoint,
index,
}
}
EvaluatedValue::Custom(value) => {
let index = self.customs.len();
self.customs.push(value);
HostValueToken {
family: HostValueFamily::Custom,
index,
}
}
EvaluatedValue::External(value) => {
let index = self.externals.len();
self.externals.push(value);
HostValueToken {
family: HostValueFamily::External,
index,
}
}
EvaluatedValue::Bool(value) => {
let index = self.bools.len();
self.bools.push(value);
HostValueToken {
family: HostValueFamily::Bool,
index,
}
}
EvaluatedValue::Nil => HostValueToken {
family: HostValueFamily::Nil,
index: 0,
},
EvaluatedValue::Tuple(value) => {
let index = self.tuples.len();
self.tuples.push(value);
HostValueToken {
family: HostValueFamily::Tuple,
index,
}
}
EvaluatedValue::ParameterList(value) => self.push_list(ListValueId::Parameter(value)),
EvaluatedValue::List(value) => {
let token = self.push_stored(value);
self.value_for_list(token)
}
EvaluatedValue::Function(value) => self.push_function(value),
}
}
pub(super) fn push_list(&mut self, value: ListValueId) -> HostValueToken {
let token = match value {
ListValueId::Parameter(value) => {
let index = self.parameter_lists.len();
self.parameter_lists.push(value);
HostListToken::Parameter(index)
}
ListValueId::Int(value) => self.push_stored(value.into()),
ListValueId::String(value) => self.push_stored(value.into()),
ListValueId::BitArray(value) => self.push_stored(value.into()),
ListValueId::UtfCodepoint(value) => self.push_stored(value.into()),
ListValueId::Custom(value) => self.push_stored(value.into()),
ListValueId::External(value) => self.push_stored(value.into()),
ListValueId::Float(value) => self.push_stored(value.into()),
ListValueId::Bool(value) => self.push_stored(value.into()),
ListValueId::Nil(value) => self.push_stored(value.into()),
ListValueId::Tuple(value) => self.push_stored(value.into()),
ListValueId::ParameterList(value) => self.push_stored(value.into()),
ListValueId::List(value) => self.push_stored(value.into()),
ListValueId::Function(value) => self.push_stored(value.into()),
};
self.value_for_list(token)
}
fn push_stored(&mut self, value: StoredListValueId) -> HostListToken {
let index = self.lists.len();
self.lists.push(value);
HostListToken::Stored(index)
}
fn value_for_list(&mut self, value: HostListToken) -> HostValueToken {
let index = self.list_tokens.len();
self.list_tokens.push(value);
let token = HostValueToken {
family: HostValueFamily::List,
index,
};
if let HostListToken::Stored(index) = value {
self.stored_list_values
.insert(token, self.lists[index].clone());
}
token
}
pub(super) fn push_scoped(&mut self, value: HostScopedValue) -> HostValueToken {
match value {
HostScopedValue::Int(value) => self.push(EvaluatedValue::Int(value)),
HostScopedValue::Float(value) => self.push(EvaluatedValue::Float(value)),
HostScopedValue::String(value) => self.push(EvaluatedValue::String(value)),
HostScopedValue::BitArray(value) => self.push(EvaluatedValue::BitArray(
EvaluatedBitArray::from_value(value),
)),
HostScopedValue::UtfCodepoint(value) => self.push(EvaluatedValue::UtfCodepoint(value)),
HostScopedValue::Bool(value) => self.push(EvaluatedValue::Bool(value)),
HostScopedValue::Nil => self.push(EvaluatedValue::Nil),
HostScopedValue::Value(token) => token,
HostScopedValue::List(value) => self.value_for_list(value),
HostScopedValue::Tuple(HostTupleToken(index)) => HostValueToken {
family: HostValueFamily::Tuple,
index,
},
HostScopedValue::Custom(HostCustomToken(index)) => HostValueToken {
family: HostValueFamily::Custom,
index,
},
HostScopedValue::External(HostExternalToken(index)) => HostValueToken {
family: HostValueFamily::External,
index,
},
HostScopedValue::Function(HostFunctionToken(index)) => HostValueToken {
family: HostValueFamily::Function,
index,
},
}
}
pub(super) fn value(&self, token: HostValueToken) -> EvaluatedValue {
match token.family {
HostValueFamily::Int => EvaluatedValue::Int(self.ints[token.index].clone()),
HostValueFamily::Float => EvaluatedValue::Float(self.floats[token.index]),
HostValueFamily::String => EvaluatedValue::String(self.strings[token.index].clone()),
HostValueFamily::BitArray => {
EvaluatedValue::BitArray(self.bit_arrays[token.index].clone())
}
HostValueFamily::UtfCodepoint => {
EvaluatedValue::UtfCodepoint(self.utf_codepoints[token.index])
}
HostValueFamily::Bool => EvaluatedValue::Bool(self.bools[token.index]),
HostValueFamily::Nil => EvaluatedValue::Nil,
HostValueFamily::List => {
EvaluatedValue::from(self.list_value(self.list_tokens[token.index]))
}
HostValueFamily::Tuple => EvaluatedValue::Tuple(self.tuples[token.index].clone()),
HostValueFamily::Custom => EvaluatedValue::Custom(self.customs[token.index].clone()),
HostValueFamily::External => {
EvaluatedValue::External(self.externals[token.index].clone())
}
HostValueFamily::Function => {
EvaluatedValue::Function(self.functions[token.index].clone().into_evaluated())
}
HostValueFamily::SymbolicFunction => {
EvaluatedValue::Function(self.symbolic_functions[token.index].clone().into())
}
}
}
pub(super) fn list_value(&self, token: HostListToken) -> ListValueId {
match token {
HostListToken::Parameter(index) => ListValueId::Parameter(self.parameter_lists[index]),
HostListToken::Stored(index) => self.lists[index].clone().into_value(),
}
}
fn push_function(&mut self, value: EvaluatedFunctionValue) -> HostValueToken {
let (family, index) = match value.kind() {
EvaluatedFunctionValueKind::Generic(function) => {
let index = self.symbolic_functions.len();
self.symbolic_functions.push(function.clone());
(HostValueFamily::SymbolicFunction, index)
}
EvaluatedFunctionValueKind::Never(function) => {
let index = self.functions.len();
self.functions
.push(InvocableFunctionValue::Never(function.clone()));
(HostValueFamily::Function, index)
}
EvaluatedFunctionValueKind::Int(function) => {
let index = self.functions.len();
self.functions
.push(InvocableFunctionValue::Int(function.clone()));
(HostValueFamily::Function, index)
}
EvaluatedFunctionValueKind::Float(function) => {
let index = self.functions.len();
self.functions
.push(InvocableFunctionValue::Float(function.clone()));
(HostValueFamily::Function, index)
}
EvaluatedFunctionValueKind::String(function) => {
let index = self.functions.len();
self.functions
.push(InvocableFunctionValue::String(function.clone()));
(HostValueFamily::Function, index)
}
EvaluatedFunctionValueKind::BitArray(function) => {
let index = self.functions.len();
self.functions
.push(InvocableFunctionValue::BitArray(function.clone()));
(HostValueFamily::Function, index)
}
EvaluatedFunctionValueKind::UtfCodepoint(function) => {
let index = self.functions.len();
self.functions
.push(InvocableFunctionValue::UtfCodepoint(function.clone()));
(HostValueFamily::Function, index)
}
EvaluatedFunctionValueKind::Custom(function) => {
let index = self.functions.len();
self.functions
.push(InvocableFunctionValue::Custom(function.clone()));
(HostValueFamily::Function, index)
}
EvaluatedFunctionValueKind::External(function) => {
let index = self.functions.len();
self.functions
.push(InvocableFunctionValue::External(function.clone()));
(HostValueFamily::Function, index)
}
EvaluatedFunctionValueKind::Bool(function) => {
let index = self.functions.len();
self.functions
.push(InvocableFunctionValue::Bool(function.clone()));
(HostValueFamily::Function, index)
}
EvaluatedFunctionValueKind::Nil(function) => {
let index = self.functions.len();
self.functions
.push(InvocableFunctionValue::Nil(function.clone()));
(HostValueFamily::Function, index)
}
EvaluatedFunctionValueKind::Tuple(function) => {
let index = self.functions.len();
self.functions
.push(InvocableFunctionValue::Tuple(function.clone()));
(HostValueFamily::Function, index)
}
EvaluatedFunctionValueKind::List(function) => {
let index = self.functions.len();
self.functions
.push(InvocableFunctionValue::List(function.clone()));
(HostValueFamily::Function, index)
}
EvaluatedFunctionValueKind::Function(function) => {
let index = self.functions.len();
self.functions
.push(InvocableFunctionValue::Function(function.clone()));
(HostValueFamily::Function, index)
}
};
let token = HostValueToken { family, index };
self.function_values.insert(token, value);
token
}
}
impl ScopedValues {
pub(super) fn allocate_list(
&self,
storage_type: ListStorageTypeId,
storage: &mut RuntimeListStorage,
values: &[HostValueToken],
) -> ListValueId {
match storage_type {
ListStorageTypeId::Parameter(type_id) => ParameterListValueId::new(type_id).into(),
ListStorageTypeId::Int(type_id) => storage
.int(
type_id,
values
.iter()
.map(|token| self.ints[token.index].clone())
.collect(),
)
.into(),
ListStorageTypeId::String(type_id) => storage
.string(
type_id,
values
.iter()
.map(|token| self.strings[token.index].clone())
.collect(),
)
.into(),
ListStorageTypeId::BitArray(type_id) => storage
.bit_array(
type_id,
values
.iter()
.map(|token| self.bit_arrays[token.index].clone())
.collect(),
)
.into(),
ListStorageTypeId::UtfCodepoint(type_id) => storage
.utf_codepoint(
type_id,
values
.iter()
.map(|token| self.utf_codepoints[token.index])
.collect(),
)
.into(),
ListStorageTypeId::Custom(type_id) => storage
.custom(CustomListAllocation::new(
type_id,
values
.iter()
.map(|token| self.customs[token.index].clone())
.collect(),
))
.into(),
ListStorageTypeId::External(type_id) => storage
.external(ExternalListAllocation::new(
type_id,
values
.iter()
.map(|token| self.externals[token.index].clone())
.collect(),
))
.into(),
ListStorageTypeId::Float(type_id) => storage
.float(
type_id,
values
.iter()
.map(|token| self.floats[token.index])
.collect(),
)
.into(),
ListStorageTypeId::Bool(type_id) => storage
.bool(
type_id,
values.iter().map(|token| self.bools[token.index]).collect(),
)
.into(),
ListStorageTypeId::Nil(type_id) => storage.nil(type_id, values.len()).into(),
ListStorageTypeId::Tuple(type_id) => storage
.tuple(
type_id,
values
.iter()
.map(|token| self.tuples[token.index].clone())
.collect(),
)
.into(),
ListStorageTypeId::ParameterList(type_id) => {
storage.parameter_list_list(type_id, values.len()).into()
}
ListStorageTypeId::List(type_id) => storage
.list(
type_id,
values
.iter()
.map(|token| self.stored_list_values[token].clone())
.collect(),
)
.into(),
ListStorageTypeId::Function(type_id) => storage
.function(
type_id,
values
.iter()
.map(|token| self.function_values[token].clone())
.collect(),
)
.into(),
}
}
pub(super) fn value_from_scoped(&self, value: HostScopedValue) -> EvaluatedValue {
match value {
HostScopedValue::Int(value) => EvaluatedValue::Int(value),
HostScopedValue::Float(value) => EvaluatedValue::Float(value),
HostScopedValue::String(value) => EvaluatedValue::String(value),
HostScopedValue::BitArray(value) => {
EvaluatedValue::BitArray(EvaluatedBitArray::from_value(value))
}
HostScopedValue::UtfCodepoint(value) => EvaluatedValue::UtfCodepoint(value),
HostScopedValue::Bool(value) => EvaluatedValue::Bool(value),
HostScopedValue::Nil => EvaluatedValue::Nil,
HostScopedValue::Value(token) => self.value(token),
HostScopedValue::List(HostListToken::Parameter(index)) => {
EvaluatedValue::ParameterList(self.parameter_lists[index])
}
HostScopedValue::List(HostListToken::Stored(index)) => {
EvaluatedValue::List(self.lists[index].clone())
}
HostScopedValue::Tuple(HostTupleToken(index)) => {
EvaluatedValue::Tuple(self.tuples[index].clone())
}
HostScopedValue::Custom(HostCustomToken(index)) => {
EvaluatedValue::Custom(self.customs[index].clone())
}
HostScopedValue::External(HostExternalToken(index)) => {
EvaluatedValue::External(self.externals[index].clone())
}
HostScopedValue::Function(HostFunctionToken(index)) => {
EvaluatedValue::Function(self.functions[index].clone().into_evaluated())
}
}
}
pub(super) fn list_token(&self, value: HostValueToken) -> HostListToken {
self.list_tokens[value.index]
}
pub(super) fn tuple_token(&self, value: HostValueToken) -> HostTupleToken {
HostTupleToken(value.index)
}
pub(super) fn custom_token(&self, value: HostValueToken) -> HostCustomToken {
HostCustomToken(value.index)
}
pub(super) fn external_token(&self, value: HostValueToken) -> HostExternalToken {
HostExternalToken(value.index)
}
pub(super) fn function_token(&self, value: HostValueToken) -> HostFunctionToken {
HostFunctionToken(value.index)
}
pub(super) fn int(&self, value: HostValueToken) -> BigInt {
self.ints[value.index].clone()
}
pub(super) fn float(&self, value: HostValueToken) -> f64 {
self.floats[value.index]
}
pub(super) fn string(&self, value: HostValueToken) -> EcoString {
self.strings[value.index].clone()
}
pub(super) fn bit_array(&self, value: HostValueToken) -> crate::BitArrayValue {
self.bit_arrays[value.index].value()
}
pub(super) fn utf_codepoint(&self, value: HostValueToken) -> char {
self.utf_codepoints[value.index]
}
pub(super) fn bool(&self, value: HostValueToken) -> bool {
self.bools[value.index]
}
fn take_int(&mut self, value: HostValueToken) -> BigInt {
self.ints.swap_remove(value.index)
}
fn take_float(&mut self, value: HostValueToken) -> f64 {
self.floats.swap_remove(value.index)
}
fn take_string(&mut self, value: HostValueToken) -> EcoString {
self.strings.swap_remove(value.index)
}
fn take_bit_array(&mut self, value: HostValueToken) -> crate::BitArrayValue {
self.bit_arrays.swap_remove(value.index).into_value()
}
fn take_utf_codepoint(&mut self, value: HostValueToken) -> char {
self.utf_codepoints.swap_remove(value.index)
}
fn take_bool(&mut self, value: HostValueToken) -> bool {
self.bools.swap_remove(value.index)
}
fn take_nil(&mut self, _value: HostValueToken) {}
fn take_external(&mut self, value: HostValueToken) -> crate::runtime::EvaluatedExternalValue {
self.externals.swap_remove(value.index)
}
fn take_tuple(&mut self, value: HostValueToken) -> Vec<EvaluatedValue> {
self.tuples.swap_remove(value.index)
}
fn take_custom(&mut self, value: HostValueToken) -> EvaluatedCustomValue {
self.customs.swap_remove(value.index)
}
pub(super) fn tuple_len(&self, value: HostTupleToken) -> usize {
self.tuples[value.0].len()
}
pub(super) fn tuple_values(&self, value: HostTupleToken) -> Vec<EvaluatedValue> {
self.tuples[value.0].clone()
}
pub(super) fn custom_constructor(&self, value: HostCustomToken) -> usize {
self.customs[value.0].constructor().index()
}
pub(super) fn custom_fields(&self, value: HostCustomToken) -> Vec<EvaluatedValue> {
self.customs[value.0].fields().to_vec()
}
pub(super) fn take_custom_fields(&mut self, value: HostCustomToken) -> Box<[EvaluatedValue]> {
self.customs[value.0].take_fields()
}
pub(super) fn function(&self, value: HostFunctionToken) -> InvocableFunctionValue {
self.functions[value.0].clone()
}
pub(super) fn push_tuple(&mut self, values: Vec<EvaluatedValue>) -> HostValueToken {
let index = self.tuples.len();
self.tuples.push(values);
HostValueToken {
family: HostValueFamily::Tuple,
index,
}
}
pub(super) fn push_custom(&mut self, value: EvaluatedCustomValue) -> HostValueToken {
let index = self.customs.len();
self.customs.push(value);
HostValueToken {
family: HostValueFamily::Custom,
index,
}
}
pub(super) fn push_external(&mut self, value: EvaluatedExternalValue) -> HostExternalToken {
let index = self.externals.len();
self.externals.push(value);
HostExternalToken(index)
}
pub(super) fn external(&self, value: HostExternalToken) -> &EvaluatedExternalValue {
&self.externals[value.0]
}
}
#[cfg(test)]
mod tests {
use super::{ScopedValues, StoredRuntimeListItem, StoredRuntimeValue};
use crate::host::HostCustomToken;
use crate::host::test::{StatelessTestProvider, TestTypeParameter, stateless_identity};
use crate::runtime::state::list::RuntimeListStorage;
use crate::runtime::{EvaluatedBitArray, EvaluatedCustomValue, EvaluatedValue};
use crate::{
BitArrayValue, HostCall, HostCallCompletion, HostCallError, HostList, HostListType,
HostModule, HostProviderModule, HostProviderSet, HostTupleType, HostTypeList,
HostTypeListEnd, HostedExecution, ModuleSource, PackageSource, StatelessHostProfile, Value,
compile_typed_host_program, plan_host_program,
};
use ecow::EcoString;
use num_bigint::BigInt;
#[test]
fn stored_runtime_value_preserves_its_exact_type_and_restores_its_value() {
let stored =
StoredRuntimeValue::new(EvaluatedValue::Int(7.into()), crate::plan::ValueType::Int);
let mut scoped = ScopedValues::default();
let restored = scoped.push(stored.value().clone());
assert_eq!(stored.type_(), &crate::plan::ValueType::Int);
assert_eq!(scoped.int(restored), BigInt::from(7));
}
#[test]
fn stored_list_items_decode_every_supported_scalar_family() {
let bits = BitArrayValue::from_bytes(vec![1]);
let mut values = ScopedValues::default();
assert_eq!(
StoredRuntimeListItem::new(&mut values, EvaluatedValue::Int(7.into())).into_int(),
BigInt::from(7),
);
assert_eq!(
StoredRuntimeListItem::new(&mut values, EvaluatedValue::Float(1.5)).into_float(),
1.5,
);
assert_eq!(
StoredRuntimeListItem::new(&mut values, EvaluatedValue::String("text".into()),)
.into_string(),
EcoString::from("text"),
);
assert_eq!(
StoredRuntimeListItem::new(
&mut values,
EvaluatedValue::BitArray(EvaluatedBitArray::from_value(bits.clone())),
)
.into_bit_array(),
bits,
);
assert_eq!(
StoredRuntimeListItem::new(&mut values, EvaluatedValue::UtfCodepoint('A'),)
.into_utf_codepoint(),
'A',
);
assert!(StoredRuntimeListItem::new(&mut values, EvaluatedValue::Bool(true)).into_bool());
StoredRuntimeListItem::new(&mut values, EvaluatedValue::Nil).into_nil();
}
#[test]
fn stored_list_items_preserve_external_leases_and_tuple_element_order() {
let store = crate::host::HostExternalStore::default();
let lease = store.insert(
7usize,
|_, left, right| left == right,
|_, value| *value as u64,
|context, value| {
let stored = crate::host::HostStoredValue::<BigInt>::new(
StoredRuntimeValue::test_int((*value).into()),
);
format!("Payload({})", context.inspect_stored_value(&stored)).into()
},
);
let external = crate::runtime::evaluated::EvaluatedExternalValue::new(
crate::plan::execution::type_::ExternalTypeId::new(0),
lease.clone(),
);
let mut values = ScopedValues::default();
let retained = StoredRuntimeListItem::new(&mut values, EvaluatedValue::External(external))
.into_external_lease();
assert_eq!(retained.id(), lease.id());
let stored_equal = |_: &StoredRuntimeValue, _: &StoredRuntimeValue| false;
let stored_hash = |_: &StoredRuntimeValue| 0;
let stored_inspect = |_: &StoredRuntimeValue| EcoString::from("7");
let equality = crate::host::HostExternalEquality::new(&stored_equal);
let hashing = crate::host::HostExternalHashing::new(&stored_hash);
let inspection = crate::host::HostExternalInspection::new(&stored_inspect);
assert!(retained.source_equal(&equality, &lease));
assert_eq!(retained.source_hash(&hashing), 7);
assert_eq!(retained.inspection(&inspection), "Payload(7)");
let mut tuple = StoredRuntimeListItem::new(
&mut values,
EvaluatedValue::Tuple(vec![
EvaluatedValue::Int(1.into()),
EvaluatedValue::String("second".into()),
]),
)
.into_tuple_items();
let second = tuple.take_item(1).into_string();
let first = tuple.take_item(0).into_int();
assert_eq!(first, BigInt::from(1),);
assert_eq!(second, EcoString::from("second"));
}
#[test]
fn stored_list_custom_fields_preserve_constructor_order_and_nested_list_storage() {
let plan = crate::runtime::plan_src(
"pub type Item { Item(Int, List(Int)) } pub fn values() { [2] } pub fn main() { Item(1, values()) }",
);
let mut storage = RuntimeListStorage::default();
let nested = storage.int(plan.int_list_function_id(0).type_id(), vec![2.into()]);
let custom = EvaluatedCustomValue::from_fields(
plan.custom_constructor_id(0, 0),
vec![
EvaluatedValue::Int(1.into()),
EvaluatedValue::List(nested.into()),
]
.into_boxed_slice(),
);
let mut values = ScopedValues::default();
let mut fields = StoredRuntimeListItem::new(&mut values, EvaluatedValue::Custom(custom))
.into_custom_fields();
assert_eq!(fields.constructor(), 0);
let nested = fields.take_field(1).into_list();
assert_eq!(nested.len(), 1);
assert_eq!(
nested.decode_item(0, |item| item.into_int()),
Some(BigInt::from(2)),
);
assert_eq!(fields.take_field(0).into_int(), BigInt::from(1));
}
#[test]
fn taking_custom_fields_preserves_other_scoped_custom_tokens() {
let plan = crate::runtime::plan_src(
"pub type Item { First(Int) Second(String) } pub fn main() { #(First(7), Second(\"kept\")) }",
);
let first = EvaluatedCustomValue::from_fields(
plan.custom_constructor_id(0, 0),
vec![EvaluatedValue::Int(7.into())].into_boxed_slice(),
);
let second = EvaluatedCustomValue::from_fields(
plan.custom_constructor_id(0, 1),
vec![EvaluatedValue::String("kept".into())].into_boxed_slice(),
);
let mut values = ScopedValues::default();
values.push_custom(first);
values.push_custom(second);
assert_eq!(
values.take_custom_fields(HostCustomToken(0)).as_ref(),
&[EvaluatedValue::Int(7.into())],
);
assert!(values.custom_fields(HostCustomToken(0)).is_empty());
assert_eq!(
values.custom_constructor(HostCustomToken(1)),
plan.custom_constructor_id(0, 1).index(),
);
assert_eq!(
values.custom_fields(HostCustomToken(1)),
vec![EvaluatedValue::String("kept".into())],
);
}
#[test]
fn runtime_host_call_reads_scoped_compound_values() {
type Seventh = HostTypeList<(), HostTypeListEnd>;
type Sixth = HostTypeList<bool, Seventh>;
type Fifth = HostTypeList<char, Sixth>;
type Fourth = HostTypeList<BitArrayValue, Fifth>;
type Third = HostTypeList<EcoString, Fourth>;
type Second = HostTypeList<f64, Third>;
type Elements = HostTypeList<BigInt, Second>;
type Tuple = HostTupleType<Elements>;
type Values = HostListType<Tuple>;
fn inspect<'call>(
mut call: HostCall<'call, StatelessHostProfile, StatelessTestProvider, bool>,
values: HostList<'call, Tuple>,
) -> Result<HostCallCompletion<'call, bool>, HostCallError> {
let value = call
.list_item(values, 0)
.expect("tuple list should contain one value");
let (int, (float, (string, (bits, (codepoint, (bool_, (nil, ()))))))) =
call.tuple_values(value);
let matches = call.tuple_len(value) == 7
&& call.equal::<Tuple>(value, value)
&& call.equal::<BigInt>(int, 1.into())
&& call.equal::<f64>(float, 1.5)
&& call.equal::<EcoString>(string, "text".into())
&& call.equal::<BitArrayValue>(bits, BitArrayValue::from_bytes(vec![1]))
&& call.equal::<char>(codepoint, 'A')
&& call.equal::<bool>(bool_, true)
&& call.equal::<()>(nil, ());
Ok(call.return_value(matches))
}
let provider = HostProviderModule::<StatelessHostProfile>::new("application", "main")
.expect("provider module should be valid")
.with_scoped_function::<StatelessTestProvider, (Values,), bool, _>("inspect", inspect)
.expect("tuple-list provider should be valid")
.with_scoped_function::<
StatelessTestProvider,
(TestTypeParameter,),
TestTypeParameter,
_,
>("identity", stateless_identity)
.expect("tuple-list provider should be valid");
let source = r#"
@external(erlang, "host", "inspect")
fn inspect(
values: List(#(Int, Float, String, BitArray, UtfCodepoint, Bool, Nil)),
) -> Bool
@external(erlang, "host", "identity")
fn identity(value: value) -> value
pub fn main() {
let assert <<codepoint:utf8_codepoint>> = <<"A":utf8>>
inspect(identity([#(1, 1.5, "text", <<1>>, codepoint, True, Nil)]))
}
"#;
let typed = compile_typed_host_program(
"application",
"main",
[PackageSource::new(
"application",
Vec::<EcoString>::new(),
[ModuleSource::new("main", "src/main.gleam", source)],
)],
HostProviderSet::with_providers(Vec::<HostModule>::new(), [provider])
.expect("provider module should be unique"),
)
.expect("host source should compile");
let plan = plan_host_program(typed).expect("host source should plan");
let execution =
HostedExecution::try_from_module_plan(plan).expect("hosted execution should seal");
let expected = Value::Bool(true);
assert_eq!(execution.run_main(&mut (), &mut Vec::new()), Ok(expected),);
}
}