rune/runtime/
const_value.rs

1#[macro_use]
2mod macros;
3
4use core::any;
5use core::cmp::Ordering;
6use core::fmt;
7
8use rust_alloc::sync::Arc;
9
10use serde::{Deserialize, Serialize};
11
12use crate as rune;
13use crate::alloc::prelude::*;
14use crate::alloc::{self, HashMap};
15use crate::runtime;
16use crate::{Hash, TypeHash};
17
18use super::{
19    Bytes, FromValue, Inline, Object, OwnedTuple, Repr, ToValue, Tuple, Type, TypeInfo, Value,
20    VmErrorKind,
21};
22
23/// Derive for the [`ToConstValue`](trait@ToConstValue) trait.
24pub use rune_macros::ToConstValue;
25
26use super::{AnyTypeInfo, RuntimeError, VmIntegerRepr};
27
28/// Cheap conversion trait to convert something infallibly into a [`ConstValue`].
29pub trait IntoConstValue {
30    /// Convert into a dynamic [`ConstValue`].
31    #[doc(hidden)]
32    fn into_const_value(self) -> alloc::Result<ConstValue>;
33}
34
35impl IntoConstValue for ConstValue {
36    #[inline]
37    fn into_const_value(self) -> alloc::Result<ConstValue> {
38        Ok(self)
39    }
40}
41
42impl IntoConstValue for &ConstValue {
43    #[inline]
44    fn into_const_value(self) -> alloc::Result<ConstValue> {
45        self.try_clone()
46    }
47}
48
49/// Convert something into a [`ConstValue`].
50///
51/// # Examples
52///
53/// ```
54/// let value = rune::to_const_value((i32::MIN, u64::MAX))?;
55/// let (a, b) = rune::from_const_value::<(i32, u64)>(value)?;
56///
57/// assert_eq!(a, i32::MIN);
58/// assert_eq!(b, u64::MAX);
59/// # Ok::<_, rune::support::Error>(())
60/// ```
61pub fn from_const_value<T>(value: impl IntoConstValue) -> Result<T, RuntimeError>
62where
63    T: FromConstValue,
64{
65    T::from_const_value(value.into_const_value()?)
66}
67
68/// Convert something into a [`ConstValue`].
69///
70/// # Examples
71///
72/// ```
73/// let value = rune::to_const_value((i32::MIN, u64::MAX))?;
74/// let (a, b) = rune::from_const_value::<(i32, u64)>(value)?;
75///
76/// assert_eq!(a, i32::MIN);
77/// assert_eq!(b, u64::MAX);
78/// # Ok::<_, rune::support::Error>(())
79/// ```
80pub fn to_const_value(value: impl ToConstValue) -> Result<ConstValue, RuntimeError> {
81    value.to_const_value()
82}
83
84/// Convert a value into a constant value.
85pub trait ToConstValue: Sized {
86    /// Convert into a constant value.
87    fn to_const_value(self) -> Result<ConstValue, RuntimeError>;
88
89    /// Return the constant constructor for the given type.
90    #[inline]
91    fn construct() -> Option<Arc<dyn ConstConstruct>> {
92        None
93    }
94}
95
96impl ToConstValue for ConstValue {
97    #[inline]
98    fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
99        Ok(self)
100    }
101}
102
103impl ToConstValue for Value {
104    #[inline]
105    fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
106        ConstValue::from_value_ref(&self)
107    }
108}
109
110#[derive(Debug, TryClone, Deserialize, Serialize)]
111pub(crate) enum ConstValueKind {
112    /// An inline constant value.
113    Inline(#[try_clone(copy)] Inline),
114    /// A string constant designated by its slot.
115    String(String),
116    /// A byte string.
117    Bytes(Bytes),
118    /// A vector of values.
119    Vec(Vec<ConstValue>),
120    /// An anonymous tuple.
121    Tuple(Box<[ConstValue]>),
122    /// An anonymous object.
123    Object(HashMap<String, ConstValue>),
124    /// An option.
125    Option(Option<Box<ConstValue>>),
126    /// A struct with the given type.
127    Struct(Hash, Box<[ConstValue]>),
128}
129
130impl ConstValueKind {
131    fn type_info(&self) -> TypeInfo {
132        fn full_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
133            write!(f, "constant struct")
134        }
135
136        match self {
137            ConstValueKind::Inline(value) => value.type_info(),
138            ConstValueKind::String(..) => TypeInfo::any::<String>(),
139            ConstValueKind::Bytes(..) => TypeInfo::any::<Bytes>(),
140            ConstValueKind::Vec(..) => TypeInfo::any::<runtime::Vec>(),
141            ConstValueKind::Tuple(..) => TypeInfo::any::<OwnedTuple>(),
142            ConstValueKind::Object(..) => TypeInfo::any::<Object>(),
143            ConstValueKind::Option(..) => TypeInfo::any::<Option<Value>>(),
144            ConstValueKind::Struct(hash, ..) => {
145                TypeInfo::any_type_info(AnyTypeInfo::new(full_name, *hash))
146            }
147        }
148    }
149}
150
151/// A constant value.
152#[derive(Deserialize, Serialize)]
153#[serde(transparent)]
154pub struct ConstValue {
155    kind: ConstValueKind,
156}
157
158impl ConstValue {
159    /// Construct a new tuple constant value.
160    pub fn tuple(values: Box<[ConstValue]>) -> ConstValue {
161        ConstValue {
162            kind: ConstValueKind::Tuple(values),
163        }
164    }
165
166    /// Construct a constant value for a struct.
167    pub fn for_struct<const N: usize>(
168        hash: Hash,
169        fields: [ConstValue; N],
170    ) -> Result<ConstValue, RuntimeError> {
171        let fields = Box::<[ConstValue]>::try_from(fields)?;
172
173        Ok(ConstValue {
174            kind: ConstValueKind::Struct(hash, fields),
175        })
176    }
177
178    /// Try to coerce the current value as the specified integer `T`.
179    ///
180    /// # Examples
181    ///
182    /// ```
183    /// let value = rune::to_const_value(u32::MAX)?;
184    ///
185    /// assert_eq!(value.as_integer::<u64>()?, u32::MAX as u64);
186    /// assert!(value.as_integer::<i32>().is_err());
187    ///
188    /// # Ok::<(), rune::support::Error>(())
189    /// ```
190    pub fn as_integer<T>(&self) -> Result<T, RuntimeError>
191    where
192        T: TryFrom<i64> + TryFrom<u64>,
193    {
194        match self.kind {
195            ConstValueKind::Inline(Inline::Signed(value)) => match value.try_into() {
196                Ok(number) => Ok(number),
197                Err(..) => Err(RuntimeError::new(
198                    VmErrorKind::ValueToIntegerCoercionError {
199                        from: VmIntegerRepr::from(value),
200                        to: any::type_name::<T>(),
201                    },
202                )),
203            },
204            ConstValueKind::Inline(Inline::Unsigned(value)) => match value.try_into() {
205                Ok(number) => Ok(number),
206                Err(..) => Err(RuntimeError::new(
207                    VmErrorKind::ValueToIntegerCoercionError {
208                        from: VmIntegerRepr::from(value),
209                        to: any::type_name::<T>(),
210                    },
211                )),
212            },
213            ref kind => Err(RuntimeError::new(VmErrorKind::ExpectedNumber {
214                actual: kind.type_info(),
215            })),
216        }
217    }
218
219    inline_macros!(inline_into);
220
221    /// Coerce into tuple.
222    #[inline]
223    pub fn into_tuple(self) -> Result<Box<[ConstValue]>, RuntimeError> {
224        match self.kind {
225            ConstValueKind::Tuple(tuple) => Ok(tuple),
226            kind => Err(RuntimeError::expected::<Tuple>(kind.type_info())),
227        }
228    }
229
230    /// Access the interior value.
231    pub(crate) fn as_kind(&self) -> &ConstValueKind {
232        &self.kind
233    }
234
235    /// Construct a constant value from a reference to a value..
236    pub(crate) fn from_value_ref(value: &Value) -> Result<ConstValue, RuntimeError> {
237        let inner = match value.as_ref() {
238            Repr::Inline(value) => ConstValueKind::Inline(*value),
239            Repr::Dynamic(value) => {
240                return Err(RuntimeError::from(VmErrorKind::ConstNotSupported {
241                    actual: value.type_info(),
242                }));
243            }
244            Repr::Any(value) => match value.type_hash() {
245                Option::<Value>::HASH => {
246                    let option = value.borrow_ref::<Option<Value>>()?;
247
248                    ConstValueKind::Option(match &*option {
249                        Some(some) => Some(Box::try_new(Self::from_value_ref(some)?)?),
250                        None => None,
251                    })
252                }
253                String::HASH => {
254                    let s = value.borrow_ref::<String>()?;
255                    ConstValueKind::String(s.try_to_owned()?)
256                }
257                Bytes::HASH => {
258                    let s = value.borrow_ref::<Bytes>()?;
259                    ConstValueKind::Bytes(s.try_to_owned()?)
260                }
261                runtime::Vec::HASH => {
262                    let vec = value.borrow_ref::<runtime::Vec>()?;
263                    let mut const_vec = Vec::try_with_capacity(vec.len())?;
264
265                    for value in vec.iter() {
266                        const_vec.try_push(Self::from_value_ref(value)?)?;
267                    }
268
269                    ConstValueKind::Vec(const_vec)
270                }
271                runtime::OwnedTuple::HASH => {
272                    let tuple = value.borrow_ref::<runtime::OwnedTuple>()?;
273                    let mut const_tuple = Vec::try_with_capacity(tuple.len())?;
274
275                    for value in tuple.iter() {
276                        const_tuple.try_push(Self::from_value_ref(value)?)?;
277                    }
278
279                    ConstValueKind::Tuple(const_tuple.try_into_boxed_slice()?)
280                }
281                Object::HASH => {
282                    let object = value.borrow_ref::<Object>()?;
283                    let mut const_object = HashMap::try_with_capacity(object.len())?;
284
285                    for (key, value) in object.iter() {
286                        let key = key.try_clone()?;
287                        let value = Self::from_value_ref(value)?;
288                        const_object.try_insert(key, value)?;
289                    }
290
291                    ConstValueKind::Object(const_object)
292                }
293                _ => {
294                    return Err(RuntimeError::from(VmErrorKind::ConstNotSupported {
295                        actual: value.type_info(),
296                    }));
297                }
298            },
299        };
300
301        Ok(Self { kind: inner })
302    }
303
304    #[inline]
305    #[cfg(test)]
306    pub(crate) fn to_value(&self) -> Result<Value, RuntimeError> {
307        self.to_value_with(&EmptyConstContext)
308    }
309
310    /// Convert into virtual machine value.
311    ///
312    /// We provide this associated method since a constant value can be
313    /// converted into a value infallibly, which is not captured by the trait
314    /// otherwise.
315    pub(crate) fn to_value_with(&self, cx: &dyn ConstContext) -> Result<Value, RuntimeError> {
316        match &self.kind {
317            ConstValueKind::Inline(value) => Ok(Value::from(*value)),
318            ConstValueKind::String(string) => Ok(Value::try_from(string.try_clone()?)?),
319            ConstValueKind::Bytes(b) => Ok(Value::try_from(b.try_clone()?)?),
320            ConstValueKind::Option(option) => Ok(Value::try_from(match option {
321                Some(some) => Some(Self::to_value_with(some, cx)?),
322                None => None,
323            })?),
324            ConstValueKind::Vec(vec) => {
325                let mut v = runtime::Vec::with_capacity(vec.len())?;
326
327                for value in vec {
328                    v.push(Self::to_value_with(value, cx)?)?;
329                }
330
331                Ok(Value::try_from(v)?)
332            }
333            ConstValueKind::Tuple(tuple) => {
334                let mut t = Vec::try_with_capacity(tuple.len())?;
335
336                for value in tuple.iter() {
337                    t.try_push(Self::to_value_with(value, cx)?)?;
338                }
339
340                Ok(Value::try_from(OwnedTuple::try_from(t)?)?)
341            }
342            ConstValueKind::Object(object) => {
343                let mut o = Object::with_capacity(object.len())?;
344
345                for (key, value) in object {
346                    let key = key.try_clone()?;
347                    let value = Self::to_value_with(value, cx)?;
348                    o.insert(key, value)?;
349                }
350
351                Ok(Value::try_from(o)?)
352            }
353            ConstValueKind::Struct(hash, fields) => {
354                let Some(constructor) = cx.get(*hash) else {
355                    return Err(RuntimeError::missing_constant_constructor(*hash));
356                };
357
358                constructor.const_construct(fields)
359            }
360        }
361    }
362
363    /// Get the type information of the value.
364    pub(crate) fn type_info(&self) -> TypeInfo {
365        self.kind.type_info()
366    }
367}
368
369impl TryClone for ConstValue {
370    fn try_clone(&self) -> alloc::Result<Self> {
371        Ok(Self {
372            kind: self.kind.try_clone()?,
373        })
374    }
375}
376
377impl FromValue for ConstValue {
378    #[inline]
379    fn from_value(value: Value) -> Result<Self, RuntimeError> {
380        ConstValue::from_value_ref(&value)
381    }
382}
383
384impl ToValue for ConstValue {
385    #[inline]
386    fn to_value(self) -> Result<Value, RuntimeError> {
387        ConstValue::to_value_with(&self, &EmptyConstContext)
388    }
389}
390
391impl From<ConstValueKind> for ConstValue {
392    #[inline]
393    fn from(kind: ConstValueKind) -> Self {
394        Self { kind }
395    }
396}
397
398impl From<Inline> for ConstValue {
399    #[inline]
400    fn from(value: Inline) -> Self {
401        Self::from(ConstValueKind::Inline(value))
402    }
403}
404
405impl From<String> for ConstValue {
406    #[inline]
407    fn from(value: String) -> Self {
408        Self::from(ConstValueKind::String(value))
409    }
410}
411
412impl From<Bytes> for ConstValue {
413    #[inline]
414    fn from(value: Bytes) -> Self {
415        Self::from(ConstValueKind::Bytes(value))
416    }
417}
418
419impl TryFrom<&str> for ConstValue {
420    type Error = alloc::Error;
421
422    #[inline]
423    fn try_from(value: &str) -> Result<Self, Self::Error> {
424        Ok(ConstValue::from(String::try_from(value)?))
425    }
426}
427
428impl ToConstValue for &str {
429    #[inline]
430    fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
431        Ok(ConstValue::try_from(self)?)
432    }
433}
434
435impl TryFrom<&[u8]> for ConstValue {
436    type Error = alloc::Error;
437
438    #[inline]
439    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
440        Ok(ConstValue::from(Bytes::try_from(value)?))
441    }
442}
443
444impl ToConstValue for &[u8] {
445    #[inline]
446    fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
447        Ok(ConstValue::try_from(self)?)
448    }
449}
450
451impl fmt::Debug for ConstValue {
452    #[inline]
453    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454        self.kind.fmt(f)
455    }
456}
457
458/// Convert a value from a constant value.
459pub trait FromConstValue: Sized {
460    /// Convert from a constant value.
461    fn from_const_value(value: ConstValue) -> Result<Self, RuntimeError>;
462}
463
464impl FromConstValue for ConstValue {
465    #[inline]
466    fn from_const_value(value: ConstValue) -> Result<Self, RuntimeError> {
467        Ok(value)
468    }
469}
470
471/// Implementation of a constant constructor.
472///
473/// Do not implement manually, this is provided when deriving
474/// [`ToConstValue`](derive@ToConstValue).
475pub trait ConstConstruct: 'static + Send + Sync {
476    /// Construct from values.
477    #[doc(hidden)]
478    fn const_construct(&self, fields: &[ConstValue]) -> Result<Value, RuntimeError>;
479
480    /// Construct from values.
481    #[doc(hidden)]
482    fn runtime_construct(&self, fields: &mut [Value]) -> Result<Value, RuntimeError>;
483}
484
485pub(crate) trait ConstContext {
486    fn get(&self, hash: Hash) -> Option<&dyn ConstConstruct>;
487}
488
489pub(crate) struct EmptyConstContext;
490
491impl ConstContext for EmptyConstContext {
492    #[inline]
493    fn get(&self, _: Hash) -> Option<&dyn ConstConstruct> {
494        None
495    }
496}