diff --git a/crates/rune-macros/src/from_value.rs b/crates/rune-macros/src/from_value.rs index 6bd5729bd..007d2f109 100644 --- a/crates/rune-macros/src/from_value.rs +++ b/crates/rune-macros/src/from_value.rs @@ -159,9 +159,13 @@ impl Expander<'_> { #variant_data::Tuple(tuple) => match name { #(#unnamed_matches,)* #missing, }, - #variant_data::Struct(object) => match name { - #(#named_matches,)* #missing, - }, + #variant_data::Struct(data) => { + let object = variant.accessor(data); + + match name { + #(#named_matches,)* #missing, + } + } } } }; diff --git a/crates/rune-macros/src/to_value.rs b/crates/rune-macros/src/to_value.rs index a010f8219..04b307322 100644 --- a/crates/rune-macros/src/to_value.rs +++ b/crates/rune-macros/src/to_value.rs @@ -99,10 +99,10 @@ impl Expander<'_> { let ident = self.cx.field_ident(f)?; _ = self.cx.field_attrs(&f.attrs); - let name = &syn::LitStr::new(&ident.to_string(), ident.span()); + let name = syn::LitStr::new(&ident.to_string(), ident.span()); to_values.push(quote! { - object.insert(<#string as #try_from<_>>::try_from(#name)?, #to_value::to_value(self.#ident)?) + object.insert(<#string as #try_from<_>>::try_from(#name)?, #to_value::to_value(self.#ident)?)? }); } diff --git a/crates/rune/src/compile/context.rs b/crates/rune/src/compile/context.rs index 2f5956555..dc794daca 100644 --- a/crates/rune/src/compile/context.rs +++ b/crates/rune/src/compile/context.rs @@ -734,10 +734,10 @@ impl Context { .copied() .enumerate() .map(|(position, name)| { - Ok(( - Box::::try_from(name)?, - meta::FieldMeta { position }, - )) + Ok(meta::FieldMeta { + name: name.try_into()?, + position, + }) }) .try_collect::>()??, }), @@ -765,6 +765,7 @@ impl Context { enum_hash: ty.hash, hash, item: item.try_clone()?, + fields: fields.to_fields()?, })), type_parameters: Hash::EMPTY, })?; @@ -799,10 +800,10 @@ impl Context { .copied() .enumerate() .map(|(position, name)| { - Ok(( - Box::::try_from(name)?, - meta::FieldMeta { position }, - )) + Ok(meta::FieldMeta { + name: name.try_into()?, + position, + }) }) .try_collect::>()??, }) @@ -1116,10 +1117,10 @@ impl Context { .copied() .enumerate() .map(|(position, name)| { - Ok(( - Box::::try_from(name)?, - meta::FieldMeta { position }, - )) + Ok(meta::FieldMeta { + name: name.try_into()?, + position, + }) }) .try_collect::>()??, }), diff --git a/crates/rune/src/compile/ir/interpreter.rs b/crates/rune/src/compile/ir/interpreter.rs index f72b126a1..d5ab55064 100644 --- a/crates/rune/src/compile/ir/interpreter.rs +++ b/crates/rune/src/compile/ir/interpreter.rs @@ -7,9 +7,7 @@ use crate::compile::meta; use crate::compile::{self, IrErrorKind, ItemId, ModId, WithSpan}; use crate::hir; use crate::query::{Query, Used}; -use crate::runtime::{ - self, BorrowRefRepr, ConstValue, Mutable, Object, OwnedTuple, RefRepr, Value, -}; +use crate::runtime::{self, ConstValue, Object, OwnedTuple, RefRepr, Value}; use crate::TypeHash; /// The interpreter that executed [Ir][crate::ir::Ir]. @@ -187,38 +185,18 @@ impl ir::Scopes { ir::IrTargetKind::Name(name) => Ok(self.get_name(name, ir_target)?.clone()), ir::IrTargetKind::Field(ir_target, field) => { let value = self.get_target(ir_target)?; - let value = value.borrow_ref_repr().with_span(ir_target)?; + let object = value.borrow_ref::().with_span(ir_target)?; - let value = match value { - BorrowRefRepr::Mutable(value) => value, - actual => { - return Err(compile::Error::expected_type::( - ir_target, - actual.type_info(), - )); - } + let Some(value) = object.get(field.as_ref()) else { + return Err(compile::Error::new( + ir_target, + IrErrorKind::MissingField { + field: field.try_clone()?, + }, + )); }; - match &*value { - Mutable::Object(object) => { - if let Some(value) = object.get(field.as_ref()) { - return Ok(value.clone()); - } - } - actual => { - return Err(compile::Error::expected_type::( - ir_target, - actual.type_info(), - )) - } - } - - Err(compile::Error::new( - ir_target, - IrErrorKind::MissingField { - field: field.try_clone()?, - }, - )) + Ok(value.clone()) } ir::IrTargetKind::Index(target, index) => { let value = self.get_target(target)?; @@ -284,30 +262,9 @@ impl ir::Scopes { } ir::IrTargetKind::Field(target, field) => { let target = self.get_target(target)?; - - let mut target = match target.as_ref_repr().with_span(ir_target)? { - RefRepr::Mutable(current) => current.borrow_mut().with_span(ir_target)?, - actual => { - return Err(compile::Error::expected_type::( - ir_target, - actual.type_info().with_span(ir_target)?, - )); - } - }; - - match &mut *target { - Mutable::Object(object) => { - let field = field.as_ref().try_to_owned()?; - object.insert(field, value).with_span(ir_target)?; - } - actual => { - return Err(compile::Error::expected_type::( - ir_target, - actual.type_info(), - )); - } - } - + let mut object = target.borrow_mut::().with_span(ir_target)?; + let field = field.as_ref().try_to_owned()?; + object.insert(field, value).with_span(ir_target)?; Ok(()) } ir::IrTargetKind::Index(target, index) => { @@ -374,35 +331,18 @@ impl ir::Scopes { } ir::IrTargetKind::Field(target, field) => { let value = self.get_target(target)?; + let mut object = value.borrow_mut::().with_span(ir_target)?; - let mut value = match value.as_ref_repr().with_span(ir_target)? { - RefRepr::Mutable(value) => value.borrow_mut().with_span(ir_target)?, - actual => { - return Err(compile::Error::expected_type::( - ir_target, - actual.type_info().with_span(ir_target)?, - )) - } + let Some(value) = object.get_mut(field.as_ref()) else { + return Err(compile::Error::new( + ir_target, + IrErrorKind::MissingField { + field: field.try_clone()?, + }, + )); }; - match &mut *value { - Mutable::Object(object) => { - let Some(value) = object.get_mut(field.as_ref()) else { - return Err(compile::Error::new( - ir_target, - IrErrorKind::MissingField { - field: field.try_clone()?, - }, - )); - }; - - op(value) - } - actual => Err(compile::Error::expected_type::( - ir_target, - actual.type_info(), - )), - } + op(value) } ir::IrTargetKind::Index(target, index) => { let current = self.get_target(target)?; diff --git a/crates/rune/src/compile/meta.rs b/crates/rune/src/compile/meta.rs index 29116e1ff..e0b4ea454 100644 --- a/crates/rune/src/compile/meta.rs +++ b/crates/rune/src/compile/meta.rs @@ -295,13 +295,28 @@ pub struct Alias { #[non_exhaustive] pub struct FieldsNamed { /// Fields associated with the type. - pub(crate) fields: HashMap, FieldMeta>, + pub(crate) fields: Box<[FieldMeta]>, +} + +impl FieldsNamed { + /// Coerce into a hashmap of fields. + pub(crate) fn to_fields(&self) -> alloc::Result, usize>> { + let mut fields = HashMap::new(); + + for f in self.fields.iter() { + fields.try_insert(f.name.try_clone()?, f.position)?; + } + + Ok(fields) + } } /// Metadata for a single named field. #[derive(Debug, TryClone)] pub struct FieldMeta { /// Position of the field in its containing type declaration. + pub(crate) name: Box, + /// The position of the field. pub(crate) position: usize, } diff --git a/crates/rune/src/compile/unit_builder.rs b/crates/rune/src/compile/unit_builder.rs index 47f9bcd99..0fd58c64d 100644 --- a/crates/rune/src/compile/unit_builder.rs +++ b/crates/rune/src/compile/unit_builder.rs @@ -318,6 +318,7 @@ impl UnitBuilder { let rtti = Arc::new(Rtti { hash, item: pool.item(meta.item_meta.item).try_to_owned()?, + fields: HashMap::new(), }); self.constants @@ -348,6 +349,7 @@ impl UnitBuilder { let rtti = Arc::new(Rtti { hash: meta.hash, item: pool.item(meta.item_meta.item).try_to_owned()?, + fields: HashMap::new(), }); if self @@ -404,6 +406,7 @@ impl UnitBuilder { let rtti = Arc::new(Rtti { hash: meta.hash, item: pool.item(meta.item_meta.item).try_to_owned()?, + fields: HashMap::new(), }); if self @@ -443,12 +446,16 @@ impl UnitBuilder { .functions .try_insert(meta.hash, signature)?; } - meta::Kind::Struct { .. } => { + meta::Kind::Struct { + fields: meta::Fields::Named(ref named), + .. + } => { let hash = pool.item_type_hash(meta.item_meta.item); let rtti = Arc::new(Rtti { hash, item: pool.item(meta.item_meta.item).try_to_owned()?, + fields: named.to_fields()?, }); self.constants @@ -474,6 +481,7 @@ impl UnitBuilder { enum_hash, hash: meta.hash, item: pool.item(meta.item_meta.item).try_to_owned()?, + fields: HashMap::new(), }); if self @@ -522,6 +530,7 @@ impl UnitBuilder { enum_hash, hash: meta.hash, item: pool.item(meta.item_meta.item).try_to_owned()?, + fields: HashMap::new(), }); if self @@ -566,7 +575,7 @@ impl UnitBuilder { } meta::Kind::Variant { enum_hash, - fields: meta::Fields::Named(..), + fields: meta::Fields::Named(ref named), .. } => { let hash = pool.item_type_hash(meta.item_meta.item); @@ -575,6 +584,7 @@ impl UnitBuilder { enum_hash, hash, item: pool.item(meta.item_meta.item).try_to_owned()?, + fields: named.to_fields()?, }); if self diff --git a/crates/rune/src/hir/lowering.rs b/crates/rune/src/hir/lowering.rs index e5d84e142..d7b9b7396 100644 --- a/crates/rune/src/hir/lowering.rs +++ b/crates/rune/src/hir/lowering.rs @@ -290,11 +290,15 @@ fn expr_object<'hir>( } }); - let mut check_object_fields = |fields: &HashMap<_, meta::FieldMeta>, item: &Item| { - let mut fields = fields.try_clone()?; + let mut check_object_fields = |fields: &[meta::FieldMeta], item: &Item| { + let mut named = HashMap::new(); + + for f in fields.iter() { + named.try_insert(f.name.as_ref(), f)?; + } for assign in assignments.iter_mut() { - match fields.remove(assign.key.1) { + match named.remove(assign.key.1) { Some(field_meta) => { assign.position = Some(field_meta.position); } @@ -310,11 +314,11 @@ fn expr_object<'hir>( }; } - if let Some(field) = fields.into_keys().next() { + if let Some(field) = named.into_keys().next() { return Err(compile::Error::new( span, ErrorKind::LitObjectMissingField { - field, + field: field.try_into()?, item: item.try_to_owned()?, }, )); @@ -335,7 +339,7 @@ fn expr_object<'hir>( fields: meta::Fields::Empty, .. } => { - check_object_fields(&HashMap::new(), item)?; + check_object_fields(&[], item)?; hir::ExprObjectKind::EmptyStruct { hash: meta.hash } } meta::Kind::Struct { @@ -1675,8 +1679,8 @@ fn struct_match_for( meta::Fields::Empty if open => HashSet::new(), meta::Fields::Named(st) => st .fields - .keys() - .try_cloned() + .iter() + .map(|f| f.name.try_clone()) .try_collect::>()??, _ => return Ok(None), }; diff --git a/crates/rune/src/hir/lowering2.rs b/crates/rune/src/hir/lowering2.rs index 9fdb647ed..763779be4 100644 --- a/crates/rune/src/hir/lowering2.rs +++ b/crates/rune/src/hir/lowering2.rs @@ -1185,11 +1185,15 @@ fn expr_object<'hir>( comma.at_most_one(cx)?; p.expect(K!['}'])?; - let mut check_object_fields = |fields: &HashMap<_, meta::FieldMeta>, item: &crate::Item| { - let mut fields = fields.try_clone()?; + let mut check_object_fields = |fields: &[meta::FieldMeta], item: &crate::Item| { + let mut named = HashMap::new(); + + for f in fields { + named.try_insert(f.name.as_ref(), f)?; + } for assign in assignments.iter_mut() { - let Some(meta) = fields.remove(assign.key.1) else { + let Some(meta) = named.remove(assign.key.1) else { return Err(Error::new( assign.key.0, ErrorKind::LitObjectNotField { @@ -1202,11 +1206,11 @@ fn expr_object<'hir>( assign.position = Some(meta.position); } - if let Some(field) = fields.into_keys().next() { + if let Some(field) = named.into_keys().next() { return Err(Error::new( p.span(), ErrorKind::LitObjectMissingField { - field, + field: field.try_into()?, item: item.try_to_owned()?, }, )); @@ -1228,7 +1232,7 @@ fn expr_object<'hir>( fields: meta::Fields::Empty, .. } => { - check_object_fields(&HashMap::new(), item)?; + check_object_fields(&[], item)?; hir::ExprObjectKind::EmptyStruct { hash: meta.hash } } meta::Kind::Struct { @@ -2679,8 +2683,8 @@ fn struct_match_for( meta::Fields::Empty if open => HashSet::new(), meta::Fields::Named(st) => st .fields - .keys() - .try_cloned() + .iter() + .map(|f| f.name.try_clone()) .try_collect::>()??, _ => return Ok(None), }; diff --git a/crates/rune/src/indexing/index.rs b/crates/rune/src/indexing/index.rs index 021fc82d1..1a40e5e9b 100644 --- a/crates/rune/src/indexing/index.rs +++ b/crates/rune/src/indexing/index.rs @@ -3,7 +3,7 @@ use core::mem::take; use tracing::instrument_ast; use crate::alloc::prelude::*; -use crate::alloc::{HashMap, VecDeque}; +use crate::alloc::VecDeque; use crate::ast::{self, OptionSpanned, Spanned}; use crate::compile::{ self, attrs, meta, Doc, DynLocation, ErrorKind, ItemMeta, Location, Visibility, WithSpan, @@ -1336,14 +1336,19 @@ fn convert_fields(cx: ResolveContext<'_>, body: ast::Fields) -> compile::Result< ast::Fields::Empty => meta::Fields::Empty, ast::Fields::Unnamed(tuple) => meta::Fields::Unnamed(tuple.len()), ast::Fields::Named(st) => { - let mut fields = HashMap::try_with_capacity(st.len())?; + let mut fields = Vec::try_with_capacity(st.len())?; for (position, (ast::Field { name, .. }, _)) in st.iter().enumerate() { let name = name.resolve(cx)?; - fields.try_insert(name.try_into()?, meta::FieldMeta { position })?; + fields.try_push(meta::FieldMeta { + name: name.try_into()?, + position, + })?; } - meta::Fields::Named(meta::FieldsNamed { fields }) + meta::Fields::Named(meta::FieldsNamed { + fields: fields.try_into()?, + }) } }) } diff --git a/crates/rune/src/indexing/index2.rs b/crates/rune/src/indexing/index2.rs index 556ee3a89..bf1f0d5e6 100644 --- a/crates/rune/src/indexing/index2.rs +++ b/crates/rune/src/indexing/index2.rs @@ -1,7 +1,7 @@ use core::mem::replace; +use crate::alloc; use crate::alloc::prelude::*; -use crate::alloc::{self, HashMap}; use crate::ast::{self, Kind, Span, Spanned}; use crate::compile::{ meta, Doc, DynLocation, Error, ErrorKind, Location, Result, Visibility, WithSpan, @@ -1166,7 +1166,7 @@ fn fields(idx: &mut Indexer<'_, '_>, p: &mut Stream<'_>) -> Result match p.kind() { StructBody => { p.one(K!['{']).exactly_one(idx)?; - let mut fields = HashMap::new(); + let mut fields = Vec::new(); let mut comma = Remaining::default(); while let MaybeNode::Some(field) = p.eat(Field) { @@ -1174,13 +1174,18 @@ fn fields(idx: &mut Indexer<'_, '_>, p: &mut Stream<'_>) -> Result let name = field.parse(|p| p.ast::())?; let name = name.resolve(resolve_context!(idx.q))?; let position = fields.len(); - fields.try_insert(name.try_into()?, meta::FieldMeta { position })?; + fields.try_push(meta::FieldMeta { + name: name.try_into()?, + position, + })?; comma = p.remaining(idx, K![,])?; } comma.at_most_one(idx)?; p.one(K!['}']).exactly_one(idx)?; - Ok(meta::Fields::Named(meta::FieldsNamed { fields })) + Ok(meta::Fields::Named(meta::FieldsNamed { + fields: fields.try_into()?, + })) } TupleBody => { p.one(K!['(']).exactly_one(idx)?; diff --git a/crates/rune/src/internal_macros.rs b/crates/rune/src/internal_macros.rs index a060adc9c..1f6c50280 100644 --- a/crates/rune/src/internal_macros.rs +++ b/crates/rune/src/internal_macros.rs @@ -163,17 +163,3 @@ macro_rules! from_value_ref { } }; } - -/// Implements a set of common value conversions. -macro_rules! from_value2 { - ($ty:ty, $into_ref:ident, $into_mut:ident, $into:ident) => { - impl $crate::runtime::FromValue for $ty { - fn from_value(value: Value) -> Result { - let value = value.$into()?; - Ok(value) - } - } - - from_value_ref!($ty, $into_ref, $into_mut, $into); - }; -} diff --git a/crates/rune/src/module/module_meta.rs b/crates/rune/src/module/module_meta.rs index 70062b6d5..c06bf5a86 100644 --- a/crates/rune/src/module/module_meta.rs +++ b/crates/rune/src/module/module_meta.rs @@ -5,6 +5,7 @@ use ::rust_alloc::sync::Arc; use crate as rune; use crate::alloc; use crate::alloc::prelude::*; +use crate::alloc::HashMap; use crate::compile::context::{AttributeMacroHandler, MacroHandler, TraitHandler}; use crate::compile::{meta, Docs}; use crate::function_meta::AssociatedName; @@ -89,6 +90,21 @@ pub(crate) enum Fields { Empty, } +impl Fields { + /// Coerce into fields hash map. + pub(crate) fn to_fields(&self) -> alloc::Result, usize>> { + let mut fields = HashMap::new(); + + if let Fields::Named(names) = self { + for (index, name) in names.iter().copied().enumerate() { + fields.try_insert(name.try_into()?, index)?; + } + } + + Ok(fields) + } +} + /// Metadata about a variant. pub struct Variant { /// The name of the variant. diff --git a/crates/rune/src/modules/object.rs b/crates/rune/src/modules/object.rs index 68f676a1b..b06b58cc9 100644 --- a/crates/rune/src/modules/object.rs +++ b/crates/rune/src/modules/object.rs @@ -1,10 +1,10 @@ //! The dynamic [`Object`] container. -use core::cmp::Ordering; - use crate as rune; +use crate::alloc::clone::TryClone; +use crate::alloc::fmt::TryWrite; use crate::runtime::object::{RuneIter, RuneIterKeys, RuneValues}; -use crate::runtime::{EnvProtocolCaller, Object, Protocol, Value, VmResult}; +use crate::runtime::{EnvProtocolCaller, Formatter, Object, Protocol, Value, VmResult}; use crate::{ContextError, Module}; /// The dynamic [`Object`] container. @@ -44,15 +44,10 @@ pub fn module() -> Result { m.function_meta(Object::len__meta)?; m.function_meta(Object::is_empty__meta)?; m.function_meta(Object::rune_insert)?; - m.function_meta(remove)?; + m.function_meta(remove__meta)?; m.function_meta(Object::clear__meta)?; - m.function_meta(contains_key)?; - m.function_meta(get)?; - - m.function_meta(Object::rune_iter__meta)?; - m.function_meta(Object::rune_keys__meta)?; - m.function_meta(Object::rune_values__meta)?; - m.associated_function(Protocol::INTO_ITER, Object::rune_iter)?; + m.function_meta(contains_key__meta)?; + m.function_meta(get__meta)?; m.function_meta(partial_eq__meta)?; m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; @@ -60,11 +55,15 @@ pub fn module() -> Result { m.function_meta(eq__meta)?; m.implement_trait::(rune::item!(::std::cmp::Eq))?; - m.function_meta(partial_cmp__meta)?; - m.implement_trait::(rune::item!(::std::cmp::PartialOrd))?; + m.function_meta(clone__meta)?; + m.implement_trait::(rune::item!(::std::clone::Clone))?; - m.function_meta(cmp__meta)?; - m.implement_trait::(rune::item!(::std::cmp::Ord))?; + m.function_meta(debug_fmt__meta)?; + + m.function_meta(Object::rune_iter__meta)?; + m.function_meta(Object::rune_keys__meta)?; + m.function_meta(Object::rune_values__meta)?; + m.associated_function(Protocol::INTO_ITER, Object::rune_iter)?; m.ty::()?; m.function_meta(RuneIter::next__meta)?; @@ -97,7 +96,7 @@ pub fn module() -> Result { /// let object = #{a: 42}; /// assert!(object.contains_key("a")); /// ``` -#[rune::function(instance)] +#[rune::function(keep, instance)] #[inline] fn contains_key(object: &Object, key: &str) -> bool { object.contains_key(key) @@ -113,7 +112,7 @@ fn contains_key(object: &Object, key: &str) -> bool { /// assert_eq!(object.remove("a"), Some(42)); /// assert_eq!(object.remove("a"), None); /// ``` -#[rune::function(instance)] +#[rune::function(keep, instance)] #[inline] fn remove(object: &mut Object, key: &str) -> Option { object.remove(key) @@ -128,28 +127,76 @@ fn remove(object: &mut Object, key: &str) -> Option { /// assert_eq!(object.get("a"), Some(42)); /// assert_eq!(object.get("b"), None); /// ``` -#[rune::function(instance)] +#[rune::function(keep, instance)] #[inline] fn get(object: &Object, key: &str) -> Option { object.get(key).cloned() } +/// Test two objects for partial equality. +/// +/// # Examples +/// +/// ```rune +/// let a = #{a: 42}; +/// let b = #{a: 43}; +/// +/// assert_eq!(a, a); +/// assert_ne!(a, b); +/// assert_ne!(b, a); +/// ``` #[rune::function(keep, instance, protocol = PARTIAL_EQ)] fn partial_eq(this: &Object, other: &Object) -> VmResult { Object::partial_eq_with(this, other, &mut EnvProtocolCaller) } +/// Test two objects for total equality. +/// +/// # Examples +/// +/// ```rune +/// use std::ops::eq; +/// +/// let a = #{a: 42}; +/// let b = #{a: 43}; +/// +/// assert_eq!(eq(a, a), true); +/// assert_eq!(eq(a, b), false); +/// assert_eq!(eq(b, a), false); +/// ``` #[rune::function(keep, instance, protocol = EQ)] fn eq(this: &Object, other: &Object) -> VmResult { Object::eq_with(this, other, Value::eq_with, &mut EnvProtocolCaller) } -#[rune::function(keep, instance, protocol = PARTIAL_CMP)] -fn partial_cmp(this: &Object, other: &Object) -> VmResult> { - Object::partial_cmp_with(this, other, &mut EnvProtocolCaller) +/// Clones an object. +/// +/// # Examples +/// +/// ```rune +/// let a = #{a: 42}; +/// let b = a.clone(); +/// assert_eq!(a, b); +/// +/// b.b = 43; +/// assert_ne!(a, b); +/// ``` +#[rune::function(keep, instance, protocol = CLONE)] +fn clone(this: &Object) -> VmResult { + VmResult::Ok(vm_try!(this.try_clone())) } -#[rune::function(keep, instance, protocol = CMP)] -fn cmp(this: &Object, other: &Object) -> VmResult { - Object::cmp_with(this, other, &mut EnvProtocolCaller) +/// Write a debug representation of an object. +/// +/// # Examples +/// +/// ```rune +/// let a = #{a: 42, b: 43}; +/// +/// println!("{a:?}"); +/// ``` +#[rune::function(keep, instance, protocol = DEBUG_FMT)] +#[inline] +fn debug_fmt(this: &Object, f: &mut Formatter) -> VmResult<()> { + vm_write!(f, "{this:?}") } diff --git a/crates/rune/src/runtime/const_value.rs b/crates/rune/src/runtime/const_value.rs index 5fbff5d74..9f4894c60 100644 --- a/crates/rune/src/runtime/const_value.rs +++ b/crates/rune/src/runtime/const_value.rs @@ -139,7 +139,7 @@ impl ConstValueKind { ConstValueKind::Bytes(..) => TypeInfo::any::(), ConstValueKind::Vec(..) => TypeInfo::any::(), ConstValueKind::Tuple(..) => TypeInfo::any::(), - ConstValueKind::Object(..) => TypeInfo::static_type(static_type::OBJECT), + ConstValueKind::Object(..) => TypeInfo::any::(), ConstValueKind::Option(..) => TypeInfo::static_type(static_type::OPTION), ConstValueKind::Struct(hash, ..) => { TypeInfo::any_type_info(AnyTypeInfo::new(full_name, *hash)) @@ -241,17 +241,6 @@ impl ConstValue { Some(some) => Some(Box::try_new(Self::from_value_ref(some)?)?), None => None, }), - Mutable::Object(ref object) => { - let mut const_object = HashMap::try_with_capacity(object.len())?; - - for (key, value) in object { - let key = key.try_clone()?; - let value = Self::from_value_ref(value)?; - const_object.try_insert(key, value)?; - } - - ConstValueKind::Object(const_object) - } value => { return Err(RuntimeError::from(VmErrorKind::ConstNotSupported { actual: value.type_info(), @@ -287,6 +276,18 @@ impl ConstValue { ConstValueKind::Tuple(const_tuple.try_into_boxed_slice()?) } + Object::HASH => { + let object = value.borrow_ref::()?; + let mut const_object = HashMap::try_with_capacity(object.len())?; + + for (key, value) in object.iter() { + let key = key.try_clone()?; + let value = Self::from_value_ref(value)?; + const_object.try_insert(key, value)?; + } + + ConstValueKind::Object(const_object) + } _ => { return Err(RuntimeError::from(VmErrorKind::ConstNotSupported { actual: value.type_info(), diff --git a/crates/rune/src/runtime/from_value.rs b/crates/rune/src/runtime/from_value.rs index b51e48681..4ffef4b5b 100644 --- a/crates/rune/src/runtime/from_value.rs +++ b/crates/rune/src/runtime/from_value.rs @@ -10,21 +10,29 @@ use super::{ /// Derive macro for the [`FromValue`] trait for converting types from the /// dynamic `Value` container. /// -/// # Examples +/// This can be implemented for structs and variants. /// -/// ``` -/// use rune::{FromValue, Vm}; +/// For structs, this will try to decode any struct-like data into the desired data type: +/// +/// ```rust /// use std::sync::Arc; +/// use rune::{FromValue, Vm}; /// -/// #[derive(FromValue)] +/// #[derive(Debug, PartialEq, FromValue)] /// struct Foo { -/// field: u64, +/// a: u64, +/// b: u64, /// } /// /// let mut sources = rune::sources! { /// entry => { +/// struct Foo { +/// a, +/// b, +/// } +/// /// pub fn main() { -/// #{field: 42} +/// Foo { a: 1, b: 2 } /// } /// } /// }; @@ -35,7 +43,41 @@ use super::{ /// let foo = vm.call(["main"], ())?; /// let foo: Foo = rune::from_value(foo)?; /// -/// assert_eq!(foo.field, 42); +/// assert_eq!(foo, Foo { a: 1, b: 2 }); +/// # Ok::<_, rune::support::Error>(()) +/// ``` +/// +/// For enums, the variant name of the rune-local variant is matched: +/// +/// ```rust +/// use std::sync::Arc; +/// use rune::{FromValue, Vm}; +/// +/// #[derive(Debug, PartialEq, FromValue)] +/// enum Enum { +/// Variant(u32), +/// Variant2 { a: u32, b: u32 }, +/// } +/// +/// let mut sources = rune::sources! { +/// entry => { +/// enum Enum { +/// Variant(a), +/// } +/// +/// pub fn main() { +/// Enum::Variant(42) +/// } +/// } +/// }; +/// +/// let unit = rune::prepare(&mut sources).build()?; +/// +/// let mut vm = Vm::without_runtime(Arc::new(unit)); +/// let foo = vm.call(["main"], ())?; +/// let foo: Enum = rune::from_value(foo)?; +/// +/// assert_eq!(foo, Enum::Variant(42)); /// # Ok::<_, rune::support::Error>(()) /// ``` pub use rune_macros::FromValue; @@ -416,7 +458,7 @@ cfg_std! { T: FromValue, { fn from_value(value: Value) -> Result { - let object = value.into_object()?; + let object = value.into_any::<$crate::runtime::Object>()?; let mut output = <$ty>::with_capacity(object.len()); @@ -443,7 +485,7 @@ macro_rules! impl_try_map { T: FromValue, { fn from_value(value: Value) -> Result { - let object = value.into_object()?; + let object = value.into_any::<$crate::runtime::Object>()?; let mut output = <$ty>::try_with_capacity(object.len())?; diff --git a/crates/rune/src/runtime/mod.rs b/crates/rune/src/runtime/mod.rs index 1582b3d93..d7dba9293 100644 --- a/crates/rune/src/runtime/mod.rs +++ b/crates/rune/src/runtime/mod.rs @@ -167,11 +167,11 @@ pub use self::unit::{Unit, UnitStorage}; mod value; #[cfg(any(test, feature = "cli"))] pub(crate) use self::value::OwnedRepr; -pub(crate) use self::value::{BorrowRefRepr, MutRepr, Mutable, RefRepr}; pub use self::value::{ - EmptyStruct, Inline, RawValueGuard, Rtti, Struct, TupleStruct, TypeValue, Value, ValueMutGuard, - ValueRefGuard, VariantRtti, + Accessor, EmptyStruct, Inline, RawValueGuard, Rtti, Struct, TupleStruct, TypeValue, Value, + ValueMutGuard, ValueRefGuard, VariantRtti, }; +pub(crate) use self::value::{BorrowRefRepr, MutRepr, Mutable, RefRepr}; mod variant; pub use self::variant::{Variant, VariantData}; diff --git a/crates/rune/src/runtime/object.rs b/crates/rune/src/runtime/object.rs index aa4f1a32b..5972981c5 100644 --- a/crates/rune/src/runtime/object.rs +++ b/crates/rune/src/runtime/object.rs @@ -1,6 +1,5 @@ use core::borrow; use core::cmp; -use core::cmp::Ordering; use core::fmt; use core::hash; use core::iter; @@ -13,7 +12,7 @@ use crate::alloc::{hash_map, HashMap}; use crate::runtime::{ FromValue, ProtocolCaller, RawAnyGuard, Ref, ToValue, Value, VmError, VmResult, }; -use crate::{Any, ItemBuf}; +use crate::Any; /// An owning iterator over the entries of a `Object`. /// @@ -81,7 +80,6 @@ pub type Values<'a> = hash_map::Values<'a, String, Value>; /// ``` #[derive(Any, Default)] #[repr(transparent)] -#[rune(builtin, static_type = OBJECT)] #[rune(item = ::std::object)] pub struct Object { inner: HashMap, @@ -408,72 +406,6 @@ impl Object { VmResult::Ok(true) } - - pub(crate) fn partial_cmp_with( - a: &Self, - b: &Self, - caller: &mut dyn ProtocolCaller, - ) -> VmResult> { - let mut b = b.inner.iter(); - - for (k1, v1) in a.inner.iter() { - let Some((k2, v2)) = b.next() else { - return VmResult::Ok(Some(Ordering::Greater)); - }; - - match k1.partial_cmp(k2) { - Some(Ordering::Equal) => (), - other => return VmResult::Ok(other), - } - - match Value::partial_cmp_with(v1, v2, caller) { - VmResult::Ok(Some(Ordering::Equal)) => (), - other => return other, - } - } - - if b.next().is_some() { - return VmResult::Ok(Some(Ordering::Less)); - } - - VmResult::Ok(Some(Ordering::Equal)) - } - - pub(crate) fn cmp_with( - a: &Self, - b: &Self, - caller: &mut dyn ProtocolCaller, - ) -> VmResult { - let mut b = b.inner.iter(); - - for (k1, v1) in a.inner.iter() { - let Some((k2, v2)) = b.next() else { - return VmResult::Ok(Ordering::Greater); - }; - - match k1.cmp(k2) { - Ordering::Equal => (), - other => return VmResult::Ok(other), - } - - match Value::cmp_with(v1, v2, caller) { - VmResult::Ok(Ordering::Equal) => (), - other => return other, - } - } - - if b.next().is_some() { - return VmResult::Ok(Ordering::Less); - } - - VmResult::Ok(Ordering::Equal) - } - - /// Debug implementation for a struct. This assumes that all fields - /// corresponds to identifiers. - pub(crate) fn debug_struct<'a>(&'a self, item: &'a ItemBuf) -> DebugStruct<'a> { - DebugStruct { item, st: self } - } } impl TryClone for Object { @@ -515,32 +447,12 @@ impl IntoIterator for Object { } impl fmt::Debug for Object { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_map().entries(self.inner.iter()).finish() } } -from_value2!(Object, into_object_ref, into_object_mut, into_object); - -pub struct DebugStruct<'a> { - item: &'a ItemBuf, - st: &'a Object, -} - -impl fmt::Display for DebugStruct<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use ::rust_alloc::string::ToString; - - let mut d = f.debug_struct(&self.item.to_string()); - - for (key, value) in self.st.iter() { - d.field(key, value); - } - - d.finish() - } -} - #[derive(Any)] #[rune(item = ::std::object, name = Iter)] pub struct RuneIter { diff --git a/crates/rune/src/runtime/static_type.rs b/crates/rune/src/runtime/static_type.rs index dc5023204..49f496796 100644 --- a/crates/rune/src/runtime/static_type.rs +++ b/crates/rune/src/runtime/static_type.rs @@ -172,11 +172,9 @@ any_type! { ::std::tuple::Tuple { impl for rt::Tuple; } -} -static_type! { /// The specialized type information for the [`Object`] type. - pub(crate) static [OBJECT, OBJECT_HASH] = ::std::object::Object { + ::std::object::Object { impl for rt::Struct; impl for HashMap<::rust_alloc::string::String, T>; impl for HashMap; @@ -189,7 +187,9 @@ static_type! { #[cfg_attr(rune_docsrs, doc(cfg(feature = "std")))] impl for ::std::collections::HashMap; } +} +static_type! { pub(crate) static [RESULT, RESULT_HASH] = ::std::result::Result { impl for Result; } diff --git a/crates/rune/src/runtime/value.rs b/crates/rune/src/runtime/value.rs index 1af90cee5..165471f73 100644 --- a/crates/rune/src/runtime/value.rs +++ b/crates/rune/src/runtime/value.rs @@ -7,7 +7,7 @@ pub use self::inline::Inline; mod serde; mod rtti; -pub use self::rtti::{Rtti, VariantRtti}; +pub use self::rtti::{Accessor, Rtti, VariantRtti}; mod data; pub use self::data::{EmptyStruct, Struct, TupleStruct}; @@ -422,7 +422,6 @@ impl Value { }); } RefRepr::Mutable(value) => match &*vm_try!(value.borrow_ref()) { - Mutable::Object(value) => Mutable::Object(vm_try!(value.try_clone())), Mutable::Option(value) => Mutable::Option(vm_try!(value.try_clone())), Mutable::Result(value) => Mutable::Result(vm_try!(value.try_clone())), Mutable::EmptyStruct(value) => Mutable::EmptyStruct(vm_try!(value.try_clone())), @@ -484,9 +483,6 @@ impl Value { }; match &*vm_try!(value.borrow_ref()) { - Mutable::Object(value) => { - vm_try!(vm_write!(f, "{value:?}")); - } Mutable::Option(value) => { vm_try!(vm_write!(f, "{value:?}")); } @@ -701,7 +697,6 @@ impl Value { value => Ok(TypeValue::NotTypedInline(NotTypedInlineValue(value))), }, OwnedRepr::Mutable(value) => match value { - Mutable::Object(object) => Ok(TypeValue::Object(object)), Mutable::EmptyStruct(empty) => Ok(TypeValue::EmptyStruct(empty)), Mutable::TupleStruct(tuple) => Ok(TypeValue::TupleStruct(tuple)), Mutable::Struct(object) => Ok(TypeValue::Struct(object)), @@ -710,7 +705,8 @@ impl Value { }, OwnedRepr::Any(value) => match value.type_hash() { OwnedTuple::HASH => Ok(TypeValue::Tuple(value.downcast()?)), - _ => Ok(TypeValue::NotTypedRef(NotTypedRefValue(value))), + Object::HASH => Ok(TypeValue::Object(value.downcast()?)), + _ => Ok(TypeValue::NotTypedAnyObj(NotTypedAnyObj(value))), }, } } @@ -803,16 +799,6 @@ impl Value { into_struct, } - into! { - /// Coerce into a [`Object`]. - Object(Object), - into_object_ref, - into_object_mut, - borrow_object_ref, - borrow_object_mut, - into_object, - } - /// Borrow as a tuple. /// /// This ensures that the value has read access to the underlying value @@ -1167,12 +1153,7 @@ impl Value { } (Mutable::Struct(a), Mutable::Struct(b)) => { if a.rtti.hash == b.rtti.hash { - return Object::eq_with( - &a.data, - &b.data, - Value::partial_eq_with, - caller, - ); + return Vec::eq_with(&a.data, &b.data, Value::partial_eq_with, caller); } } (Mutable::Variant(a), Mutable::Variant(b)) => { @@ -1326,9 +1307,6 @@ impl Value { }); } (BorrowRefRepr::Mutable(a), BorrowRefRepr::Mutable(b)) => match (&*a, &*b) { - (Mutable::Object(a), Mutable::Object(b)) => { - return Object::eq_with(a, b, Value::eq_with, caller); - } (Mutable::EmptyStruct(a), Mutable::EmptyStruct(b)) => { if a.rtti.hash == b.rtti.hash { // NB: don't get any future ideas, this must fall through to @@ -1346,7 +1324,7 @@ impl Value { } (Mutable::Struct(a), Mutable::Struct(b)) => { if a.rtti.hash == b.rtti.hash { - return Object::eq_with(&a.data, &b.data, Value::eq_with, caller); + return Vec::eq_with(&a.data, &b.data, Value::eq_with, caller); } } (Mutable::Variant(a), Mutable::Variant(b)) => { @@ -1422,9 +1400,6 @@ impl Value { }) } (BorrowRefRepr::Mutable(a), BorrowRefRepr::Mutable(b)) => match (&*a, &*b) { - (Mutable::Object(a), Mutable::Object(b)) => { - return Object::partial_cmp_with(a, b, caller); - } (Mutable::EmptyStruct(a), Mutable::EmptyStruct(b)) => { if a.rtti.hash == b.rtti.hash { // NB: don't get any future ideas, this must fall through to @@ -1442,7 +1417,7 @@ impl Value { } (Mutable::Struct(a), Mutable::Struct(b)) => { if a.rtti.hash == b.rtti.hash { - return Object::partial_cmp_with(&a.data, &b.data, caller); + return Vec::partial_cmp_with(&a.data, &b.data, caller); } } (Mutable::Variant(a), Mutable::Variant(b)) => { @@ -1511,9 +1486,6 @@ impl Value { ) { (BorrowRefRepr::Inline(a), BorrowRefRepr::Inline(b)) => return a.cmp(b), (BorrowRefRepr::Mutable(a), BorrowRefRepr::Mutable(b)) => match (&*a, &*b) { - (Mutable::Object(a), Mutable::Object(b)) => { - return Object::cmp_with(a, b, caller); - } (Mutable::EmptyStruct(a), Mutable::EmptyStruct(b)) => { if a.rtti.hash == b.rtti.hash { // NB: don't get any future ideas, this must fall through to @@ -1531,7 +1503,7 @@ impl Value { } (Mutable::Struct(a), Mutable::Struct(b)) => { if a.rtti.hash == b.rtti.hash { - return Object::cmp_with(&a.data, &b.data, caller); + return Vec::cmp_with(&a.data, &b.data, caller); } } (Mutable::Variant(a), Mutable::Variant(b)) => { @@ -1903,7 +1875,6 @@ from! { TupleStruct => TupleStruct, Struct => Struct, Variant => Variant, - Object => Object, } any_from! { @@ -1918,6 +1889,7 @@ any_from! { super::Stream, super::Function, super::Future, + super::Object, } from_container! { @@ -1990,11 +1962,11 @@ pub struct NotTypedMutableValue(Mutable); /// Wrapper for an any ref value kind. #[doc(hidden)] -pub struct NotTypedRefValue(AnyObj); +pub struct NotTypedAnyObj(AnyObj); /// The coersion of a value into a typed value. -#[doc(hidden)] #[non_exhaustive] +#[doc(hidden)] pub enum TypeValue { /// The unit value. Unit, @@ -2018,7 +1990,7 @@ pub enum TypeValue { NotTypedMutable(NotTypedMutableValue), /// Not a typed value. #[doc(hidden)] - NotTypedRef(NotTypedRefValue), + NotTypedAnyObj(NotTypedAnyObj), } impl TypeValue { @@ -2028,21 +2000,19 @@ impl TypeValue { match self { TypeValue::Unit => TypeInfo::any::(), TypeValue::Tuple(..) => TypeInfo::any::(), - TypeValue::Object(..) => TypeInfo::static_type(static_type::OBJECT), + TypeValue::Object(..) => TypeInfo::any::(), TypeValue::EmptyStruct(empty) => empty.type_info(), TypeValue::TupleStruct(tuple) => tuple.type_info(), TypeValue::Struct(object) => object.type_info(), TypeValue::Variant(empty) => empty.type_info(), TypeValue::NotTypedInline(value) => value.0.type_info(), TypeValue::NotTypedMutable(value) => value.0.type_info(), - TypeValue::NotTypedRef(value) => value.0.type_info(), + TypeValue::NotTypedAnyObj(value) => value.0.type_info(), } } } pub(crate) enum Mutable { - /// An object. - Object(Object), /// An empty value indicating nothing. Option(Option), /// A stored result in a slot. @@ -2060,7 +2030,6 @@ pub(crate) enum Mutable { impl Mutable { pub(crate) fn type_info(&self) -> TypeInfo { match self { - Mutable::Object(..) => TypeInfo::static_type(static_type::OBJECT), Mutable::Option(..) => TypeInfo::static_type(static_type::OPTION), Mutable::Result(..) => TypeInfo::static_type(static_type::RESULT), Mutable::EmptyStruct(empty) => empty.type_info(), @@ -2076,7 +2045,6 @@ impl Mutable { /// *enum*, and not the type hash of the variant itself. pub(crate) fn type_hash(&self) -> Hash { match self { - Mutable::Object(..) => static_type::OBJECT.hash, Mutable::Result(..) => static_type::RESULT.hash, Mutable::Option(..) => static_type::OPTION.hash, Mutable::EmptyStruct(empty) => empty.rtti.hash, diff --git a/crates/rune/src/runtime/value/data.rs b/crates/rune/src/runtime/value/data.rs index e60273fc4..ad719ceb2 100644 --- a/crates/rune/src/runtime/value/data.rs +++ b/crates/rune/src/runtime/value/data.rs @@ -1,14 +1,15 @@ use core::borrow::Borrow; use core::fmt; use core::hash; +use core::mem::take; use rust_alloc::sync::Arc; use crate as rune; use crate::alloc::prelude::*; -use crate::runtime::{Object, OwnedTuple, TypeInfo}; +use crate::runtime::{OwnedTuple, TypeInfo}; -use super::{Hash, Rtti, Value}; +use super::{Rtti, Value}; /// A empty with a well-defined type. #[derive(TryClone)] @@ -88,58 +89,69 @@ impl fmt::Debug for TupleStruct { pub struct Struct { /// The type hash of the object. pub(crate) rtti: Arc, - /// Content of the object. - pub(crate) data: Object, + /// Contents of the object. + pub(crate) data: Box<[Value]>, } impl Struct { - /// Access runtime type information. + /// Access struct rtti. pub fn rtti(&self) -> &Arc { &self.rtti } - /// Access underlying data. - pub fn data(&self) -> &Object { + /// Access truct data. + pub fn data(&self) -> &[Value] { &self.data } - /// Access underlying data mutably. - pub fn data_mut(&mut self) -> &mut Object { + /// Access struct data mutably. + pub fn data_mut(&mut self) -> &mut [Value] { &mut self.data } - /// Get type info for the typed object. - pub fn type_info(&self) -> TypeInfo { - TypeInfo::typed(self.rtti.clone()) - } - - /// Get the type hash of the object. - #[inline] - pub fn type_hash(&self) -> Hash { - self.rtti.hash - } - - /// Get the given key in the object. - pub fn get(&self, k: &Q) -> Option<&Value> + /// Get a field through the accessor. + pub fn get(&self, key: &Q) -> Option<&Value> where - String: Borrow, - Q: ?Sized + hash::Hash + Eq + Ord, + Box: Borrow, + Q: hash::Hash + Eq + ?Sized, { - self.data.get(k) + self.data.get(*self.rtti.fields.get(key)?) } - /// Get the given mutable value by key in the object. - pub fn get_mut(&mut self, k: &Q) -> Option<&mut Value> + /// Get a field through the accessor. + pub fn get_mut(&mut self, key: &Q) -> Option<&mut Value> where - String: Borrow, - Q: ?Sized + hash::Hash + Eq + Ord, + Box: Borrow, + Q: hash::Hash + Eq + ?Sized, { - self.data.get_mut(k) + self.data.get_mut(*self.rtti.fields.get(key)?) + } + + /// Get type info for the typed object. + pub(crate) fn type_info(&self) -> TypeInfo { + TypeInfo::typed(self.rtti.clone()) } } impl fmt::Debug for Struct { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.data.debug_struct(&self.rtti.item)) + write!(f, "{} {{", self.rtti.item)?; + + let mut first = true; + + for (index, field) in self.data.iter().enumerate() { + let Some((name, _)) = self.rtti.fields.iter().find(|t| *t.1 == index) else { + continue; + }; + + if !take(&mut first) { + write!(f, ", ")?; + } + + write!(f, "{name}: {field:?}")?; + } + + write!(f, "}}")?; + Ok(()) } } diff --git a/crates/rune/src/runtime/value/rtti.rs b/crates/rune/src/runtime/value/rtti.rs index 09187c941..85bf2fb5b 100644 --- a/crates/rune/src/runtime/value/rtti.rs +++ b/crates/rune/src/runtime/value/rtti.rs @@ -1,8 +1,12 @@ +use core::borrow::Borrow; use core::cmp::Ordering; use core::hash; use serde::{Deserialize, Serialize}; +use crate::alloc::prelude::*; +use crate::alloc::HashMap; +use crate::runtime::Value; use crate::{Hash, ItemBuf}; /// Runtime information on variant. @@ -15,6 +19,28 @@ pub struct VariantRtti { pub hash: Hash, /// The name of the variant. pub item: ItemBuf, + /// Fields associated with the variant. + pub fields: HashMap, usize>, +} + +impl VariantRtti { + /// Access a named field mutably from the given data. + pub fn get_field<'a, Q>(&self, data: &'a [Value], key: &Q) -> Option<&'a Value> + where + Box: Borrow, + Q: hash::Hash + Eq + ?Sized, + { + data.get(*self.fields.get(key)?) + } + + /// Access a named field immutably from the given data. + pub fn get_field_mut<'a, Q>(&self, data: &'a mut [Value], key: &Q) -> Option<&'a mut Value> + where + Box: Borrow, + Q: hash::Hash + Eq + ?Sized, + { + data.get_mut(*self.fields.get(key)?) + } } impl PartialEq for VariantRtti { @@ -43,6 +69,25 @@ impl Ord for VariantRtti { } } +/// Field accessor for a variant struct. +#[doc(hidden)] +pub struct Accessor<'a> { + pub(crate) fields: &'a HashMap, usize>, + pub(crate) data: &'a [Value], +} + +impl<'a> Accessor<'a> { + /// Get a field through the accessor. + #[doc(hidden)] + pub fn get(&self, key: &Q) -> Option<&Value> + where + Box: Borrow, + Q: hash::Hash + Eq + ?Sized, + { + self.data.get(*self.fields.get(key)?) + } +} + /// Runtime information on variant. #[derive(Debug, Serialize, Deserialize)] #[non_exhaustive] @@ -51,6 +96,28 @@ pub struct Rtti { pub hash: Hash, /// The item of the type. pub item: ItemBuf, + /// Mapping from field names to their corresponding indexes. + pub fields: HashMap, usize>, +} + +impl Rtti { + /// Access a named field mutably from the given data. + pub fn get_field<'a, Q>(&self, data: &'a [Value], key: &Q) -> Option<&'a Value> + where + Box: Borrow, + Q: hash::Hash + Eq + ?Sized, + { + data.get(*self.fields.get(key)?) + } + + /// Access a named field immutably from the given data. + pub fn get_field_mut<'a, Q>(&self, data: &'a mut [Value], key: &Q) -> Option<&'a mut Value> + where + Box: Borrow, + Q: hash::Hash + Eq + ?Sized, + { + data.get_mut(*self.fields.get(key)?) + } } impl PartialEq for Rtti { diff --git a/crates/rune/src/runtime/value/serde.rs b/crates/rune/src/runtime/value/serde.rs index 114fbdfd9..3bc632316 100644 --- a/crates/rune/src/runtime/value/serde.rs +++ b/crates/rune/src/runtime/value/serde.rs @@ -2,7 +2,7 @@ use core::fmt; use crate::alloc; use crate::alloc::prelude::*; -use crate::runtime::{self, BorrowRefRepr, Bytes, Inline, Mutable, Object, Vec}; +use crate::runtime::{self, BorrowRefRepr, Bytes, Inline, Mutable, Object, OwnedTuple, Vec}; use crate::TypeHash; use serde::de::{self, Deserialize as _, Error as _}; @@ -38,15 +38,6 @@ impl ser::Serialize for Value { Inline::Ordering(..) => Err(ser::Error::custom("cannot serialize orderings")), }, BorrowRefRepr::Mutable(value) => match &*value { - Mutable::Object(object) => { - let mut serializer = serializer.serialize_map(Some(object.len()))?; - - for (key, value) in object { - serializer.serialize_entry(key, value)?; - } - - serializer.end() - } Mutable::Option(option) => >::serialize(option, serializer), Mutable::EmptyStruct(..) => { Err(ser::Error::custom("cannot serialize empty structs")) @@ -77,10 +68,8 @@ impl ser::Serialize for Value { serializer.end() } - runtime::OwnedTuple::HASH => { - let tuple = value - .borrow_ref::() - .map_err(S::Error::custom)?; + OwnedTuple::HASH => { + let tuple = value.borrow_ref::().map_err(S::Error::custom)?; let mut serializer = serializer.serialize_seq(Some(tuple.len()))?; for value in tuple.iter() { @@ -89,6 +78,16 @@ impl ser::Serialize for Value { serializer.end() } + Object::HASH => { + let object = value.borrow_ref::().map_err(S::Error::custom)?; + let mut serializer = serializer.serialize_map(Some(object.len()))?; + + for (key, value) in object.iter() { + serializer.serialize_entry(key, value)?; + } + + serializer.end() + } _ => Err(ser::Error::custom("cannot serialize external references")), }, } diff --git a/crates/rune/src/runtime/variant.rs b/crates/rune/src/runtime/variant.rs index 085e00d79..7f77520cf 100644 --- a/crates/rune/src/runtime/variant.rs +++ b/crates/rune/src/runtime/variant.rs @@ -5,9 +5,9 @@ use ::rust_alloc::sync::Arc; use crate as rune; use crate::alloc::clone::TryClone; -use crate::runtime::{ - Object, OwnedTuple, ProtocolCaller, TypeInfo, Value, VariantRtti, Vec, VmResult, -}; +use crate::alloc::Box; + +use super::{Accessor, OwnedTuple, ProtocolCaller, TypeInfo, Value, VariantRtti, Vec, VmResult}; /// The variant of a type. #[derive(TryClone)] @@ -17,8 +17,17 @@ pub struct Variant { } impl Variant { + /// Construct a field accessor from this variant. + #[doc(hidden)] + pub fn accessor<'a>(&'a self, data: &'a [Value]) -> Accessor<'a> { + Accessor { + fields: &self.rtti.fields, + data, + } + } + /// Construct a unit variant. - pub fn unit(rtti: Arc) -> Self { + pub(crate) fn unit(rtti: Arc) -> Self { Self { rtti, data: VariantData::Empty, @@ -26,7 +35,7 @@ impl Variant { } /// Construct a tuple variant. - pub fn tuple(rtti: Arc, tuple: OwnedTuple) -> Self { + pub(crate) fn tuple(rtti: Arc, tuple: OwnedTuple) -> Self { Self { rtti, data: VariantData::Tuple(tuple), @@ -34,7 +43,7 @@ impl Variant { } /// Construct a struct variant. - pub fn struct_(rtti: Arc, data: Object) -> Self { + pub(crate) fn struct_(rtti: Arc, data: Box<[Value]>) -> Self { Self { rtti, data: VariantData::Struct(data), @@ -57,7 +66,7 @@ impl Variant { } /// Get type info for the variant. - pub fn type_info(&self) -> TypeInfo { + pub(crate) fn type_info(&self) -> TypeInfo { TypeInfo::variant(self.rtti.clone()) } @@ -81,7 +90,7 @@ impl Variant { Vec::eq_with(a, b, Value::partial_eq_with, caller) } (VariantData::Struct(a), VariantData::Struct(b)) => { - Object::eq_with(a, b, Value::partial_eq_with, caller) + Vec::eq_with(a, b, Value::partial_eq_with, caller) } _ => VmResult::panic("data mismatch between variants"), } @@ -103,7 +112,7 @@ impl Variant { Vec::eq_with(a, b, Value::eq_with, caller) } (VariantData::Struct(a), VariantData::Struct(b)) => { - Object::eq_with(a, b, Value::eq_with, caller) + Vec::eq_with(a, b, Value::eq_with, caller) } _ => VmResult::panic("data mismatch between variants"), } @@ -127,9 +136,7 @@ impl Variant { match (&a.data, &b.data) { (VariantData::Empty, VariantData::Empty) => VmResult::Ok(Some(Ordering::Equal)), (VariantData::Tuple(a), VariantData::Tuple(b)) => Vec::partial_cmp_with(a, b, caller), - (VariantData::Struct(a), VariantData::Struct(b)) => { - Object::partial_cmp_with(a, b, caller) - } + (VariantData::Struct(a), VariantData::Struct(b)) => Vec::partial_cmp_with(a, b, caller), _ => VmResult::panic("data mismatch between variants"), } } @@ -152,7 +159,7 @@ impl Variant { match (&a.data, &b.data) { (VariantData::Empty, VariantData::Empty) => VmResult::Ok(Ordering::Equal), (VariantData::Tuple(a), VariantData::Tuple(b)) => Vec::cmp_with(a, b, caller), - (VariantData::Struct(a), VariantData::Struct(b)) => Object::cmp_with(a, b, caller), + (VariantData::Struct(a), VariantData::Struct(b)) => Vec::cmp_with(a, b, caller), _ => VmResult::panic("data mismatch between variants"), } } @@ -164,7 +171,7 @@ pub enum VariantData { /// A unit variant. Empty, /// A struct variant. - Struct(Object), + Struct(Box<[Value]>), /// A tuple variant. Tuple(OwnedTuple), } diff --git a/crates/rune/src/runtime/vm.rs b/crates/rune/src/runtime/vm.rs index 57475418b..fdf67691b 100644 --- a/crates/rune/src/runtime/vm.rs +++ b/crates/rune/src/runtime/vm.rs @@ -832,28 +832,45 @@ impl Vm { /// Implementation of getting a string index on an object-like type. fn try_object_like_index_get(target: &Value, field: &str) -> VmResult> { - let BorrowRefRepr::Mutable(target) = vm_try!(target.borrow_ref_repr()) else { - return VmResult::Ok(None); - }; + match vm_try!(target.as_ref_repr()) { + RefRepr::Inline(..) => VmResult::Ok(None), + RefRepr::Mutable(target) => { + let target = vm_try!(target.borrow_ref()); - let value = match &*target { - Mutable::Object(target) => target.get(field), - Mutable::Struct(target) => target.get(field), - Mutable::Variant(variant) => match variant.data() { - VariantData::Struct(target) => target.get(field), - _ => return VmResult::Ok(None), - }, - _ => return VmResult::Ok(None), - }; + let value = match &*target { + Mutable::Struct(target) => target.get(field), + Mutable::Variant(variant) => match variant.data() { + VariantData::Struct(target) => variant.rtti.get_field(target, field), + _ => return VmResult::Ok(None), + }, + _ => return VmResult::Ok(None), + }; - let Some(value) = value else { - return err(VmErrorKind::MissingField { - target: target.type_info(), - field: vm_try!(field.try_to_owned()), - }); - }; + let Some(value) = value else { + return err(VmErrorKind::MissingField { + target: target.type_info(), + field: vm_try!(field.try_to_owned()), + }); + }; + + VmResult::Ok(Some(value.clone())) + } + RefRepr::Any(value) => match value.type_hash() { + Object::HASH => { + let target = vm_try!(value.borrow_ref::()); + + let Some(value) = target.get(field) else { + return err(VmErrorKind::MissingField { + target: TypeInfo::any::(), + field: vm_try!(field.try_to_owned()), + }); + }; - VmResult::Ok(Some(value.clone())) + VmResult::Ok(Some(value.clone())) + } + _ => VmResult::Ok(None), + }, + } } /// Implementation of getting a string index on an object-like type. @@ -1037,18 +1054,16 @@ impl Vm { let mut unsupported = false; let result = BorrowMut::try_map(target, |value| { - match value { - Mutable::Object(target) => { - return target.get_mut(field); - } - Mutable::Struct(target) => { + match *value { + Mutable::Struct(ref mut target) => { return target.get_mut(field); } Mutable::Variant(Variant { - data: VariantData::Struct(st), + ref rtti, + data: VariantData::Struct(ref mut st), .. }) => { - return st.get_mut(field); + return rtti.get_field_mut(st, field); } _ => {} } @@ -1069,7 +1084,21 @@ impl Vm { }), } } - RefRepr::Any(..) => VmResult::Ok(None), + RefRepr::Any(value) => match value.type_hash() { + Object::HASH => { + let object = vm_try!(value.borrow_mut::()); + + if let Ok(value) = BorrowMut::try_map(object, |object| object.get_mut(field)) { + return VmResult::Ok(Some(value)); + } + + err(VmErrorKind::MissingField { + target: value.type_info(), + field: vm_try!(field.try_to_owned()), + }) + } + _ => VmResult::Ok(None), + }, } } @@ -1160,24 +1189,19 @@ impl Vm { RefRepr::Inline(..) => { return VmResult::Ok(CallResult::Unsupported(target.clone())); } - RefRepr::Mutable(target) => match &mut *vm_try!(target.borrow_mut()) { - Mutable::Object(object) => { - if let Some(value) = object.get(index.as_str()) { - vm_try!(out.store(&mut self.stack, || value.clone())); - return VmResult::Ok(CallResult::Ok(())); - } - } - Mutable::Struct(typed_object) => { + RefRepr::Mutable(target) => match *vm_try!(target.borrow_ref()) { + Mutable::Struct(ref typed_object) => { if let Some(value) = typed_object.get(index.as_str()) { vm_try!(out.store(&mut self.stack, || value.clone())); return VmResult::Ok(CallResult::Ok(())); } } Mutable::Variant(Variant { - data: VariantData::Struct(data), + ref rtti, + data: VariantData::Struct(ref data), .. }) => { - if let Some(value) = data.get(index.as_str()) { + if let Some(value) = rtti.get_field(data, index.as_str()) { vm_try!(out.store(&mut self.stack, || value.clone())); return VmResult::Ok(CallResult::Ok(())); } @@ -1186,7 +1210,17 @@ impl Vm { break 'fallback; } }, - RefRepr::Any(..) => break 'fallback, + RefRepr::Any(value) => match value.type_hash() { + Object::HASH => { + let object = vm_try!(value.borrow_ref::()); + + if let Some(value) = object.get(index.as_str()) { + vm_try!(out.store(&mut self.stack, || value.clone())); + return VmResult::Ok(CallResult::Ok(())); + } + } + _ => break 'fallback, + }, } return err(VmErrorKind::ObjectIndexMissing { slot }); @@ -1207,11 +1241,6 @@ impl Vm { let mut target = vm_try!(target.borrow_mut()); match &mut *target { - Mutable::Object(object) => { - let key = vm_try!(field.try_to_owned()); - vm_try!(object.insert(key, value.clone())); - return VmResult::Ok(true); - } Mutable::Struct(object) => { if let Some(v) = object.get_mut(field) { v.clone_from(value); @@ -1219,8 +1248,8 @@ impl Vm { } } Mutable::Variant(variant) => { - if let VariantData::Struct(data) = variant.data_mut() { - if let Some(v) = data.get_mut(field) { + if let VariantData::Struct(data) = &mut variant.data { + if let Some(v) = variant.rtti.get_field_mut(data, field) { v.clone_from(value); return VmResult::Ok(true); } @@ -1236,7 +1265,15 @@ impl Vm { field: vm_try!(field.try_to_owned()), }) } - RefRepr::Any(..) => VmResult::Ok(false), + RefRepr::Any(target) => match target.type_hash() { + Object::HASH => { + let key = vm_try!(field.try_to_owned()); + let mut object = vm_try!(target.borrow_mut::()); + vm_try!(object.insert(key, value.clone())); + VmResult::Ok(true) + } + _ => VmResult::Ok(false), + }, } } @@ -2893,7 +2930,7 @@ impl Vm { vm_try!(object.insert(key, take(value))); } - vm_try!(out.store(&mut self.stack, Mutable::Object(object))); + vm_try!(out.store(&mut self.stack, object)); VmResult::Ok(()) } @@ -2950,36 +2987,24 @@ impl Vm { /// Operation to allocate an object struct. #[cfg_attr(feature = "bench", inline(never))] - fn op_struct( - &mut self, - addr: InstAddress, - hash: Hash, - slot: usize, - out: Output, - ) -> VmResult<()> { - let keys = vm_try!(self - .unit - .lookup_object_keys(slot) - .ok_or(VmErrorKind::MissingStaticObjectKeys { slot })); - + fn op_struct(&mut self, addr: InstAddress, hash: Hash, _: usize, out: Output) -> VmResult<()> { let rtti = vm_try!(self .unit .lookup_rtti(hash) .ok_or(VmErrorKind::MissingRtti { hash })); - let mut data = vm_try!(Object::with_capacity(keys.len())); - let values = vm_try!(self.stack.slice_at_mut(addr, keys.len())); + let values = vm_try!(self.stack.slice_at_mut(addr, rtti.fields.len())); + let mut data = vm_try!(alloc::Vec::try_with_capacity(rtti.fields.len())); - for (key, value) in keys.iter().zip(values) { - let key = vm_try!(String::try_from(key.as_str())); - vm_try!(data.insert(key, take(value))); + for value in values { + vm_try!(data.try_push(take(value))); } vm_try!(out.store( &mut self.stack, Mutable::Struct(Struct { rtti: rtti.clone(), - data, + data: vm_try!(data.try_into()), }) )); @@ -3012,31 +3037,26 @@ impl Vm { &mut self, addr: InstAddress, hash: Hash, - slot: usize, + _: usize, out: Output, ) -> VmResult<()> { - let keys = vm_try!(self - .unit - .lookup_object_keys(slot) - .ok_or(VmErrorKind::MissingStaticObjectKeys { slot })); - let rtti = vm_try!(self .unit .lookup_variant_rtti(hash) .ok_or(VmErrorKind::MissingVariantRtti { hash })); - let mut data = vm_try!(Object::with_capacity(keys.len())); - let values = vm_try!(self.stack.slice_at_mut(addr, keys.len())); + let mut data = vm_try!(alloc::Vec::try_with_capacity(rtti.fields.len())); + let values = vm_try!(self.stack.slice_at_mut(addr, rtti.fields.len())); - for (key, value) in keys.iter().zip(values) { - let key = vm_try!(String::try_from(key.as_str())); - vm_try!(data.insert(key, take(value))); + for value in values { + vm_try!(data.try_push(take(value))); } vm_try!(out.store( &mut self.stack, - Mutable::Variant(Variant::struct_(rtti.clone(), data)) + Mutable::Variant(Variant::struct_(rtti.clone(), vm_try!(data.try_into()))) )); + VmResult::Ok(()) } @@ -3365,19 +3385,16 @@ impl Vm { let value = vm_try!(self.stack.at(addr)); - let is_match = match vm_try!(value.borrow_ref_repr()) { - BorrowRefRepr::Mutable(value) => match &*value { - Mutable::Object(object) => { - let keys = vm_try!(self - .unit - .lookup_object_keys(slot) - .ok_or(VmErrorKind::MissingStaticObjectKeys { slot })); + let is_match = match vm_try!(value.try_borrow_ref::()) { + Some(object) => { + let keys = vm_try!(self + .unit + .lookup_object_keys(slot) + .ok_or(VmErrorKind::MissingStaticObjectKeys { slot })); - test(object, keys, exact) - } - _ => false, - }, - _ => false, + test(&object, keys, exact) + } + None => false, }; vm_try!(out.store(&mut self.stack, is_match)); diff --git a/crates/rune/src/tests.rs b/crates/rune/src/tests.rs index 75702c9ec..a14be11e9 100644 --- a/crates/rune/src/tests.rs +++ b/crates/rune/src/tests.rs @@ -15,7 +15,7 @@ pub(crate) mod prelude { pub(crate) use crate::module::InstallWith; pub(crate) use crate::parse; pub(crate) use crate::runtime::{ - self, Bytes, Formatter, Function, InstAddress, MaybeTypeOf, Mut, Mutable, Object, Output, + self, Bytes, Formatter, Function, InstAddress, MaybeTypeOf, Mutable, Object, Output, OwnedRepr, OwnedTuple, Protocol, RawAnyGuard, Ref, Stack, Tuple, TypeHash, TypeInfo, TypeOf, UnsafeToRef, VecTuple, VmErrorKind, VmResult, }; @@ -23,7 +23,7 @@ pub(crate) mod prelude { pub(crate) use crate::tests::{eval, run}; pub(crate) use crate::{ from_value, prepare, sources, span, vm_try, Any, Context, ContextError, Diagnostics, - FromValue, Hash, Item, ItemBuf, Module, Source, Sources, ToValue, Value, Vm, + FromValue, Hash, Item, ItemBuf, Module, Source, Sources, Value, Vm, }; pub(crate) use futures_executor::block_on; @@ -432,7 +432,6 @@ mod core_macros; mod custom_macros; mod debug_fmt; mod deprecation; -mod derive_from_to_value; mod destructuring; mod esoteric_impls; mod external_constructor; diff --git a/crates/rune/src/tests/derive_from_to_value.rs b/crates/rune/tests/derive_from_value.rs similarity index 61% rename from crates/rune/src/tests/derive_from_to_value.rs rename to crates/rune/tests/derive_from_value.rs index 2199cac12..0a8e74ad8 100644 --- a/crates/rune/src/tests/derive_from_to_value.rs +++ b/crates/rune/tests/derive_from_value.rs @@ -1,11 +1,8 @@ -#![allow( - unused, - clippy::enum_variant_names, - clippy::vec_init_then_push, - clippy::needless_return -)] +#![allow(unused)] -prelude!(); +use rune::alloc::prelude::*; +use rune::runtime::{Mut, Object, OwnedTuple, Ref, Tuple}; +use rune::{Any, FromValue, ToValue}; #[derive(Any)] struct Custom {} @@ -15,7 +12,7 @@ struct TestUnit; #[derive(FromValue)] struct TestNamed { - a: Mut, + a: Mut, b: Mut, c: Mut, d: Ref, @@ -23,11 +20,11 @@ struct TestNamed { } #[derive(FromValue)] -struct TestUnnamed(Mut, Mut); +struct TestUnnamed(Mut, Mut); #[derive(ToValue)] struct Test2 { - a: alloc::String, + a: String, b: OwnedTuple, c: Object, d: Custom, @@ -35,20 +32,20 @@ struct Test2 { } #[derive(ToValue)] -struct Test2Unnamed(alloc::String, Custom); +struct Test2Unnamed(String, Custom); #[derive(FromValue)] enum TestEnum { - TestUnit, - TestNamed { - a: Mut, + Unit, + Named { + a: Mut, b: Mut, c: Mut, d: Ref, e: Mut, }, - TestUnnamed( - Mut, + Unnamed( + Mut, Mut, Mut, Ref,