diff --git a/crates/rune/src/any.rs b/crates/rune/src/any.rs index a742f6fd3..b0f359c58 100644 --- a/crates/rune/src/any.rs +++ b/crates/rune/src/any.rs @@ -1,5 +1,6 @@ use core::any; +use crate::alloc::String; use crate::compile::Named; use crate::runtime::{AnyTypeInfo, TypeHash}; @@ -124,3 +125,5 @@ cfg_std! { } crate::__internal_impl_any!(::std::error, anyhow::Error); + +impl Any for String {} diff --git a/crates/rune/src/compile/error.rs b/crates/rune/src/compile/error.rs index 802d7d963..3e8f3293b 100644 --- a/crates/rune/src/compile/error.rs +++ b/crates/rune/src/compile/error.rs @@ -20,7 +20,7 @@ use crate::macros::{SyntheticId, SyntheticKind}; use crate::parse::{Expectation, IntoExpectation, LexerMode}; use crate::runtime::debug::DebugSignature; use crate::runtime::unit::EncodeError; -use crate::runtime::{AccessError, RuntimeError, TypeInfo, TypeOf, VmError}; +use crate::runtime::{AccessError, AnyObjError, RuntimeError, TypeInfo, TypeOf, VmError}; #[cfg(feature = "std")] use crate::source; use crate::{Hash, Item, ItemBuf, SourceId}; @@ -1307,6 +1307,13 @@ impl From for ErrorKind { } } +impl From for ErrorKind { + #[inline] + fn from(error: AnyObjError) -> Self { + Self::from(RuntimeError::from(error)) + } +} + impl From for ErrorKind { #[inline] fn from(error: EncodeError) -> Self { diff --git a/crates/rune/src/compile/ir/eval.rs b/crates/rune/src/compile/ir/eval.rs index 6099c1a69..25e5d0075 100644 --- a/crates/rune/src/compile/ir/eval.rs +++ b/crates/rune/src/compile/ir/eval.rs @@ -7,7 +7,8 @@ use crate::ast::{Span, Spanned}; use crate::compile::ir::{self}; use crate::compile::{self, WithSpan}; use crate::query::Used; -use crate::runtime::{Inline, Mutable, Object, OwnedTuple, Value, ValueBorrowRef}; +use crate::runtime::{Inline, Object, OwnedTuple, Value, ValueBorrowRef}; +use crate::TypeHash; /// The outcome of a constant evaluation. pub enum EvalOutcome { @@ -139,18 +140,25 @@ fn eval_ir_binary( return Ok(Value::from(out)); } - (ValueBorrowRef::Mutable(a), ValueBorrowRef::Mutable(b)) => { - let out = 'out: { - if let (Mutable::String(a), Mutable::String(b)) = (&*a, &*b) { - if let ir::IrBinaryOp::Add = ir.op { - break 'out Mutable::String(add_strings(a, b).with_span(span)?); + (ValueBorrowRef::Any(a), ValueBorrowRef::Any(b)) => { + let value = 'out: { + match (a.type_hash(), b.type_hash()) { + (String::HASH, String::HASH) => { + let a = a.borrow_ref::().with_span(span)?; + let b = b.borrow_ref::().with_span(span)?; + + if let ir::IrBinaryOp::Add = ir.op { + let string = add_strings(&*a, &*b).with_span(span)?; + break 'out Value::new(string).with_span(span)?; + } } + _ => {} } return Err(EvalOutcome::not_const(span)); }; - return Ok(Value::try_from(out).with_span(span)?); + return Ok(value); } _ => (), } @@ -366,15 +374,16 @@ fn eval_ir_template( return Err(EvalOutcome::not_const(ir)); } }, - ValueBorrowRef::Mutable(value) => match &*value { - Mutable::String(s) => { - buf.try_push_str(s)?; + ValueBorrowRef::Any(value) => match value.type_hash() { + String::HASH => { + let s = value.borrow_ref::().with_span(ir)?; + buf.try_push_str(&s)?; } _ => { return Err(EvalOutcome::not_const(ir)); } }, - ValueBorrowRef::Any(..) => { + ValueBorrowRef::Mutable(..) => { return Err(EvalOutcome::not_const(ir)); } } diff --git a/crates/rune/src/macros/format_args.rs b/crates/rune/src/macros/format_args.rs index cd9e3befd..f0de49298 100644 --- a/crates/rune/src/macros/format_args.rs +++ b/crates/rune/src/macros/format_args.rs @@ -7,8 +7,7 @@ use crate::ast::{self, Span, Spanned}; use crate::compile::{self, WithSpan}; use crate::macros::{quote, MacroContext, Quote, ToTokens, TokenStream}; use crate::parse::{Parse, Parser, Peek, Peeker}; -use crate::runtime::format; -use crate::runtime::{Inline, Mutable, OwnedValue}; +use crate::runtime::{format, Inline}; /// A format specification: A format string followed by arguments to be /// formatted in accordance with that string. @@ -49,14 +48,7 @@ impl FormatArgs { } } - let format = format.take_value().with_span(&self.format)?; - - let OwnedValue::Mutable(Mutable::String(format)) = format else { - return Err(compile::Error::msg( - &self.format, - "format argument must be a string", - )); - }; + let format = format.into_any::().with_span(&self.format)?; let mut unused_pos = (0..pos.len()).try_collect::>()?; let mut unused_named = named diff --git a/crates/rune/src/modules/iter.rs b/crates/rune/src/modules/iter.rs index 675e25621..a1a8b14ac 100644 --- a/crates/rune/src/modules/iter.rs +++ b/crates/rune/src/modules/iter.rs @@ -8,8 +8,8 @@ use crate::modules::collections::VecDeque; use crate::modules::collections::{HashMap, HashSet}; use crate::runtime::range::RangeIter; use crate::runtime::{ - FromValue, Function, Inline, InstAddress, Mutable, Object, Output, OwnedTuple, Protocol, - TypeHash, Value, ValueBorrowRef, Vec, VmErrorKind, VmResult, + FromValue, Function, Inline, InstAddress, Object, Output, OwnedTuple, Protocol, TypeHash, + Value, ValueBorrowRef, Vec, VmErrorKind, VmResult, }; use crate::shared::Caller; use crate::{Any, ContextError, Module, Params}; @@ -489,17 +489,21 @@ pub fn module() -> Result { ValueBorrowRef::Inline(Inline::Char(c)) => { vm_try!(string.try_push(*c)); } - ValueBorrowRef::Mutable(value) => match &*value { - Mutable::String(s) => { - vm_try!(string.try_push_str(s)); + ValueBorrowRef::Inline(value) => { + return VmResult::expected::(value.type_info()); + } + ValueBorrowRef::Mutable(value) => { + return VmResult::expected::(value.type_info()); + } + ValueBorrowRef::Any(value) => match value.type_hash() { + String::HASH => { + let s = vm_try!(value.borrow_ref::()); + vm_try!(string.try_push_str(&*s)); } - actual => { - return VmResult::expected::(actual.type_info()); + _ => { + return VmResult::expected::(value.type_info()); } }, - actual => { - return VmResult::expected::(actual.type_info()); - } } } diff --git a/crates/rune/src/modules/string.rs b/crates/rune/src/modules/string.rs index 446813a0a..0cc228b61 100644 --- a/crates/rune/src/modules/string.rs +++ b/crates/rune/src/modules/string.rs @@ -762,12 +762,6 @@ fn split(this: Ref, value: Value) -> VmResult { vm_try!(rune::to_value(Split::new(this, *c))) } ValueBorrowRef::Mutable(value) => match &*value { - Mutable::String(ref s) => { - vm_try!(rune::to_value(Split::new( - this, - vm_try!(Box::try_from(s.as_str())) - ))) - } Mutable::Function(ref f) => { vm_try!(rune::to_value(Split::new(this, vm_try!(f.try_clone())))) } @@ -778,11 +772,27 @@ fn split(this: Ref, value: Value) -> VmResult { ]) } }, - actual => { + ValueBorrowRef::Any(value) => match value.type_hash() { + String::HASH => { + let s = vm_try!(value.borrow_ref::()); + + vm_try!(rune::to_value(Split::new( + this, + vm_try!(Box::try_from(s.as_str())) + ))) + } + _ => { + return VmResult::err([ + VmErrorKind::expected::(value.type_info()), + VmErrorKind::bad_argument(0), + ]); + } + }, + value => { return VmResult::err([ - VmErrorKind::expected::(actual.type_info()), + VmErrorKind::expected::(value.type_info()), VmErrorKind::bad_argument(0), - ]) + ]); } }; @@ -805,7 +815,6 @@ fn split_once(this: &str, value: Value) -> VmResult> { let outcome = match vm_try!(value.borrow_ref()) { ValueBorrowRef::Inline(Inline::Char(pat)) => this.split_once(*pat), ValueBorrowRef::Mutable(value) => match &*value { - Mutable::String(s) => this.split_once(s.as_str()), Mutable::Function(f) => { let mut err = None; @@ -833,6 +842,18 @@ fn split_once(this: &str, value: Value) -> VmResult> { ]) } }, + ValueBorrowRef::Any(value) => match value.type_hash() { + String::HASH => { + let s = vm_try!(value.borrow_ref::()); + this.split_once(s.as_str()) + } + _ => { + return VmResult::err([ + VmErrorKind::expected::(value.type_info()), + VmErrorKind::bad_argument(0), + ]); + } + }, ref actual => { return VmResult::err([ VmErrorKind::expected::(actual.type_info()), diff --git a/crates/rune/src/runtime/access.rs b/crates/rune/src/runtime/access.rs index bf9f14645..f9293649a 100644 --- a/crates/rune/src/runtime/access.rs +++ b/crates/rune/src/runtime/access.rs @@ -12,11 +12,17 @@ const MOVED: usize = 1usize.rotate_right(1); /// Mask indicating if the value is exclusively set or moved. const MASK: usize = EXCLUSIVE | MOVED; -/// An error raised while downcasting. -#[derive(Debug, PartialEq)] -#[allow(missing_docs)] +/// An error raised when failing to access a value. +/// +/// Access errors can be raised for various reasons, such as: +/// * The value you are trying to access is an empty placeholder. +/// * The value is already being accessed in an incompatible way, such as trying +/// to access a value exclusively twice. +/// * The value has been taken and is no longer present. +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] #[non_exhaustive] -pub(crate) struct AccessError { +pub struct AccessError { kind: AccessErrorKind, } @@ -64,7 +70,8 @@ impl From for AccessError { } } -#[derive(Debug, PartialEq)] +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] pub(crate) enum AccessErrorKind { Empty, NotAccessibleRef(Snapshot), diff --git a/crates/rune/src/runtime/any_obj.rs b/crates/rune/src/runtime/any_obj.rs index 69825a7fa..7e70288a3 100644 --- a/crates/rune/src/runtime/any_obj.rs +++ b/crates/rune/src/runtime/any_obj.rs @@ -13,6 +13,13 @@ use super::{ RefVtable, Snapshot, TypeInfo, VmErrorKind, }; +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] +pub(super) enum AnyObjErrorKind { + Cast(AnyTypeInfo, TypeInfo), + AccessError(AccessError), +} + /// Errors caused when accessing or coercing an [`AnyObj`]. #[cfg_attr(test, derive(PartialEq))] pub struct AnyObjError { @@ -30,11 +37,17 @@ impl AnyObjError { } } -#[derive(Debug)] -#[cfg_attr(test, derive(PartialEq))] -pub(super) enum AnyObjErrorKind { - Cast(AnyTypeInfo, TypeInfo), - AccessError(AccessError), +impl core::error::Error for AnyObjError {} + +impl fmt::Display for AnyObjError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.kind { + AnyObjErrorKind::Cast(expected, actual) => { + write!(f, "Expected type `{expected}` but found `{actual}`") + } + AnyObjErrorKind::AccessError(error) => error.fmt(f), + } + } } impl fmt::Debug for AnyObjError { @@ -370,6 +383,31 @@ impl AnyObj { } } + /// Try to borrow a reference to the interior value while checking for + /// shared access. + /// + /// Returns `None` if the interior type is not `T`. + /// + /// This prevents other exclusive accesses from being performed while the + /// guard returned from this function is live. + pub fn try_borrow_ref(&self) -> Result>, AccessError> + where + T: Any, + { + let vtable = vtable(self); + + if (vtable.type_id)() != TypeId::of::() { + return Ok(None); + } + + // SAFETY: We've checked for the appropriate type just above. + unsafe { + let guard = self.shared.as_ref().access.shared()?; + let data = vtable.as_ptr(self.shared); + Ok(Some(BorrowRef::new(data, guard))) + } + } + /// Returns some mutable reference to the boxed value if it is of type `T`. pub fn borrow_mut(&self) -> Result, AnyObjError> where diff --git a/crates/rune/src/runtime/args.rs b/crates/rune/src/runtime/args.rs index 3b1e6e4c7..0b7f35028 100644 --- a/crates/rune/src/runtime/args.rs +++ b/crates/rune/src/runtime/args.rs @@ -3,7 +3,8 @@ use core::fmt; use crate::alloc::Vec; use crate::runtime::{GuardedArgs, Stack, ToValue, Value, VmResult}; -#[derive(Debug, PartialEq)] +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] pub(crate) struct DynArgsUsed; impl fmt::Display for DynArgsUsed { diff --git a/crates/rune/src/runtime/const_value.rs b/crates/rune/src/runtime/const_value.rs index cf72c8b66..b7931e6ca 100644 --- a/crates/rune/src/runtime/const_value.rs +++ b/crates/rune/src/runtime/const_value.rs @@ -6,6 +6,7 @@ use crate::runtime::{ self, Bytes, FromValue, Inline, Mutable, Object, OwnedTuple, ToValue, TypeInfo, Value, ValueBorrowRef, VmErrorKind, VmResult, }; +use crate::TypeHash; /// A constant value. #[derive(Debug, Deserialize, Serialize)] @@ -54,7 +55,6 @@ impl ConstValue { } }, ValueBorrowRef::Mutable(value) => match &*value { - Mutable::String(s) => Self::String(vm_try!(s.try_to_owned())), Mutable::Option(option) => Self::Option(match option { Some(some) => Some(vm_try!(Box::try_new(vm_try!(Self::from_value_ref(some))))), None => None, @@ -95,11 +95,17 @@ impl ConstValue { }) } }, - value => { - return VmResult::err(VmErrorKind::ConstNotSupported { - actual: value.type_info(), - }) - } + ValueBorrowRef::Any(value) => match value.type_hash() { + String::HASH => { + let s = vm_try!(value.borrow_ref::()); + Self::String(vm_try!(s.try_to_owned())) + } + _ => { + return VmResult::err(VmErrorKind::ConstNotSupported { + actual: value.type_info(), + }); + } + }, }) } diff --git a/crates/rune/src/runtime/format.rs b/crates/rune/src/runtime/format.rs index 1b5c5b300..e98bd444a 100644 --- a/crates/rune/src/runtime/format.rs +++ b/crates/rune/src/runtime/format.rs @@ -13,10 +13,8 @@ use crate as rune; use crate::alloc::clone::TryClone; use crate::alloc::fmt::TryWrite; use crate::alloc::String; -use crate::runtime::{ - Formatter, Inline, Mutable, ProtocolCaller, Value, ValueRef, VmErrorKind, VmResult, -}; -use crate::Any; +use crate::runtime::{Formatter, Inline, ProtocolCaller, Value, ValueRef, VmErrorKind, VmResult}; +use crate::{Any, TypeHash}; /// Error raised when trying to parse a type string and it fails. #[derive(Debug, Clone, Copy)] @@ -231,18 +229,19 @@ impl FormatSpec { break 'fallback; } }, - ValueRef::Mutable(value) => match &*vm_try!(value.borrow_ref()) { - Mutable::String(s) => { - vm_try!(f.buf_mut().try_push_str(s)); + ValueRef::Mutable(..) => { + break 'fallback; + } + ValueRef::Any(value) => match value.type_hash() { + String::HASH => { + let s = vm_try!(value.borrow_ref::()); + vm_try!(f.buf_mut().try_push_str(&s)); vm_try!(self.format_fill(f, self.align, self.fill, None)); } _ => { break 'fallback; } }, - ValueRef::Any(..) => { - break 'fallback; - } } return VmResult::Ok(()); @@ -274,17 +273,18 @@ impl FormatSpec { break 'fallback; } }, - ValueRef::Mutable(value) => match &*vm_try!(value.borrow_ref()) { - Mutable::String(s) => { + ValueRef::Mutable(..) => { + break 'fallback; + } + ValueRef::Any(value) => match value.type_hash() { + String::HASH => { + let s = vm_try!(value.borrow_ref::()); vm_write!(f, "{s:?}"); } _ => { break 'fallback; } }, - ValueRef::Any(..) => { - break 'fallback; - } } return VmResult::Ok(()); diff --git a/crates/rune/src/runtime/from_value.rs b/crates/rune/src/runtime/from_value.rs index 82ac6d8ef..b371beca9 100644 --- a/crates/rune/src/runtime/from_value.rs +++ b/crates/rune/src/runtime/from_value.rs @@ -1,7 +1,7 @@ use core::cmp::Ordering; -use crate::alloc; -use crate::runtime::{AnyObj, Mut, Mutable, RawAnyGuard, Ref, Value, ValueRepr, VmError, VmResult}; +use crate::alloc::{self, String}; +use crate::runtime::{AnyObj, Mut, RawAnyGuard, Ref, Value, VmError, VmResult}; use crate::Any; /// Cheap conversion trait to convert something infallibly into a dynamic [`Value`]. @@ -255,17 +255,9 @@ where from_value_ref!(Option, into_option_ref, into_option_mut, into_option); -impl FromValue for alloc::String { - fn from_value(value: Value) -> VmResult { - let string = vm_try!(value.borrow_string_ref()); - let string = string.as_ref(); - VmResult::Ok(vm_try!(alloc::String::try_from(string))) - } -} - impl FromValue for ::rust_alloc::string::String { fn from_value(value: Value) -> VmResult { - let string = vm_try!(alloc::String::from_value(value)); + let string = vm_try!(String::from_value(value)); let string = ::rust_alloc::string::String::from(string); VmResult::Ok(string) } @@ -288,75 +280,12 @@ impl FromValue for ::rust_alloc::boxed::Box { } } -impl FromValue for Mut { - fn from_value(value: Value) -> VmResult { - let value = match vm_try!(value.into_repr()) { - ValueRepr::Inline(value) => { - return VmResult::expected::(value.type_info()); - } - ValueRepr::Mutable(value) => vm_try!(value.into_mut()), - ValueRepr::Any(value) => { - return VmResult::expected::(value.type_info()); - } - }; - - let result = Mut::try_map(value, |kind| match kind { - Mutable::String(string) => Some(string), - _ => None, - }); - - match result { - Ok(string) => VmResult::Ok(string), - Err(actual) => VmResult::expected::(actual.type_info()), - } - } -} - -impl FromValue for Ref { - fn from_value(value: Value) -> VmResult { - let value = match vm_try!(value.into_repr()) { - ValueRepr::Inline(value) => { - return VmResult::expected::(value.type_info()); - } - ValueRepr::Mutable(value) => vm_try!(value.into_ref()), - ValueRepr::Any(value) => { - return VmResult::expected::(value.type_info()); - } - }; - - let result = Ref::try_map(value, |value| match value { - Mutable::String(string) => Some(string), - _ => None, - }); - - match result { - Ok(string) => VmResult::Ok(string), - Err(actual) => VmResult::expected::(actual.type_info()), - } - } -} - impl FromValue for Ref { fn from_value(value: Value) -> VmResult { - let value = match vm_try!(value.into_repr()) { - ValueRepr::Inline(value) => { - return VmResult::expected::(value.type_info()); - } - ValueRepr::Mutable(value) => vm_try!(value.into_ref()), - ValueRepr::Any(value) => { - return VmResult::expected::(value.type_info()); - } - }; - - let result = Ref::try_map(value, |kind| match kind { - Mutable::String(string) => Some(string.as_str()), - _ => None, - }); - - match result { - Ok(string) => VmResult::Ok(string), - Err(actual) => VmResult::expected::(actual.type_info()), - } + VmResult::Ok(Ref::map( + vm_try!(Ref::::from_value(value)), + String::as_str, + )) } } @@ -364,28 +293,9 @@ impl UnsafeToRef for str { type Guard = RawAnyGuard; unsafe fn unsafe_to_ref<'a>(value: Value) -> VmResult<(&'a Self, Self::Guard)> { - let value = match vm_try!(value.into_repr()) { - ValueRepr::Inline(value) => { - return VmResult::expected::(value.type_info()); - } - ValueRepr::Mutable(value) => vm_try!(value.into_ref()), - ValueRepr::Any(value) => { - return VmResult::expected::(value.type_info()); - } - }; - - let result = Ref::try_map(value, |value| match value { - Mutable::String(string) => Some(string.as_str()), - _ => None, - }); - - match result { - Ok(string) => { - let (string, guard) = Ref::into_raw(string); - VmResult::Ok((string.as_ref(), guard)) - } - Err(actual) => VmResult::expected::(actual.type_info()), - } + let string = vm_try!(value.into_any_ref::()); + let (string, guard) = Ref::into_raw(string); + VmResult::Ok((string.as_ref().as_str(), guard)) } } @@ -393,86 +303,29 @@ impl UnsafeToMut for str { type Guard = RawAnyGuard; unsafe fn unsafe_to_mut<'a>(value: Value) -> VmResult<(&'a mut Self, Self::Guard)> { - let value = match vm_try!(value.into_repr()) { - ValueRepr::Inline(value) => { - return VmResult::expected::(value.type_info()); - } - ValueRepr::Mutable(value) => vm_try!(value.into_mut()), - ValueRepr::Any(value) => { - return VmResult::expected::(value.type_info()); - } - }; - - let result = Mut::try_map(value, |kind| match kind { - Mutable::String(string) => Some(string.as_mut_str()), - _ => None, - }); - - match result { - Ok(string) => { - let (mut string, guard) = Mut::into_raw(string); - VmResult::Ok((string.as_mut(), guard)) - } - Err(actual) => VmResult::expected::(actual.type_info()), - } + let string = vm_try!(value.into_any_mut::()); + let (mut string, guard) = Mut::into_raw(string); + VmResult::Ok((string.as_mut().as_mut_str(), guard)) } } -impl UnsafeToRef for alloc::String { +impl UnsafeToRef for String { type Guard = RawAnyGuard; unsafe fn unsafe_to_ref<'a>(value: Value) -> VmResult<(&'a Self, Self::Guard)> { - let value = match vm_try!(value.into_repr()) { - ValueRepr::Inline(value) => { - return VmResult::expected::(value.type_info()); - } - ValueRepr::Mutable(value) => vm_try!(value.into_ref()), - ValueRepr::Any(value) => { - return VmResult::expected::(value.type_info()); - } - }; - - let result = Ref::try_map(value, |value| match value { - Mutable::String(string) => Some(string), - _ => None, - }); - - match result { - Ok(string) => { - let (string, guard) = Ref::into_raw(string); - VmResult::Ok((string.as_ref(), guard)) - } - Err(actual) => VmResult::expected::(actual.type_info()), - } + let string = vm_try!(value.into_any_ref::()); + let (string, guard) = Ref::into_raw(string); + VmResult::Ok((string.as_ref(), guard)) } } -impl UnsafeToMut for alloc::String { +impl UnsafeToMut for String { type Guard = RawAnyGuard; unsafe fn unsafe_to_mut<'a>(value: Value) -> VmResult<(&'a mut Self, Self::Guard)> { - let value = match vm_try!(value.into_repr()) { - ValueRepr::Inline(value) => { - return VmResult::expected::(value.type_info()); - } - ValueRepr::Mutable(value) => vm_try!(value.into_mut()), - ValueRepr::Any(value) => { - return VmResult::expected::(value.type_info()); - } - }; - - let result = Mut::try_map(value, |value| match value { - Mutable::String(string) => Some(string), - _ => None, - }); - - match result { - Ok(string) => { - let (mut string, guard) = Mut::into_raw(string); - VmResult::Ok((string.as_mut(), guard)) - } - Err(actual) => VmResult::expected::(actual.type_info()), - } + let string = vm_try!(value.into_any_mut::()); + let (mut string, guard) = Mut::into_raw(string); + VmResult::Ok((string.as_mut(), guard)) } } @@ -580,7 +433,7 @@ cfg_std! { }; } - impl_map!(::std::collections::HashMap, alloc::String); + impl_map!(::std::collections::HashMap, String); impl_map!(::std::collections::HashMap<::rust_alloc::string::String, T>, ::rust_alloc::string::String); } @@ -607,7 +460,7 @@ macro_rules! impl_try_map { }; } -impl_try_map!(alloc::HashMap, alloc::String); +impl_try_map!(alloc::HashMap, String); #[cfg(feature = "alloc")] impl_try_map!(alloc::HashMap<::rust_alloc::string::String, T>, ::rust_alloc::string::String); diff --git a/crates/rune/src/runtime/mod.rs b/crates/rune/src/runtime/mod.rs index 2058565dd..bcecfdb52 100644 --- a/crates/rune/src/runtime/mod.rs +++ b/crates/rune/src/runtime/mod.rs @@ -10,9 +10,8 @@ mod steps_between; use self::steps_between::StepsBetween; mod access; -pub(crate) use self::access::{ - Access, AccessError, AccessErrorKind, AccessGuard, RawAccessGuard, Snapshot, -}; +pub use self::access::AccessError; +pub(crate) use self::access::{Access, AccessErrorKind, AccessGuard, RawAccessGuard, Snapshot}; mod borrow_mut; pub use self::borrow_mut::BorrowMut; diff --git a/crates/rune/src/runtime/panic.rs b/crates/rune/src/runtime/panic.rs index 5a707bb70..2c6a37c91 100644 --- a/crates/rune/src/runtime/panic.rs +++ b/crates/rune/src/runtime/panic.rs @@ -42,6 +42,7 @@ impl From for Panic { } } +#[cfg(test)] impl PartialEq for Panic { #[inline] fn eq(&self, _: &Self) -> bool { diff --git a/crates/rune/src/runtime/stack.rs b/crates/rune/src/runtime/stack.rs index 2f93f94c3..f07ce9b80 100644 --- a/crates/rune/src/runtime/stack.rs +++ b/crates/rune/src/runtime/stack.rs @@ -9,7 +9,8 @@ use crate::alloc::{self, Vec}; use crate::runtime::{InstAddress, Output, Value, VmErrorKind}; /// An error raised when accessing an address on the stack. -#[derive(Debug, PartialEq)] +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] #[non_exhaustive] pub struct StackError { addr: InstAddress, @@ -25,7 +26,8 @@ impl fmt::Display for StackError { impl core::error::Error for StackError {} /// An error raised when accessing a slice on the stack. -#[derive(Debug, PartialEq)] +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] #[non_exhaustive] pub struct SliceError { addr: InstAddress, diff --git a/crates/rune/src/runtime/tuple.rs b/crates/rune/src/runtime/tuple.rs index 50fca3f64..1d83b8390 100644 --- a/crates/rune/src/runtime/tuple.rs +++ b/crates/rune/src/runtime/tuple.rs @@ -6,8 +6,8 @@ use crate as rune; use crate::alloc::clone::TryClone; use crate::alloc::{self, Box}; use crate::runtime::{ - ConstValue, FromValue, Mut, Mutable, OwnedValue, RawAnyGuard, Ref, ToValue, UnsafeToMut, - UnsafeToRef, Value, ValueShared, VmErrorKind, VmResult, + ConstValue, FromValue, Mut, Mutable, RawAnyGuard, Ref, ToValue, UnsafeToMut, UnsafeToRef, + Value, ValueRepr, ValueShared, VmErrorKind, VmResult, }; #[cfg(feature = "alloc")] use crate::runtime::{Hasher, ProtocolCaller}; @@ -280,16 +280,16 @@ impl TryFrom<::rust_alloc::boxed::Box<[ConstValue]>> for OwnedTuple { impl FromValue for OwnedTuple { fn from_value(value: Value) -> VmResult { - match vm_try!(value.take_value()) { - OwnedValue::Inline(value) => match value { + match vm_try!(value.into_repr()) { + ValueRepr::Inline(value) => match value { Inline::Unit => VmResult::Ok(Self::new()), actual => VmResult::expected::(actual.type_info()), }, - OwnedValue::Mutable(value) => match value { + ValueRepr::Mutable(value) => match vm_try!(value.take()) { Mutable::Tuple(tuple) => VmResult::Ok(tuple), actual => VmResult::expected::(actual.type_info()), }, - OwnedValue::Any(value) => VmResult::expected::(value.type_info()), + ValueRepr::Any(value) => VmResult::expected::(value.type_info()), } } } diff --git a/crates/rune/src/runtime/type_info.rs b/crates/rune/src/runtime/type_info.rs index e533215a8..c071cf91e 100644 --- a/crates/rune/src/runtime/type_info.rs +++ b/crates/rune/src/runtime/type_info.rs @@ -101,7 +101,7 @@ impl fmt::Display for TypeInfo { write!(f, "{}", rtti.item)?; } TypeInfoKind::Any(info) => { - write!(f, "{}", info.name)?; + write!(f, "{info}")?; } } @@ -125,3 +125,10 @@ impl AnyTypeInfo { Self { name, hash } } } + +impl fmt::Display for AnyTypeInfo { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.name.fmt(f) + } +} diff --git a/crates/rune/src/runtime/unit/storage.rs b/crates/rune/src/runtime/unit/storage.rs index 01a8f34b8..62b16d5d5 100644 --- a/crates/rune/src/runtime/unit/storage.rs +++ b/crates/rune/src/runtime/unit/storage.rs @@ -205,7 +205,8 @@ enum EncodeErrorKind { /// Error indicating that a bad instruction was located at the given instruction /// pointer. -#[derive(Debug, PartialEq)] +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] pub struct BadInstruction { pub(crate) ip: usize, } @@ -221,7 +222,8 @@ impl core::error::Error for BadInstruction {} /// Error indicating that a bad instruction was located at the given instruction /// pointer. -#[derive(Debug, PartialEq)] +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] pub struct BadJump { pub(crate) jump: usize, } diff --git a/crates/rune/src/runtime/value.rs b/crates/rune/src/runtime/value.rs index 4b341f5a8..8ba4864c2 100644 --- a/crates/rune/src/runtime/value.rs +++ b/crates/rune/src/runtime/value.rs @@ -31,7 +31,7 @@ use crate::runtime::{ }; #[cfg(feature = "alloc")] use crate::runtime::{Hasher, Tuple}; -use crate::{Any, Hash}; +use crate::{Any, Hash, TypeHash}; /// Defined guard for a reference value. /// @@ -380,16 +380,17 @@ impl Value { Mutable::Format(format) => { vm_try!(format.spec.format(&format.value, f, caller)); } - Mutable::String(string) => { - vm_try!(f.push_str(string)); - } _ => { break 'fallback; } }, - ValueBorrowRef::Any(..) => { - break 'fallback; - } + ValueBorrowRef::Any(value) => match value.type_hash() { + String::HASH => { + let string = vm_try!(value.borrow_ref::()); + vm_try!(f.push_str(&*string)); + } + _ => break 'fallback, + }, } return VmResult::Ok(()); @@ -428,7 +429,6 @@ impl Value { }); } ValueBorrowRef::Mutable(value) => match &*value { - Mutable::String(value) => Mutable::String(vm_try!(value.try_clone())), Mutable::Bytes(value) => Mutable::Bytes(vm_try!(value.try_clone())), Mutable::Vec(value) => Mutable::Vec(vm_try!(value.try_clone())), Mutable::Tuple(value) => Mutable::Tuple(vm_try!(value.try_clone())), @@ -503,9 +503,6 @@ impl Value { }; match &*vm_try!(value.borrow_ref()) { - Mutable::String(value) => { - vm_write!(f, "{value:?}"); - } Mutable::Bytes(value) => { vm_write!(f, "{value:?}"); } @@ -725,31 +722,17 @@ impl Value { self.borrow_string_ref() } - /// Borrow the value of a string as a reference. + /// Borrow the interior value as a string reference. pub fn borrow_string_ref(&self) -> Result, RuntimeError> { - let value = match self.borrow_ref()? { - ValueBorrowRef::Mutable(value) => value, - actual => { - return Err(RuntimeError::expected::(actual.type_info())); - } - }; - - let result = BorrowRef::try_map(value, |kind| match kind { - Mutable::String(string) => Some(string.as_str()), - _ => None, - }); - - match result { - Ok(s) => Ok(s), - Err(actual) => Err(RuntimeError::expected::(actual.type_info())), - } + let string = self.borrow_any_ref::()?; + Ok(BorrowRef::map(string, String::as_str)) } /// Take the current value as a string. #[inline] pub fn into_string(self) -> Result { match self.take_value()? { - OwnedValue::Mutable(Mutable::String(string)) => Ok(string), + OwnedValue::Any(value) => Ok(value.downcast()?), actual => Err(RuntimeError::expected::(actual.type_info())), } } @@ -978,9 +961,12 @@ impl Value { /// This consumes the underlying value. #[inline] pub fn into_any_obj(self) -> Result { - match self.take_value()? { - OwnedValue::Any(value) => Ok(value), - ref actual => Err(RuntimeError::expected_any_obj(actual.type_info())), + match self.into_repr()? { + ValueRepr::Inline(value) => Err(RuntimeError::expected_any_obj(value.type_info())), + ValueRepr::Mutable(value) => Err(RuntimeError::expected_any_obj( + value.borrow_ref()?.type_info(), + )), + ValueRepr::Any(value) => Ok(value), } } @@ -1016,12 +1002,13 @@ impl Value { where T: Any, { - match self.into_repr()? { - ValueRepr::Inline(value) => Err(RuntimeError::expected_any::(value.type_info())), - ValueRepr::Mutable(value) => Err(RuntimeError::expected_any::( + match self.repr { + Repr::Inline(value) => Err(RuntimeError::expected_any::(value.type_info())), + Repr::Mutable(value) => Err(RuntimeError::expected_any::( value.borrow_ref()?.type_info(), )), - ValueRepr::Any(value) => Ok(value.into_ref()?), + Repr::Any(value) => Ok(value.into_ref()?), + Repr::Empty => Err(RuntimeError::from(AccessError::empty())), } } @@ -1055,12 +1042,13 @@ impl Value { where T: Any, { - match self.into_repr()? { - ValueRepr::Inline(value) => Err(RuntimeError::expected_any::(value.type_info())), - ValueRepr::Mutable(value) => Err(RuntimeError::expected_any::( + match self.repr { + Repr::Inline(value) => Err(RuntimeError::expected_any::(value.type_info())), + Repr::Mutable(value) => Err(RuntimeError::expected_any::( value.borrow_ref()?.type_info(), )), - ValueRepr::Any(value) => Ok(value.into_mut()?), + Repr::Any(value) => Ok(value.into_mut()?), + Repr::Empty => Err(RuntimeError::from(AccessError::empty())), } } @@ -1124,9 +1112,16 @@ impl Value { where T: Any, { - let value = match self.take_value()? { - OwnedValue::Any(any) => any, - actual => return Err(RuntimeError::expected_any::(actual.type_info())), + let value = match self.into_repr()? { + ValueRepr::Inline(value) => { + return Err(RuntimeError::expected_any::(value.type_info())) + } + ValueRepr::Mutable(value) => { + return Err(RuntimeError::expected_any::( + value.borrow_ref()?.type_info(), + )) + } + ValueRepr::Any(any) => any, }; Ok(value.downcast::()?) @@ -1237,9 +1232,6 @@ impl Value { break 'fallback; } - (Mutable::String(a), Mutable::String(b)) => { - return VmResult::Ok(*a == *b); - } (Mutable::Option(a), Mutable::Option(b)) => match (a, b) { (Some(a), Some(b)) => return Value::partial_eq_with(a, b, caller), (None, None) => return VmResult::Ok(true), @@ -1325,10 +1317,6 @@ impl Value { } }, ValueBorrowRef::Mutable(value) => match &*value { - Mutable::String(string) => { - hasher.write_str(string); - return VmResult::Ok(()); - } Mutable::Bytes(bytes) => { hasher.write(bytes); return VmResult::Ok(()); @@ -1341,7 +1329,14 @@ impl Value { } _ => {} }, - ValueBorrowRef::Any(..) => {} + ValueBorrowRef::Any(value) => match value.type_hash() { + String::HASH => { + let string = vm_try!(value.borrow_ref::()); + hasher.write_str(&*string); + return VmResult::Ok(()); + } + _ => {} + }, } let mut args = DynGuardedArgs::new((hasher,)); @@ -1429,9 +1424,6 @@ impl Value { return Variant::eq_with(a, b, caller); } } - (Mutable::String(a), Mutable::String(b)) => { - return VmResult::Ok(*a == *b); - } (Mutable::Option(a), Mutable::Option(b)) => match (a, b) { (Some(a), Some(b)) => return Value::eq_with(a, b, caller), (None, None) => return VmResult::Ok(true), @@ -1532,9 +1524,6 @@ impl Value { return Variant::partial_cmp_with(a, b, caller); } } - (Mutable::String(a), Mutable::String(b)) => { - return VmResult::Ok(a.partial_cmp(b)); - } (Mutable::Option(a), Mutable::Option(b)) => match (a, b) { (Some(a), Some(b)) => return Value::partial_cmp_with(a, b, caller), (None, None) => return VmResult::Ok(Some(Ordering::Equal)), @@ -1630,9 +1619,6 @@ impl Value { return Variant::cmp_with(a, b, caller); } } - (Mutable::String(a), Mutable::String(b)) => { - return VmResult::Ok(a.cmp(b)); - } (Mutable::Option(a), Mutable::Option(b)) => match (a, b) { (Some(a), Some(b)) => return Value::cmp_with(a, b, caller), (None, None) => return VmResult::Ok(Ordering::Equal), @@ -1771,6 +1757,18 @@ impl Value { } } + pub(crate) fn try_borrow_ref(&self) -> Result>, AccessError> + where + T: Any, + { + match &self.repr { + Repr::Inline(..) => Ok(None), + Repr::Mutable(..) => Ok(None), + Repr::Any(value) => value.try_borrow_ref(), + Repr::Empty => Err(AccessError::empty()), + } + } + pub(crate) fn value_ref(&self) -> Result, AccessError> { match &self.repr { Repr::Inline(value) => Ok(ValueRef::Inline(value)), @@ -1975,7 +1973,6 @@ inline_from! { } from! { - String => String, Bytes => Bytes, ControlFlow => ControlFlow, Function => Function, @@ -1993,6 +1990,10 @@ from! { Stream => Stream, } +any_from! { + String, +} + from_container! { Option => Option, Result => Result, @@ -2263,8 +2264,6 @@ impl Inline { } pub(crate) enum Mutable { - /// A UTF-8 string. - String(String), /// A byte string. Bytes(Bytes), /// A vector containing any values. @@ -2304,7 +2303,6 @@ pub(crate) enum Mutable { impl Mutable { pub(crate) fn type_info(&self) -> TypeInfo { match self { - Mutable::String(..) => TypeInfo::static_type(static_type::STRING), Mutable::Bytes(..) => TypeInfo::static_type(static_type::BYTES), Mutable::Vec(..) => TypeInfo::static_type(static_type::VEC), Mutable::Tuple(..) => TypeInfo::static_type(static_type::TUPLE), @@ -2331,7 +2329,6 @@ impl Mutable { /// *enum*, and not the type hash of the variant itself. pub(crate) fn type_hash(&self) -> Hash { match self { - Mutable::String(..) => static_type::STRING.hash, Mutable::Bytes(..) => static_type::BYTES.hash, Mutable::Vec(..) => static_type::VEC.hash, Mutable::Tuple(..) => static_type::TUPLE.hash, diff --git a/crates/rune/src/runtime/value/macros.rs b/crates/rune/src/runtime/value/macros.rs index af8eaa60e..2fec5b964 100644 --- a/crates/rune/src/runtime/value/macros.rs +++ b/crates/rune/src/runtime/value/macros.rs @@ -277,6 +277,30 @@ macro_rules! from { }; } +macro_rules! any_from { + ($($ty:ty),* $(,)*) => { + $( + impl TryFrom<$ty> for Value { + type Error = alloc::Error; + + #[inline] + fn try_from(value: $ty) -> Result { + Value::new(value) + } + } + + impl IntoOutput for $ty { + type Output = $ty; + + #[inline] + fn into_output(self) -> VmResult { + VmResult::Ok(self) + } + } + )* + }; +} + macro_rules! inline_from { ($($variant:ident => $ty:ty),* $(,)*) => { $( diff --git a/crates/rune/src/runtime/value/serde.rs b/crates/rune/src/runtime/value/serde.rs index bc5892e45..ab32893ad 100644 --- a/crates/rune/src/runtime/value/serde.rs +++ b/crates/rune/src/runtime/value/serde.rs @@ -3,6 +3,7 @@ use core::fmt; use crate::alloc; use crate::alloc::prelude::*; use crate::runtime::{Bytes, Inline, Mutable, Object, ValueBorrowRef, Vec}; +use crate::TypeHash; use serde::de::{self, Deserialize as _, Error as _}; use serde::ser::{self, Error as _, SerializeMap as _, SerializeSeq as _}; @@ -37,7 +38,6 @@ impl ser::Serialize for Value { Inline::Ordering(..) => Err(ser::Error::custom("cannot serialize orderings")), }, ValueBorrowRef::Mutable(value) => match &*value { - Mutable::String(string) => serializer.serialize_str(string), Mutable::Bytes(bytes) => serializer.serialize_bytes(bytes), Mutable::Vec(vec) => { let mut serializer = serializer.serialize_seq(Some(vec.len()))?; @@ -92,9 +92,13 @@ impl ser::Serialize for Value { Err(ser::Error::custom("cannot serialize `start..end` ranges")) } }, - ValueBorrowRef::Any(..) => { - Err(ser::Error::custom("cannot serialize external references")) - } + ValueBorrowRef::Any(value) => match value.type_hash() { + String::HASH => { + let string = value.borrow_ref::().map_err(S::Error::custom)?; + serializer.serialize_str(string.as_str()) + } + _ => Err(ser::Error::custom("cannot serialize external references")), + }, } } } diff --git a/crates/rune/src/runtime/vm.rs b/crates/rune/src/runtime/vm.rs index 16e1a2cc0..1223da72d 100644 --- a/crates/rune/src/runtime/vm.rs +++ b/crates/rune/src/runtime/vm.rs @@ -2479,15 +2479,11 @@ impl Vm { let value = vm_try!(self.stack.at(value)); 'fallback: { - let ValueBorrowRef::Mutable(field) = vm_try!(index.borrow_ref()) else { + let Some(field) = vm_try!(index.try_borrow_ref::()) else { break 'fallback; }; - let Mutable::String(field) = &*field else { - break 'fallback; - }; - - if vm_try!(Self::try_object_slot_index_set(target, field, value)) { + if vm_try!(Self::try_object_slot_index_set(target, &*field, value)) { return VmResult::Ok(()); } }; @@ -2633,24 +2629,21 @@ impl Vm { let index = vm_try!(self.stack.at(index)); let target = vm_try!(self.stack.at(target)); - match vm_try!(index.borrow_ref()) { - ValueBorrowRef::Inline(value) => { - if let Inline::Integer(index) = value { - let Ok(index) = usize::try_from(*index) else { - return err(VmErrorKind::MissingIndexInteger { - target: vm_try!(target.type_info()), - index: VmIntegerRepr::from(*index), - }); - }; + match vm_try!(index.value_ref()) { + ValueRef::Inline(Inline::Integer(index)) => { + let Ok(index) = usize::try_from(*index) else { + return err(VmErrorKind::MissingIndexInteger { + target: vm_try!(target.type_info()), + index: VmIntegerRepr::from(*index), + }); + }; - if let Some(value) = vm_try!(Self::try_tuple_like_index_get(target, index)) - { - break 'store value; - } + if let Some(value) = vm_try!(Self::try_tuple_like_index_get(target, index)) { + break 'store value; } } - ValueBorrowRef::Mutable(value) => { - if let Mutable::String(index) = &*value { + ValueRef::Any(value) => { + if let Some(index) = vm_try!(value.try_borrow_ref::()) { if let Some(value) = vm_try!(Self::try_object_like_index_get(target, index.as_str())) { @@ -2658,7 +2651,7 @@ impl Vm { } } } - ValueBorrowRef::Any(..) => (), + _ => {} } let target = target.clone(); @@ -2966,7 +2959,7 @@ impl Vm { vm_try!(value.string_display_with(&mut f, &mut *self)); } - vm_try!(out.store(&mut self.stack, Mutable::String(f.string))); + vm_try!(out.store(&mut self.stack, f.string)); VmResult::Ok(()) } @@ -3082,11 +3075,7 @@ impl Vm { let v = vm_try!(self.stack.at(addr)); let is_match = 'out: { - let ValueBorrowRef::Mutable(value) = vm_try!(v.borrow_ref()) else { - break 'out false; - }; - - let Mutable::String(actual) = &*value else { + let Some(actual) = vm_try!(v.try_borrow_ref::()) else { break 'out false; }; diff --git a/crates/rune/src/runtime/vm_error.rs b/crates/rune/src/runtime/vm_error.rs index 094b7e2ec..9ee481bfe 100644 --- a/crates/rune/src/runtime/vm_error.rs +++ b/crates/rune/src/runtime/vm_error.rs @@ -451,7 +451,7 @@ impl From for VmErrorKind { } /// An opaque simple runtime error. -#[derive(Debug, PartialEq)] +#[cfg_attr(test, derive(PartialEq))] pub struct RuntimeError { error: VmErrorKind, } @@ -493,6 +493,13 @@ impl RuntimeError { } } +impl fmt::Debug for RuntimeError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.error.fmt(f) + } +} + impl From for RuntimeError { fn from(value: AnyObjError) -> Self { match value.into_kind() { @@ -540,8 +547,8 @@ impl fmt::Display for RuntimeError { impl core::error::Error for RuntimeError {} /// The kind of error encountered. -#[derive(Debug, PartialEq)] -#[non_exhaustive] +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] #[doc(hidden)] pub(crate) enum VmErrorKind { AllocError {