diff --git a/crates/rune/src/runtime.rs b/crates/rune/src/runtime.rs index 37b65950b..598d6c21f 100644 --- a/crates/rune/src/runtime.rs +++ b/crates/rune/src/runtime.rs @@ -5,7 +5,7 @@ mod tests; mod access; pub(crate) use self::access::{Access, AccessErrorKind}; -pub use self::access::{AccessError, BorrowMut, BorrowRef, RawAccessGuard}; +pub use self::access::{AccessError, BorrowMut, BorrowRef, RawAccessGuard, Snapshot}; mod any_obj; pub use self::any_obj::{AnyObj, AnyObjError, AnyObjVtable}; diff --git a/crates/rune/src/runtime/access.rs b/crates/rune/src/runtime/access.rs index 12893d52d..43557e8c0 100644 --- a/crates/rune/src/runtime/access.rs +++ b/crates/rune/src/runtime/access.rs @@ -13,10 +13,6 @@ use crate::runtime::{AnyObjError, RawStr}; /// Sentinel value to indicate that access is taken. const MOVED: isize = isize::MAX; -/// Panic if we reach this number of shared accesses and we try to add one more, -/// since it's the largest we can support. -const MAX_USES: isize = isize::MIN; - /// An error raised while downcasting. #[derive(Debug, PartialEq)] #[allow(missing_docs)] @@ -157,9 +153,9 @@ cfg_std! { /// Snapshot that can be used to indicate how the value was being accessed at /// the time of an error. -#[derive(Debug, PartialEq)] +#[derive(PartialEq)] #[repr(transparent)] -struct Snapshot(isize); +pub struct Snapshot(isize); impl fmt::Display for Snapshot { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -175,6 +171,13 @@ impl fmt::Display for Snapshot { } } +impl fmt::Debug for Snapshot { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Snapshot({})", self) + } +} + /// Access flags. /// /// These accomplish the following things: @@ -212,20 +215,20 @@ impl Access { /// Test if we can have shared access without modifying the internal count. #[inline] pub(crate) fn is_shared(&self) -> bool { - self.get() <= 0 + self.0.get() <= 0 } /// Test if we can have exclusive access without modifying the internal /// count. #[inline] pub(crate) fn is_exclusive(&self) -> bool { - self.get() == 0 + self.0.get() == 0 } /// Test if the data has been taken. #[inline] pub(crate) fn is_taken(&self) -> bool { - self.get() == MOVED + self.0.get() == MOVED } /// Mark that we want shared access to the given access token. @@ -235,19 +238,17 @@ impl Access { /// The returned guard must not outlive the access token that created it. #[inline] pub(crate) unsafe fn shared(&self) -> Result, NotAccessibleRef> { - let state = self.get(); + let state = self.0.get(); - if state == MAX_USES { + if state == isize::MIN { crate::alloc::abort(); } - let n = state.wrapping_sub(1); - - if n >= 0 { - return Err(NotAccessibleRef(Snapshot(self.0.get()))); + if state > 0 { + return Err(NotAccessibleRef(Snapshot(state))); } - self.set(n); + self.0.set(state - 1); Ok(AccessGuard(self)) } @@ -258,13 +259,13 @@ impl Access { /// The returned guard must not outlive the access token that created it. #[inline] pub(crate) unsafe fn exclusive(&self) -> Result, NotAccessibleMut> { - let n = self.get(); + let state = self.0.get(); - if n != 0 { - return Err(NotAccessibleMut(Snapshot(self.0.get()))); + if state != 0 { + return Err(NotAccessibleMut(Snapshot(state))); } - self.set(n.wrapping_add(1)); + self.0.set(state + 1); Ok(AccessGuard(self)) } @@ -277,56 +278,52 @@ impl Access { /// The returned guard must not outlive the access token that created it. #[inline] pub(crate) unsafe fn take(&self) -> Result { - let state = self.get(); + let state = self.0.get(); if state != 0 { - return Err(NotAccessibleTake(Snapshot(self.0.get()))); + return Err(NotAccessibleTake(Snapshot(state))); } - self.set(MOVED); + self.0.set(MOVED); Ok(RawTakeGuard { access: self }) } /// Release the current access level. #[inline] fn release(&self) { - let b = self.get(); + let b = self.0.get(); let b = if b < 0 { - debug_assert!(b < 0); - b.wrapping_add(1) + b + 1 } else { - debug_assert_eq!(b, 1, "borrow value should be exclusive (0)"); - b.wrapping_sub(1) + debug_assert_eq!(b, 1, "Borrow value should be exclusive (0)"); + b - 1 }; - self.set(b); + self.0.set(b); } /// Untake the current access. #[inline] fn release_take(&self) { - let b = self.get(); - debug_assert_eq!(b, MOVED, "borrow value should be TAKEN ({})", MOVED); - self.set(0); - } - - /// Get the current value of the flag. - #[inline] - fn get(&self) -> isize { - self.0.get() + debug_assert_eq!( + self.0.get(), + MOVED, + "Borrow value should be TAKEN ({})", + MOVED + ); + self.0.set(0); } - /// Set the current value of the flag. - #[inline] - fn set(&self, value: isize) { - self.0.set(value); + /// Get a snapshot of current access. + pub(super) fn snapshot(&self) -> Snapshot { + Snapshot(self.0.get()) } } impl fmt::Debug for Access { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", Snapshot(self.get())) + write!(f, "{}", Snapshot(self.0.get())) } } diff --git a/crates/rune/src/runtime/shared.rs b/crates/rune/src/runtime/shared.rs index d39d49b82..cc19f12ff 100644 --- a/crates/rune/src/runtime/shared.rs +++ b/crates/rune/src/runtime/shared.rs @@ -14,7 +14,7 @@ use ::rust_alloc::sync::Arc; use crate::alloc::prelude::*; use crate::alloc::{self, Box}; -use crate::runtime::{Access, AccessError, BorrowMut, BorrowRef, RawAccessGuard}; +use crate::runtime::{Access, AccessError, BorrowMut, BorrowRef, RawAccessGuard, Snapshot}; /// A shared value. pub(crate) struct Shared { @@ -51,6 +51,11 @@ impl Shared { unsafe { self.inner.as_ref().access.is_exclusive() } } + /// Get access snapshot of shared value. + pub(crate) fn snapshot(&self) -> Snapshot { + unsafe { self.inner.as_ref().access.snapshot() } + } + /// Take the interior value, if we have exlusive access to it and there /// are no other live exlusive or shared references. /// diff --git a/crates/rune/src/runtime/value.rs b/crates/rune/src/runtime/value.rs index 413b7a408..667ae0267 100644 --- a/crates/rune/src/runtime/value.rs +++ b/crates/rune/src/runtime/value.rs @@ -19,8 +19,8 @@ use crate::runtime::{ ControlFlow, EnvProtocolCaller, Format, Formatter, FromValue, FullTypeOf, Function, Future, Generator, GeneratorState, Iterator, MaybeTypeOf, Mut, Object, OwnedTuple, Protocol, ProtocolCaller, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, Ref, - RuntimeError, Shared, SharedPointerGuard, Stream, ToValue, Type, TypeInfo, Variant, Vec, Vm, - VmErrorKind, VmIntegerRepr, VmResult, + RuntimeError, Shared, SharedPointerGuard, Snapshot, Stream, ToValue, Type, TypeInfo, Variant, + Vec, Vm, VmErrorKind, VmIntegerRepr, VmResult, }; #[cfg(feature = "alloc")] use crate::runtime::{Hasher, Tuple}; @@ -561,6 +561,14 @@ impl Value { self.inner.is_readable() } + /// Get snapshot of value. + /// + /// The snapshot details how the value is currently being access. + #[allow(unused)] + pub fn snapshot(&self) -> Snapshot { + self.inner.snapshot() + } + /// Construct an empty value. pub(crate) fn empty() -> alloc::Result { Ok(Self { @@ -1431,107 +1439,109 @@ impl Value { /// /// This is the basis for the eq operation (`partial_eq` / '=='). pub(crate) fn partial_eq_with( - a: &Value, + &self, b: &Value, caller: &mut impl ProtocolCaller, ) -> VmResult { - match ( - &*vm_try!(a.borrow_kind_ref()), - &*vm_try!(b.borrow_kind_ref()), - ) { - (ValueKind::EmptyTuple, ValueKind::EmptyTuple) => return VmResult::Ok(true), - (ValueKind::Bool(a), ValueKind::Bool(b)) => return VmResult::Ok(*a == *b), - (ValueKind::Byte(a), ValueKind::Byte(b)) => return VmResult::Ok(*a == *b), - (ValueKind::Char(a), ValueKind::Char(b)) => return VmResult::Ok(*a == *b), - (ValueKind::Integer(a), ValueKind::Integer(b)) => return VmResult::Ok(*a == *b), - (ValueKind::Float(a), ValueKind::Float(b)) => return VmResult::Ok(*a == *b), - (ValueKind::Type(a), ValueKind::Type(b)) => return VmResult::Ok(*a == *b), - (ValueKind::Bytes(a), ValueKind::Bytes(b)) => { - return VmResult::Ok(*a == *b); - } - (ValueKind::Vec(a), _) => { - return Vec::partial_eq_with(a, b.clone(), caller); - } - (ValueKind::Tuple(a), _) => { - return Vec::partial_eq_with(a, b.clone(), caller); - } - (ValueKind::Object(a), _) => { - return Object::partial_eq_with(a, b.clone(), caller); - } - (ValueKind::RangeFrom(a), ValueKind::RangeFrom(b)) => { - return RangeFrom::partial_eq_with(a, b, caller); - } - (ValueKind::RangeFull(a), ValueKind::RangeFull(b)) => { - return RangeFull::partial_eq_with(a, b, caller); - } - (ValueKind::RangeInclusive(a), ValueKind::RangeInclusive(b)) => { - return RangeInclusive::partial_eq_with(a, b, caller); - } - (ValueKind::RangeToInclusive(a), ValueKind::RangeToInclusive(b)) => { - return RangeToInclusive::partial_eq_with(a, b, caller); - } - (ValueKind::RangeTo(a), ValueKind::RangeTo(b)) => { - return RangeTo::partial_eq_with(a, b, caller); - } - (ValueKind::Range(a), ValueKind::Range(b)) => { - return Range::partial_eq_with(a, b, caller); - } - (ValueKind::ControlFlow(a), ValueKind::ControlFlow(b)) => { - return ControlFlow::partial_eq_with(a, b, caller); - } - (ValueKind::EmptyStruct(a), ValueKind::EmptyStruct(b)) => { - if a.rtti.hash == b.rtti.hash { - // NB: don't get any future ideas, this must fall through to - // the VmError below since it's otherwise a comparison - // between two incompatible types. - // - // Other than that, all units are equal. - return VmResult::Ok(true); + { + let a = vm_try!(self.borrow_kind_ref()); + + match (&*a, &*vm_try!(b.borrow_kind_ref())) { + (ValueKind::EmptyTuple, ValueKind::EmptyTuple) => return VmResult::Ok(true), + (ValueKind::Bool(a), ValueKind::Bool(b)) => return VmResult::Ok(*a == *b), + (ValueKind::Byte(a), ValueKind::Byte(b)) => return VmResult::Ok(*a == *b), + (ValueKind::Char(a), ValueKind::Char(b)) => return VmResult::Ok(*a == *b), + (ValueKind::Integer(a), ValueKind::Integer(b)) => return VmResult::Ok(*a == *b), + (ValueKind::Float(a), ValueKind::Float(b)) => return VmResult::Ok(*a == *b), + (ValueKind::Type(a), ValueKind::Type(b)) => return VmResult::Ok(*a == *b), + (ValueKind::Bytes(a), ValueKind::Bytes(b)) => { + return VmResult::Ok(*a == *b); } - } - (ValueKind::TupleStruct(a), ValueKind::TupleStruct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Vec::eq_with(&a.data, &b.data, Value::partial_eq_with, caller); + (ValueKind::RangeFrom(a), ValueKind::RangeFrom(b)) => { + return RangeFrom::partial_eq_with(a, b, caller); } - } - (ValueKind::Struct(a), ValueKind::Struct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Object::eq_with(&a.data, &b.data, Value::partial_eq_with, caller); + (ValueKind::RangeFull(a), ValueKind::RangeFull(b)) => { + return RangeFull::partial_eq_with(a, b, caller); } - } - (ValueKind::Variant(a), ValueKind::Variant(b)) => { - if a.rtti().enum_hash == b.rtti().enum_hash { - return Variant::partial_eq_with(a, b, caller); + (ValueKind::RangeInclusive(a), ValueKind::RangeInclusive(b)) => { + return RangeInclusive::partial_eq_with(a, b, caller); } + (ValueKind::RangeToInclusive(a), ValueKind::RangeToInclusive(b)) => { + return RangeToInclusive::partial_eq_with(a, b, caller); + } + (ValueKind::RangeTo(a), ValueKind::RangeTo(b)) => { + return RangeTo::partial_eq_with(a, b, caller); + } + (ValueKind::Range(a), ValueKind::Range(b)) => { + return Range::partial_eq_with(a, b, caller); + } + (ValueKind::ControlFlow(a), ValueKind::ControlFlow(b)) => { + return ControlFlow::partial_eq_with(a, b, caller); + } + (ValueKind::EmptyStruct(a), ValueKind::EmptyStruct(b)) => { + if a.rtti.hash == b.rtti.hash { + // NB: don't get any future ideas, this must fall through to + // the VmError below since it's otherwise a comparison + // between two incompatible types. + // + // Other than that, all units are equal. + return VmResult::Ok(true); + } + } + (ValueKind::TupleStruct(a), ValueKind::TupleStruct(b)) => { + if a.rtti.hash == b.rtti.hash { + return Vec::eq_with(&a.data, &b.data, Value::partial_eq_with, caller); + } + } + (ValueKind::Struct(a), ValueKind::Struct(b)) => { + if a.rtti.hash == b.rtti.hash { + return Object::eq_with(&a.data, &b.data, Value::partial_eq_with, caller); + } + } + (ValueKind::Variant(a), ValueKind::Variant(b)) => { + if a.rtti().enum_hash == b.rtti().enum_hash { + return Variant::partial_eq_with(a, b, caller); + } + } + (ValueKind::String(a), ValueKind::String(b)) => { + return VmResult::Ok(*a == *b); + } + (ValueKind::Option(a), ValueKind::Option(b)) => match (a, b) { + (Some(a), Some(b)) => return Value::partial_eq_with(a, b, caller), + (None, None) => return VmResult::Ok(true), + _ => return VmResult::Ok(false), + }, + (ValueKind::Result(a), ValueKind::Result(b)) => match (a, b) { + (Ok(a), Ok(b)) => return Value::partial_eq_with(a, b, caller), + (Err(a), Err(b)) => return Value::partial_eq_with(a, b, caller), + _ => return VmResult::Ok(false), + }, + _ => {} } - (ValueKind::String(a), ValueKind::String(b)) => { - return VmResult::Ok(*a == *b); - } - (ValueKind::Option(a), ValueKind::Option(b)) => match (a, b) { - (Some(a), Some(b)) => return Value::partial_eq_with(a, b, caller), - (None, None) => return VmResult::Ok(true), - _ => return VmResult::Ok(false), - }, - (ValueKind::Result(a), ValueKind::Result(b)) => match (a, b) { - (Ok(a), Ok(b)) => return Value::partial_eq_with(a, b, caller), - (Err(a), Err(b)) => return Value::partial_eq_with(a, b, caller), - _ => return VmResult::Ok(false), - }, - _ => { - match vm_try!(caller.try_call_protocol_fn( - Protocol::PARTIAL_EQ, - a.clone(), - (b.clone(),) - )) { - CallResult::Ok(value) => return bool::from_value(value), - CallResult::Unsupported(..) => {} + + match &*a { + ValueKind::Vec(a) => { + return Vec::partial_eq_with(a, b.clone(), caller); + } + ValueKind::Tuple(a) => { + return Vec::partial_eq_with(a, b.clone(), caller); } + ValueKind::Object(a) => { + return Object::partial_eq_with(a, b.clone(), caller); + } + _ => {} } } + if let CallResult::Ok(value) = + vm_try!(caller.try_call_protocol_fn(Protocol::PARTIAL_EQ, self.clone(), (b.clone(),))) + { + return <_>::from_value(value); + } + err(VmErrorKind::UnsupportedBinaryOperation { op: "partial_eq", - lhs: vm_try!(a.type_info()), + lhs: vm_try!(self.type_info()), rhs: vm_try!(b.type_info()), }) } @@ -1583,13 +1593,13 @@ impl Value { ValueKind::Vec(vec) => { return Vec::hash_with(vec, hasher, caller); } - _ => { - match vm_try!(caller.try_call_protocol_fn(Protocol::HASH, self.clone(), (hasher,))) - { - CallResult::Ok(value) => return <()>::from_value(value), - CallResult::Unsupported(..) => {} - } - } + _ => {} + } + + if let CallResult::Ok(value) = + vm_try!(caller.try_call_protocol_fn(Protocol::HASH, self.clone(), (hasher,))) + { + return <_>::from_value(value); } err(VmErrorKind::UnsupportedUnaryOperation { @@ -1704,13 +1714,13 @@ impl Value { (Err(a), Err(b)) => return Value::eq_with(a, b, caller), _ => return VmResult::Ok(false), }, - _ => { - match vm_try!(caller.try_call_protocol_fn(Protocol::EQ, self.clone(), (b.clone(),))) - { - CallResult::Ok(value) => return bool::from_value(value), - CallResult::Unsupported(..) => {} - } - } + _ => {} + } + + if let CallResult::Ok(value) = + vm_try!(caller.try_call_protocol_fn(Protocol::EQ, self.clone(), (b.clone(),))) + { + return <_>::from_value(value); } err(VmErrorKind::UnsupportedBinaryOperation { @@ -1738,12 +1748,12 @@ impl Value { /// /// This is the basis for the comparison operation. pub(crate) fn partial_cmp_with( - a: &Value, + &self, b: &Value, caller: &mut impl ProtocolCaller, ) -> VmResult> { match ( - &*vm_try!(a.borrow_kind_ref()), + &*vm_try!(self.borrow_kind_ref()), &*vm_try!(b.borrow_kind_ref()), ) { (ValueKind::EmptyTuple, ValueKind::EmptyTuple) => { @@ -1827,21 +1837,18 @@ impl Value { (Ok(..), Err(..)) => return VmResult::Ok(Some(Ordering::Greater)), (Err(..), Ok(..)) => return VmResult::Ok(Some(Ordering::Less)), }, - _ => { - match vm_try!(caller.try_call_protocol_fn( - Protocol::PARTIAL_CMP, - a.clone(), - (b.clone(),) - )) { - CallResult::Ok(value) => return >::from_value(value), - CallResult::Unsupported(..) => {} - } - } + _ => {} + } + + if let CallResult::Ok(value) = + vm_try!(caller.try_call_protocol_fn(Protocol::PARTIAL_CMP, self.clone(), (b.clone(),))) + { + return <_>::from_value(value); } err(VmErrorKind::UnsupportedBinaryOperation { op: "partial_cmp", - lhs: vm_try!(a.type_info()), + lhs: vm_try!(self.type_info()), rhs: vm_try!(b.type_info()), }) } @@ -1864,12 +1871,12 @@ impl Value { /// /// This is the basis for the comparison operation (`cmp`). pub(crate) fn cmp_with( - a: &Value, + &self, b: &Value, caller: &mut impl ProtocolCaller, ) -> VmResult { match ( - &*vm_try!(a.borrow_kind_ref()), + &*vm_try!(self.borrow_kind_ref()), &*vm_try!(b.borrow_kind_ref()), ) { (ValueKind::EmptyTuple, ValueKind::EmptyTuple) => return VmResult::Ok(Ordering::Equal), @@ -1955,17 +1962,18 @@ impl Value { (Ok(..), Err(..)) => return VmResult::Ok(Ordering::Greater), (Err(..), Ok(..)) => return VmResult::Ok(Ordering::Less), }, - _ => { - match vm_try!(caller.try_call_protocol_fn(Protocol::CMP, a.clone(), (b.clone(),))) { - CallResult::Ok(value) => return Ordering::from_value(value), - CallResult::Unsupported(..) => {} - } - } + _ => {} + } + + if let CallResult::Ok(value) = + vm_try!(caller.try_call_protocol_fn(Protocol::CMP, self.clone(), (b.clone(),))) + { + return <_>::from_value(value); } err(VmErrorKind::UnsupportedBinaryOperation { op: "cmp", - lhs: vm_try!(a.type_info()), + lhs: vm_try!(self.type_info()), rhs: vm_try!(b.type_info()), }) }