1#[macro_use]
2mod macros;
3
4mod node;
5
6pub(crate) use self::node::{ConstBuilder, ConstNodeKind, ConstNodesError};
7pub use self::node::{ConstFields, ConstFieldsIter, ConstValue, ConstValueBuf};
8
9use core::any;
10use core::cmp::Ordering;
11
12use crate::alloc;
13use crate::alloc::prelude::*;
14use crate::runtime;
15use crate::{declare_dyn_trait, hash_in, Hash, TypeHash};
16
17use super::{
18 Bytes, ExpectedType, FromValue, Inline, Object, OwnedTuple, Repr, RuntimeError, ToValue, Tuple,
19 Type, Value, VmErrorKind, VmIntegerRepr,
20};
21
22pub(crate) const MAX_CONST_DEPTH: usize = 128;
34
35pub(crate) const MAX_CONST_SIZE: usize = 1 << 16;
47pub use rune_macros::ToConstValue;
119
120impl From<ConstNodesError> for RuntimeError {
123 fn from(error: ConstNodesError) -> Self {
124 match error {
125 #[cfg(any(feature = "serde", feature = "musli"))]
126 ConstNodesError::Empty | ConstNodesError::Malformed => {
127 RuntimeError::new(VmErrorKind::MalformedConstValue)
128 }
129 ConstNodesError::TooDeep { max } => {
130 RuntimeError::new(VmErrorKind::MaxConstDepth { max })
131 }
132 ConstNodesError::TooLarge { max } => {
133 RuntimeError::new(VmErrorKind::MaxConstSize { max })
134 }
135 ConstNodesError::Alloc(error) => RuntimeError::from(error),
136 }
137 }
138}
139
140pub fn from_const_value<T>(value: impl AsRef<ConstValue>) -> Result<T, RuntimeError>
153where
154 T: FromConstValue,
155{
156 T::from_const_value(value.as_ref())
157}
158
159pub fn to_const_value(value: impl ToConstValue) -> Result<ConstValueBuf, RuntimeError> {
172 value.to_const_value()
173}
174
175pub trait ToConstValue: Sized {
177 fn to_const_value(self) -> Result<ConstValueBuf, RuntimeError>;
179
180 #[inline]
182 #[doc(hidden)]
183 fn construct() -> alloc::Result<Option<ConstConstructImpl>> {
184 Ok(None)
185 }
186}
187
188impl ToConstValue for ConstValueBuf {
189 #[inline]
190 fn to_const_value(self) -> Result<ConstValueBuf, RuntimeError> {
191 Ok(self)
192 }
193}
194
195impl ToConstValue for &ConstValue {
196 #[inline]
197 fn to_const_value(self) -> Result<ConstValueBuf, RuntimeError> {
198 Ok(self.try_to_owned()?)
199 }
200}
201
202impl ToConstValue for Value {
203 #[inline]
204 fn to_const_value(self) -> Result<ConstValueBuf, RuntimeError> {
205 ConstValueBuf::from_value_ref(&self)
206 }
207}
208
209impl ConstValueBuf {
210 pub fn string(value: impl AsRef<str>) -> Result<ConstValueBuf, RuntimeError> {
212 let value = alloc::Box::try_from(value.as_ref())?;
213 Ok(Self::from_kind(ConstNodeKind::String(value)))
214 }
215
216 pub fn bytes(value: impl AsRef<[u8]>) -> Result<ConstValueBuf, RuntimeError> {
218 let value = alloc::Box::try_from(value.as_ref())?;
219 Ok(Self::from_kind(ConstNodeKind::Bytes(value)))
220 }
221
222 pub fn tuple<I>(fields: I) -> Result<ConstValueBuf, RuntimeError>
224 where
225 I: IntoIterator,
226 I::Item: AsRef<ConstValue>,
227 I::IntoIter: ExactSizeIterator,
228 {
229 Self::instance(OwnedTuple::HASH, Hash::EMPTY, fields)
230 }
231
232 pub fn for_struct<const N: usize>(
234 hash: Hash,
235 fields: [ConstValueBuf; N],
236 ) -> Result<ConstValueBuf, RuntimeError> {
237 Self::instance(hash, Hash::EMPTY, fields)
238 }
239
240 pub(crate) fn instance<I>(
243 hash: Hash,
244 variant_hash: Hash,
245 fields: I,
246 ) -> Result<ConstValueBuf, RuntimeError>
247 where
248 I: IntoIterator,
249 I::Item: AsRef<ConstValue>,
250 I::IntoIter: ExactSizeIterator,
251 {
252 let fields = fields.into_iter();
253
254 let mut builder = ConstBuilder::new();
255
256 let at = builder.open(ConstNodeKind::Instance {
257 hash,
258 variant_hash,
259 fields: fields.len() as u32,
260 })?;
261
262 for field in fields {
263 builder.extend(field.as_ref())?;
264 }
265
266 builder.close(at);
267 Ok(builder.build()?)
268 }
269
270 pub(crate) fn from_value_ref(value: &Value) -> Result<ConstValueBuf, RuntimeError> {
275 let mut builder = ConstBuilder::new();
276 from_value_ref_at(value, 0, &mut builder)?;
277 Ok(builder.build()?)
278 }
279}
280
281fn from_value_ref_at(
284 value: &Value,
285 depth: usize,
286 builder: &mut ConstBuilder,
287) -> Result<(), RuntimeError> {
288 if depth >= MAX_CONST_DEPTH {
289 return Err(RuntimeError::new(VmErrorKind::MaxConstDepth {
290 max: MAX_CONST_DEPTH,
291 }));
292 }
293
294 if builder.len() >= MAX_CONST_SIZE {
297 return Err(RuntimeError::new(VmErrorKind::MaxConstSize {
298 max: MAX_CONST_SIZE,
299 }));
300 }
301
302 let depth = depth + 1;
303
304 match value.as_ref() {
305 Repr::Inline(value) => {
306 builder.leaf(ConstNodeKind::Inline(*value))?;
307 }
308 Repr::Dynamic(value) => {
309 return Err(RuntimeError::from(VmErrorKind::ConstNotSupported {
310 actual: value.type_info(),
311 }));
312 }
313 Repr::Any(value) => match value.type_hash() {
314 alloc::String::HASH => {
315 let string = value.borrow_ref::<alloc::String>()?;
316 builder.leaf(ConstNodeKind::String(alloc::Box::try_from(
317 string.as_str(),
318 )?))?;
319 }
320 Bytes::HASH => {
321 let bytes = value.borrow_ref::<Bytes>()?;
322 builder.leaf(ConstNodeKind::Bytes(alloc::Box::try_from(
323 bytes.as_slice(),
324 )?))?;
325 }
326 OwnedTuple::HASH => {
327 let tuple = value.borrow_ref::<OwnedTuple>()?;
328
329 let at = builder.open(ConstNodeKind::Instance {
330 hash: OwnedTuple::HASH,
331 variant_hash: Hash::EMPTY,
332 fields: tuple.len() as u32,
333 })?;
334
335 for value in tuple.iter() {
336 from_value_ref_at(value, depth, builder)?;
337 }
338
339 builder.close(at);
340 }
341 Object::HASH => {
342 let object = value.borrow_ref::<Object>()?;
343
344 let mut keys = alloc::Vec::try_with_capacity(object.len())?;
345
346 for key in object.keys() {
347 keys.try_push(alloc::Box::try_from(key.as_str())?)?;
348 }
349
350 keys.sort();
353
354 let at = builder.open(ConstNodeKind::Object {
355 keys: keys.try_clone()?.try_into_boxed_slice()?,
356 })?;
357
358 for key in keys.iter() {
359 let Some(value) = object.get(key.as_ref()) else {
360 return Err(RuntimeError::new(VmErrorKind::MalformedConstValue));
361 };
362
363 from_value_ref_at(value, depth, builder)?;
364 }
365
366 builder.close(at);
367 }
368 Option::<Value>::HASH => {
369 let option = value.borrow_ref::<Option<Value>>()?;
370
371 match &*option {
372 Some(some) => {
373 let at = builder.open(ConstNodeKind::Instance {
374 hash: Option::<Value>::HASH,
375 variant_hash: hash_in!(crate, ::std::option::Option::Some),
376 fields: 1,
377 })?;
378
379 from_value_ref_at(some, depth, builder)?;
380 builder.close(at);
381 }
382 None => {
383 builder.leaf(ConstNodeKind::Instance {
384 hash: Option::<Value>::HASH,
385 variant_hash: hash_in!(crate, ::std::option::Option::None),
386 fields: 0,
387 })?;
388 }
389 }
390 }
391 runtime::Vec::HASH => {
392 let vec = value.borrow_ref::<runtime::Vec>()?;
393
394 let at = builder.open(ConstNodeKind::Instance {
395 hash: runtime::Vec::HASH,
396 variant_hash: Hash::EMPTY,
397 fields: vec.len() as u32,
398 })?;
399
400 for value in vec.iter() {
401 from_value_ref_at(value, depth, builder)?;
402 }
403
404 builder.close(at);
405 }
406 _ => {
407 return Err(RuntimeError::from(VmErrorKind::ConstNotSupported {
408 actual: value.type_info(),
409 }));
410 }
411 },
412 }
413
414 Ok(())
415}
416
417impl ConstValue {
418 pub fn as_integer<T>(&self) -> Result<T, RuntimeError>
431 where
432 T: TryFrom<i64> + TryFrom<u64>,
433 {
434 match self.kind() {
435 ConstNodeKind::Inline(Inline::Signed(value)) => match (*value).try_into() {
436 Ok(number) => Ok(number),
437 Err(..) => Err(RuntimeError::new(
438 VmErrorKind::ValueToIntegerCoercionError {
439 from: VmIntegerRepr::from(*value),
440 to: any::type_name::<T>(),
441 },
442 )),
443 },
444 ConstNodeKind::Inline(Inline::Unsigned(value)) => match (*value).try_into() {
445 Ok(number) => Ok(number),
446 Err(..) => Err(RuntimeError::new(
447 VmErrorKind::ValueToIntegerCoercionError {
448 from: VmIntegerRepr::from(*value),
449 to: any::type_name::<T>(),
450 },
451 )),
452 },
453 kind => Err(RuntimeError::new(VmErrorKind::ExpectedNumber {
454 actual: kind.type_info(),
455 })),
456 }
457 }
458
459 inline_macros!(inline_into);
460
461 pub fn as_string(&self) -> Result<&str, ExpectedType> {
463 let ConstNodeKind::String(value) = self.kind() else {
464 return Err(ExpectedType::new::<alloc::String>(self.type_info()));
465 };
466
467 Ok(value)
468 }
469
470 pub fn as_tuple(&self) -> Result<ConstFields<'_>, ExpectedType> {
472 let ConstNodeKind::Instance {
473 hash: OwnedTuple::HASH,
474 variant_hash: Hash::EMPTY,
475 ..
476 } = self.kind()
477 else {
478 return Err(ExpectedType::new::<Tuple>(self.type_info()));
479 };
480
481 Ok(self.fields())
482 }
483
484 pub(crate) fn to_value_with(&self, cx: &dyn ConstContext) -> Result<Value, RuntimeError> {
494 struct Frame<'a> {
496 kind: FrameKind<'a>,
497 remaining: usize,
498 values: alloc::Vec<Value>,
499 }
500
501 enum FrameKind<'a> {
502 Tuple,
503 Vec,
504 Object(&'a [alloc::Box<str>]),
505 Some,
506 }
507
508 fn close(frame: Frame<'_>) -> Result<Value, RuntimeError> {
509 match frame.kind {
510 FrameKind::Tuple => Ok(Value::try_from(OwnedTuple::try_from(frame.values)?)?),
511 FrameKind::Vec => Ok(Value::try_from(runtime::Vec::from(frame.values))?),
512 FrameKind::Object(keys) => {
513 let mut object = Object::with_capacity(keys.len())?;
514
515 for (key, value) in keys.iter().zip(frame.values) {
516 object.insert(alloc::String::try_from(key.as_ref())?, value)?;
517 }
518
519 Ok(Value::try_from(object)?)
520 }
521 FrameKind::Some => {
522 let mut values = frame.values.into_iter();
523
524 let Some(value) = values.next() else {
525 return Err(RuntimeError::new(VmErrorKind::MalformedConstValue));
526 };
527
528 Ok(Value::try_from(Some(value))?)
529 }
530 }
531 }
532
533 fn open<'a>(kind: FrameKind<'a>, len: usize) -> Result<Frame<'a>, RuntimeError> {
534 Ok(Frame {
535 kind,
536 remaining: len,
537 values: alloc::Vec::try_with_capacity(len)?,
538 })
539 }
540
541 let nodes = self.as_nodes();
542 let mut frames = alloc::Vec::<Frame<'_>>::new();
543 let mut index = 0;
544
545 loop {
546 let Some(node) = nodes.get(index) else {
547 return Err(RuntimeError::new(VmErrorKind::MalformedConstValue));
548 };
549
550 let mut produced = None;
553
554 match &node.kind {
555 ConstNodeKind::Inline(value) => {
556 produced = Some(Value::from(*value));
557 index += 1;
558 }
559 ConstNodeKind::String(string) => {
560 produced = Some(Value::try_from(string.as_ref())?);
561 index += 1;
562 }
563 ConstNodeKind::Bytes(bytes) => {
564 produced = Some(Value::try_from(bytes.as_ref())?);
565 index += 1;
566 }
567 ConstNodeKind::Object { keys } => {
568 frames.try_push(open(FrameKind::Object(keys), keys.len())?)?;
569 index += 1;
570 }
571 ConstNodeKind::Instance {
572 hash,
573 variant_hash,
574 fields,
575 } => {
576 let fields = *fields as usize;
577
578 match (*hash, *variant_hash) {
579 (OwnedTuple::HASH, Hash::EMPTY) => {
580 frames.try_push(open(FrameKind::Tuple, fields)?)?;
581 index += 1;
582 }
583 (runtime::Vec::HASH, Hash::EMPTY) => {
584 frames.try_push(open(FrameKind::Vec, fields)?)?;
585 index += 1;
586 }
587 (Option::<Value>::HASH, variant_hash) => {
588 match (variant_hash, fields) {
589 (hash_in!(crate, ::std::option::Option::Some), 1) => {
590 frames.try_push(open(FrameKind::Some, 1)?)?;
591 }
592 (hash_in!(crate, ::std::option::Option::None), 0) => {
593 produced = Some(Value::try_from(None)?);
594 }
595 _ => {
596 return Err(RuntimeError::missing_constant_constructor(*hash));
597 }
598 }
599
600 index += 1;
601 }
602 (hash, _) => {
603 let Some(constructor) = cx.get(hash) else {
608 return Err(RuntimeError::missing_constant_constructor(hash));
609 };
610
611 let size = node.size as usize;
612
613 let Some(subtree) = nodes.get(index..index + size) else {
614 return Err(RuntimeError::new(VmErrorKind::MalformedConstValue));
615 };
616
617 produced =
618 Some(constructor.const_construct(ConstValue::from_nodes(subtree))?);
619
620 index += size;
621 }
622 }
623 }
624 }
625
626 loop {
629 if let Some(value) = produced.take() {
630 let Some(frame) = frames.last_mut() else {
631 return Ok(value);
632 };
633
634 frame.values.try_push(value)?;
635 frame.remaining -= 1;
636 }
637
638 if !frames.last().is_some_and(|frame| frame.remaining == 0) {
639 break;
640 }
641
642 let Some(frame) = frames.pop() else {
643 break;
644 };
645
646 produced = Some(close(frame)?);
647 }
648 }
649 }
650}
651
652impl FromValue for ConstValueBuf {
653 #[inline]
654 fn from_value(value: Value) -> Result<Self, RuntimeError> {
655 ConstValueBuf::from_value_ref(&value)
656 }
657}
658
659impl ToValue for ConstValueBuf {
660 #[inline]
661 fn to_value(self) -> Result<Value, RuntimeError> {
662 ConstValue::to_value_with(&self, &EmptyConstContext)
663 }
664}
665
666impl ConstValue {
667 #[inline]
668 #[cfg(test)]
669 pub(crate) fn to_value(&self) -> Result<Value, RuntimeError> {
670 self.to_value_with(&EmptyConstContext)
671 }
672}
673
674impl AsRef<ConstValue> for ConstValue {
675 #[inline]
676 fn as_ref(&self) -> &ConstValue {
677 self
678 }
679}
680
681impl From<Inline> for ConstValueBuf {
682 #[inline]
683 fn from(value: Inline) -> Self {
684 ConstValueBuf::from_kind(ConstNodeKind::Inline(value))
685 }
686}
687
688impl TryFrom<alloc::String> for ConstValueBuf {
689 type Error = alloc::Error;
690
691 #[inline]
692 fn try_from(value: alloc::String) -> Result<Self, Self::Error> {
693 Ok(Self::from_kind(ConstNodeKind::String(
694 alloc::Box::try_from(value)?,
695 )))
696 }
697}
698
699impl TryFrom<alloc::Box<str>> for ConstValueBuf {
700 type Error = alloc::Error;
701
702 #[inline]
703 fn try_from(value: alloc::Box<str>) -> Result<Self, Self::Error> {
704 Ok(Self::from_kind(ConstNodeKind::String(value)))
705 }
706}
707
708impl TryFrom<Bytes> for ConstValueBuf {
709 type Error = alloc::Error;
710
711 #[inline]
712 fn try_from(value: Bytes) -> Result<Self, Self::Error> {
713 Self::try_from(value.as_slice())
714 }
715}
716
717impl TryFrom<&str> for ConstValueBuf {
718 type Error = alloc::Error;
719
720 #[inline]
721 fn try_from(value: &str) -> Result<Self, Self::Error> {
722 Ok(Self::from_kind(ConstNodeKind::String(
723 alloc::Box::try_from(value)?,
724 )))
725 }
726}
727
728impl ToConstValue for &str {
729 #[inline]
730 fn to_const_value(self) -> Result<ConstValueBuf, RuntimeError> {
731 Ok(ConstValueBuf::try_from(self)?)
732 }
733}
734
735impl TryFrom<alloc::Box<[u8]>> for ConstValueBuf {
736 type Error = alloc::Error;
737
738 #[inline]
739 fn try_from(value: alloc::Box<[u8]>) -> Result<Self, Self::Error> {
740 Ok(Self::from_kind(ConstNodeKind::Bytes(value)))
741 }
742}
743
744impl TryFrom<&[u8]> for ConstValueBuf {
745 type Error = alloc::Error;
746
747 #[inline]
748 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
749 Ok(Self::from_kind(ConstNodeKind::Bytes(alloc::Box::try_from(
750 value,
751 )?)))
752 }
753}
754
755impl ToConstValue for &[u8] {
756 #[inline]
757 fn to_const_value(self) -> Result<ConstValueBuf, RuntimeError> {
758 Ok(ConstValueBuf::try_from(self)?)
759 }
760}
761
762pub trait FromConstValue: Sized {
764 fn from_const_value(value: &ConstValue) -> Result<Self, RuntimeError>;
766}
767
768impl FromConstValue for ConstValueBuf {
769 #[inline]
770 fn from_const_value(value: &ConstValue) -> Result<Self, RuntimeError> {
771 Ok(value.try_to_owned()?)
772 }
773}
774
775impl FromConstValue for bool {
776 #[inline]
777 fn from_const_value(value: &ConstValue) -> Result<Self, RuntimeError> {
778 value.as_bool()
779 }
780}
781
782impl FromConstValue for char {
783 #[inline]
784 fn from_const_value(value: &ConstValue) -> Result<Self, RuntimeError> {
785 value.as_char()
786 }
787}
788
789macro_rules! impl_integer {
790 ($($ty:ty),* $(,)?) => {
791 $(
792 impl FromConstValue for $ty {
793 #[inline]
794 fn from_const_value(value: &ConstValue) -> Result<Self, RuntimeError> {
795 value.as_integer()
796 }
797 }
798 )*
799 };
800}
801
802impl_integer!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
803
804declare_dyn_trait! {
805 struct ConstConstructVtable;
807
808 pub struct ConstConstructImpl;
810
811 pub trait ConstConstruct {
817 #[doc(hidden)]
820 fn const_construct(&self, value: &ConstValue) -> Result<Value, RuntimeError>;
821
822 #[doc(hidden)]
824 fn runtime_construct(&self, fields: &mut [Value]) -> Result<Value, RuntimeError>;
825 }
826}
827
828pub(crate) trait ConstContext {
829 fn get(&self, hash: Hash) -> Option<&ConstConstructImpl>;
830}
831
832pub(crate) struct EmptyConstContext;
833
834impl ConstContext for EmptyConstContext {
835 #[inline]
836 fn get(&self, _: Hash) -> Option<&ConstConstructImpl> {
837 None
838 }
839}