From 0d1d8c391fb607cc9c2777f540d0895ab802c6a4 Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Sat, 27 Jul 2024 13:34:38 +0200 Subject: [PATCH] rune: Switch to storing values inline --- benches/Cargo.toml | 7 +- benches/benches/comparison.rs | 74 + benches/benches/comparisons/primes.rs | 77 + benches/benches/{bench_main.rs => main.rs} | 0 benches/benches/primes.rn | 33 - crates/rune-alloc/src/hashbrown/map.rs | 2 +- crates/rune-alloc/src/slice/iter.rs | 2 +- crates/rune-macros/src/from_value.rs | 4 +- crates/rune/src/cli/benches.rs | 10 +- crates/rune/src/cli/tests.rs | 8 +- crates/rune/src/compile/context_error.rs | 8 +- crates/rune/src/compile/error.rs | 7 +- crates/rune/src/compile/ir.rs | 8 +- crates/rune/src/compile/ir/compiler.rs | 30 +- crates/rune/src/compile/ir/eval.rs | 204 +- crates/rune/src/compile/ir/interpreter.rs | 164 +- crates/rune/src/compile/v1/assemble.rs | 2 +- crates/rune/src/hir/lowering.rs | 4 +- crates/rune/src/macros/format_args.rs | 10 +- crates/rune/src/modules/char.rs | 2 +- crates/rune/src/modules/clone.rs | 5 +- crates/rune/src/modules/future.rs | 55 +- crates/rune/src/modules/iter.rs | 25 +- crates/rune/src/modules/mem.rs | 88 +- crates/rune/src/modules/string.rs | 141 +- crates/rune/src/modules/vec.rs | 30 +- crates/rune/src/runtime/access.rs | 15 +- crates/rune/src/runtime/budget.rs | 6 +- crates/rune/src/runtime/bytes.rs | 13 +- crates/rune/src/runtime/const_value.rs | 143 +- crates/rune/src/runtime/format.rs | 130 +- crates/rune/src/runtime/from_value.rs | 139 +- crates/rune/src/runtime/function.rs | 14 +- crates/rune/src/runtime/inst.rs | 33 +- crates/rune/src/runtime/mod.rs | 5 +- crates/rune/src/runtime/range.rs | 17 +- crates/rune/src/runtime/range_from.rs | 21 +- crates/rune/src/runtime/range_inclusive.rs | 17 +- crates/rune/src/runtime/stack.rs | 31 + crates/rune/src/runtime/to_value.rs | 2 +- crates/rune/src/runtime/tuple.rs | 77 +- crates/rune/src/runtime/value.rs | 2350 +++++++++----------- crates/rune/src/runtime/value/data.rs | 145 ++ crates/rune/src/runtime/value/macros.rs | 317 +++ crates/rune/src/runtime/value/rtti.rs | 80 + crates/rune/src/runtime/value/serde.rs | 170 +- crates/rune/src/runtime/vec.rs | 64 +- crates/rune/src/runtime/vm.rs | 1212 ++++++---- crates/rune/src/runtime/vm_execution.rs | 6 +- crates/rune/src/tests.rs | 4 +- crates/rune/src/tests/external_ops.rs | 4 +- crates/rune/src/tests/getter_setter.rs | 2 +- crates/rune/src/tests/unit_constants.rs | 6 +- crates/rune/src/tests/vm_function.rs | 13 +- crates/rune/src/workspace/spanned_value.rs | 6 +- 55 files changed, 3643 insertions(+), 2399 deletions(-) create mode 100644 benches/benches/comparison.rs create mode 100644 benches/benches/comparisons/primes.rs rename benches/benches/{bench_main.rs => main.rs} (100%) delete mode 100644 benches/benches/primes.rn create mode 100644 crates/rune/src/runtime/value/data.rs create mode 100644 crates/rune/src/runtime/value/macros.rs create mode 100644 crates/rune/src/runtime/value/rtti.rs diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 89ee6c5af..3b6bffa95 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -5,6 +5,7 @@ publish = false [dependencies] rune = { path = "../crates/rune", features = ["bench", "capture-io"] } +rhai = "1.19.0" tokio = { version = "1.28.1", features = ["macros"] } criterion = "0.4.0" @@ -12,5 +13,9 @@ anyhow = "1.0.71" futures-executor = "0.3.28" [[bench]] -name = "bench_main" +name = "main" +harness = false + +[[bench]] +name = "comparison" harness = false diff --git a/benches/benches/comparison.rs b/benches/benches/comparison.rs new file mode 100644 index 000000000..89b01e4a5 --- /dev/null +++ b/benches/benches/comparison.rs @@ -0,0 +1,74 @@ +use rune::{BuildError, Context, Diagnostics, Options, Source, Sources, Vm}; +use std::any::Any; +use std::sync::Arc; + +pub(crate) fn vm( + context: &Context, + sources: &mut Sources, + diagnostics: &mut Diagnostics, +) -> Result { + let mut options = Options::default(); + options + .parse_option("function-body=true") + .expect("failed to parse option"); + + let unit = rune::prepare(sources) + .with_context(context) + .with_diagnostics(diagnostics) + .with_options(&options) + .build()?; + + let context = Arc::new(context.runtime()?); + Ok(Vm::new(context, Arc::new(unit))) +} + +pub(crate) fn sources(source: &str) -> Sources { + let mut sources = Sources::new(); + + sources + .insert(Source::new("main", source).expect("Failed to construct source")) + .expect("Failed to insert source"); + + sources +} + +macro_rules! rune_vm { + ($($tt:tt)*) => {{ + let context = rune::Context::with_default_modules().expect("Failed to build context"); + let mut diagnostics = Default::default(); + let mut sources = $crate::sources(stringify!($($tt)*)); + $crate::vm(&context, &mut sources, &mut diagnostics).expect("Program to compile successfully") + }}; +} + +macro_rules! rhai_ast { + ($($tt:tt)*) => {{ + let mut engine = $crate::rhai::Engine::new(); + engine.set_optimization_level($crate::rhai::OptimizationLevel::Full); + let ast = engine.compile(stringify!($($tt)*)).unwrap(); + $crate::RhaiRunner { engine, ast } + }}; +} + +pub(crate) struct RhaiRunner { + pub(crate) engine: crate::rhai::Engine, + pub(crate) ast: crate::rhai::AST, +} + +impl RhaiRunner { + fn eval(&self) -> T { + self.engine.eval_ast(&self.ast).unwrap() + } +} + +pub(crate) mod rhai { + pub(crate) use ::rhai::{Engine, OptimizationLevel, AST}; +} + +mod comparisons { + pub mod primes; +} + +criterion::criterion_main! { + comparisons::primes::benches, +} diff --git a/benches/benches/comparisons/primes.rs b/benches/benches/comparisons/primes.rs new file mode 100644 index 000000000..8ba4454c9 --- /dev/null +++ b/benches/benches/comparisons/primes.rs @@ -0,0 +1,77 @@ +use criterion::Criterion; +use rune::Hash; + +criterion::criterion_group!(benches, entry); + +fn entry(b: &mut Criterion) { + let mut group = b.benchmark_group("primes"); + + group.bench_function("rhai", |b| { + let ast = rhai_ast! { + const MAX_NUMBER_TO_CHECK = 10_000; + + let prime_mask = []; + prime_mask.pad(MAX_NUMBER_TO_CHECK, true); + + prime_mask[0] = false; + prime_mask[1] = false; + + let total_primes_found = 0; + + for p in 2..MAX_NUMBER_TO_CHECK { + if prime_mask[p] { + total_primes_found += 1; + let i = 2 * p; + + while i < MAX_NUMBER_TO_CHECK { + prime_mask[i] = false; + i += p; + } + } + } + + total_primes_found + }; + + b.iter(|| { + let value = ast.eval::(); + assert_eq!(value, 1229); + value + }); + }); + + group.bench_function("rune", |b| { + let mut vm = rune_vm! { + const MAX_NUMBER_TO_CHECK = 10_000; + + let prime_mask = []; + prime_mask.resize(MAX_NUMBER_TO_CHECK, true); + + prime_mask[0] = false; + prime_mask[1] = false; + + let total_primes_found = 0; + + for p in 2..MAX_NUMBER_TO_CHECK { + if prime_mask[p] { + total_primes_found += 1; + let i = 2 * p; + + while i < MAX_NUMBER_TO_CHECK { + prime_mask[i] = false; + i += p; + } + } + } + + total_primes_found + }; + + b.iter(|| { + let value = vm.call(Hash::EMPTY, ()).unwrap(); + let value: i64 = rune::from_value(value).unwrap(); + assert_eq!(value, 1229); + value + }) + }); +} diff --git a/benches/benches/bench_main.rs b/benches/benches/main.rs similarity index 100% rename from benches/benches/bench_main.rs rename to benches/benches/main.rs diff --git a/benches/benches/primes.rn b/benches/benches/primes.rn deleted file mode 100644 index da5500a9f..000000000 --- a/benches/benches/primes.rn +++ /dev/null @@ -1,33 +0,0 @@ -const MAX_NUMBER_TO_CHECK = 1000; - -/// Find prime numbers. -#[bench] -fn find_primes(b) { - let prime_mask = []; - - for n in 0..MAX_NUMBER_TO_CHECK { - prime_mask.push(true); - } - - prime_mask[0] = false; - prime_mask[1] = false; - - b.iter(|| { - let prime_mask = prime_mask.clone(); - let total_primes_found = 0; - - for p in 2..MAX_NUMBER_TO_CHECK { - if prime_mask[p] { - total_primes_found += 1; - let i = 2 * p; - - while i < MAX_NUMBER_TO_CHECK { - prime_mask[i] = false; - i += p; - } - } - } - - total_primes_found - }); -} diff --git a/crates/rune-alloc/src/hashbrown/map.rs b/crates/rune-alloc/src/hashbrown/map.rs index 39d18f909..41d7bad3c 100644 --- a/crates/rune-alloc/src/hashbrown/map.rs +++ b/crates/rune-alloc/src/hashbrown/map.rs @@ -2163,7 +2163,7 @@ impl HashMap { /// Unless you are in such a situation, higher-level and more foolproof APIs like /// `get` should be preferred. /// - /// Immutable raw entries have very limited use; you might instead want `raw_entry_mut`. + /// Inline raw entries have very limited use; you might instead want `raw_entry_mut`. /// /// # Examples /// diff --git a/crates/rune-alloc/src/slice/iter.rs b/crates/rune-alloc/src/slice/iter.rs index b78e3a981..8df62b4ac 100644 --- a/crates/rune-alloc/src/slice/iter.rs +++ b/crates/rune-alloc/src/slice/iter.rs @@ -13,7 +13,7 @@ use crate::alloc::SizedTypeProperties; use crate::hint::assume; use crate::ptr::{self, invalid, invalid_mut, NonNull}; -/// Immutable slice iterator +/// Inline slice iterator /// /// This struct is created by the [`iter`] method on [slices]. /// diff --git a/crates/rune-macros/src/from_value.rs b/crates/rune-macros/src/from_value.rs index c9528ef12..4ce4f9a38 100644 --- a/crates/rune-macros/src/from_value.rs +++ b/crates/rune-macros/src/from_value.rs @@ -30,7 +30,7 @@ impl Expander { let (expanded, expected) = match &st.fields { syn::Fields::Unit => { let expanded = quote! { - #type_value::EmptyTuple => { + #type_value::Unit => { #vm_result::Ok(Self) } #type_value::EmptyStruct(..) => { @@ -44,7 +44,7 @@ impl Expander { let expanded = self.expand_unnamed(f)?; let expanded = quote! { - #type_value::EmptyTuple => { + #type_value::Unit => { let tuple = #tuple::new(&[]); #vm_result::Ok(Self(#expanded)) } diff --git a/crates/rune/src/cli/benches.rs b/crates/rune/src/cli/benches.rs index e033ea423..6425dc86a 100644 --- a/crates/rune/src/cli/benches.rs +++ b/crates/rune/src/cli/benches.rs @@ -193,6 +193,14 @@ struct Time(u128); impl fmt::Display for Time { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}ns", self.0) + if self.0 >= 1_000_000_000 { + write!(f, "{:.3}s", self.0 as f64 / 1_000_000_000.0) + } else if self.0 >= 1_000_000 { + write!(f, "{:.3}ms", self.0 as f64 / 1_000_000.0) + } else if self.0 >= 1_000 { + write!(f, "{:.3}µs", self.0 as f64 / 1_000.0) + } else { + write!(f, "{}ns", self.0) + } } } diff --git a/crates/rune/src/cli/tests.rs b/crates/rune/src/cli/tests.rs index 0a6878012..aa35c7e7e 100644 --- a/crates/rune/src/cli/tests.rs +++ b/crates/rune/src/cli/tests.rs @@ -16,7 +16,7 @@ use crate::cli::{ use crate::compile::FileSourceLoader; use crate::doc::TestParams; use crate::modules::capture_io::CaptureIo; -use crate::runtime::{Value, ValueKind, Vm, VmError, VmResult}; +use crate::runtime::{Mutable, OwnedValue, Value, Vm, VmError, VmResult}; use crate::termcolor::{Color, ColorSpec, WriteColor}; use crate::{Diagnostics, Hash, Item, ItemBuf, Source, Sources, Unit}; @@ -458,12 +458,12 @@ impl TestCase { capture_io.drain_into(&mut self.output)?; self.outcome = match result { - VmResult::Ok(v) => match v.take_kind()? { - ValueKind::Result(result) => match result { + VmResult::Ok(v) => match v.take_value()? { + OwnedValue::Mutable(Mutable::Result(result)) => match result { Ok(..) => Outcome::Ok, Err(error) => Outcome::Err(error), }, - ValueKind::Option(option) => match option { + OwnedValue::Mutable(Mutable::Option(option)) => match option { Some(..) => Outcome::Ok, None => Outcome::None, }, diff --git a/crates/rune/src/compile/context_error.rs b/crates/rune/src/compile/context_error.rs index e1644c00c..0fa0bb1e3 100644 --- a/crates/rune/src/compile/context_error.rs +++ b/crates/rune/src/compile/context_error.rs @@ -339,7 +339,13 @@ impl fmt::Display for ContextError { hash, item_hash, } => { - write!(f,"Type hash mismatch for `{type_info}`, from module is `{hash}` while from item `{item}` is `{item_hash}`. A possibility is that it has the wrong #[rune(item = ..)] setting.")?; + let expected = item.parent().unwrap_or_default(); + + write! { + f, + "Type hash mismatch for `{type_info}`, from module is `{hash}` while from item `{item}` is `{item_hash}`.\n\ + You might not have the #[rune(item = {expected})] attribute set." + }?; } ContextError::StaticTypeHashMismatch { type_info, diff --git a/crates/rune/src/compile/error.rs b/crates/rune/src/compile/error.rs index 5cac54bf8..1511692e2 100644 --- a/crates/rune/src/compile/error.rs +++ b/crates/rune/src/compile/error.rs @@ -17,7 +17,7 @@ use crate::parse::{Expectation, IntoExpectation, LexerMode}; use crate::query::MissingId; use crate::runtime::debug::DebugSignature; use crate::runtime::unit::EncodeError; -use crate::runtime::{AccessError, RuntimeError, TypeInfo, TypeOf, ValueKind, VmError}; +use crate::runtime::{AccessError, RuntimeError, TypeInfo, TypeOf, VmError}; use crate::shared::CapacityError; #[cfg(feature = "std")] use crate::source; @@ -191,16 +191,15 @@ impl Error { } /// An error raised when we expect a certain constant value but get another. - pub(crate) fn expected_type(spanned: S, actual: &ValueKind) -> Self + pub(crate) fn expected_type(spanned: impl Spanned, actual: TypeInfo) -> Self where - S: Spanned, E: TypeOf, { Self::new( spanned, IrErrorKind::Expected { expected: E::type_info(), - actual: actual.type_info(), + actual, }, ) } diff --git a/crates/rune/src/compile/ir.rs b/crates/rune/src/compile/ir.rs index 00825dd50..30424d8ad 100644 --- a/crates/rune/src/compile/ir.rs +++ b/crates/rune/src/compile/ir.rs @@ -21,7 +21,7 @@ use crate::indexing::index; use crate::macros::MacroContext; use crate::parse::NonZeroId; use crate::query::Used; -use crate::runtime::{Value, ValueKind}; +use crate::runtime::{Inline, Value}; pub(crate) use self::compiler::Ctxt; pub(crate) use self::eval::{eval_ir, EvalOutcome}; @@ -537,9 +537,9 @@ impl IrAssignOp { where S: Copy + Spanned, { - if let ValueKind::Integer(target) = &mut *target.borrow_kind_mut().with_span(spanned)? { - if let ValueKind::Integer(operand) = *operand.borrow_kind_ref().with_span(spanned)? { - return self.assign_int(spanned, target, operand); + if let Some(Inline::Integer(target)) = target.as_inline_mut().with_span(spanned)? { + if let Some(Inline::Integer(operand)) = operand.as_inline().with_span(spanned)? { + return self.assign_int(spanned, target, *operand); } } diff --git a/crates/rune/src/compile/ir/compiler.rs b/crates/rune/src/compile/ir/compiler.rs index f9252eb43..8984ef8e7 100644 --- a/crates/rune/src/compile/ir/compiler.rs +++ b/crates/rune/src/compile/ir/compiler.rs @@ -50,7 +50,7 @@ pub(crate) fn expr(hir: &hir::Expr<'_>, c: &mut Ctxt<'_, '_>) -> compile::Result )); }; - let value = value.as_value().with_span(span)?; + let value = value.to_value().with_span(span)?; ir::Ir::new(span, value) } hir::ExprKind::Variable(name) => { @@ -204,24 +204,28 @@ fn expr_binary( fn lit(c: &mut Ctxt<'_, '_>, span: Span, hir: hir::Lit<'_>) -> compile::Result { Ok(match hir { hir::Lit::Bool(boolean) => { - let value = Value::try_from(boolean).with_span(span)?; - ir::Ir::new(span, value) - } - hir::Lit::Str(string) => { - let string = string.try_to_owned().with_span(span)?; - let value = Value::try_from(string).with_span(span)?; + let value = Value::from(boolean); ir::Ir::new(span, value) } hir::Lit::Integer(n) => { - let value = Value::try_from(n).with_span(span)?; + let value = Value::from(n); ir::Ir::new(span, value) } hir::Lit::Float(n) => { - let value = Value::try_from(n).with_span(span)?; + let value = Value::from(n); ir::Ir::new(span, value) } hir::Lit::Byte(b) => { - let value = Value::try_from(b).with_span(span)?; + let value = Value::from(b); + ir::Ir::new(span, value) + } + hir::Lit::Char(c) => { + let value = Value::from(c); + ir::Ir::new(span, value) + } + hir::Lit::Str(string) => { + let string = string.try_to_owned().with_span(span)?; + let value = Value::try_from(string).with_span(span)?; ir::Ir::new(span, value) } hir::Lit::ByteStr(byte_str) => { @@ -229,17 +233,13 @@ fn lit(c: &mut Ctxt<'_, '_>, span: Span, hir: hir::Lit<'_>) -> compile::Result { - let value = Value::try_from(c).with_span(span)?; - ir::Ir::new(span, value) - } }) } #[instrument(span = span)] fn expr_tuple(c: &mut Ctxt<'_, '_>, span: Span, hir: &hir::ExprSeq<'_>) -> compile::Result { if hir.items.is_empty() { - let value = Value::unit().with_span(span)?; + let value = Value::unit(); return Ok(ir::Ir::new(span, value)); } diff --git a/crates/rune/src/compile/ir/eval.rs b/crates/rune/src/compile/ir/eval.rs index 6a124bc7a..07510d8b1 100644 --- a/crates/rune/src/compile/ir/eval.rs +++ b/crates/rune/src/compile/ir/eval.rs @@ -7,7 +7,7 @@ use crate::ast::{Span, Spanned}; use crate::compile::ir::{self}; use crate::compile::{self, WithSpan}; use crate::query::Used; -use crate::runtime::{Object, OwnedTuple, Value, ValueKind}; +use crate::runtime::{Inline, Mutable, Object, OwnedTuple, Value, ValueBorrowRef}; /// The outcome of a constant evaluation. pub enum EvalOutcome { @@ -50,7 +50,7 @@ fn eval_ir_assign( .scopes .mut_target(&ir.target, move |t| ir.op.assign(ir, t, value))?; - Ok(Value::unit().with_span(ir)?) + Ok(Value::unit()) } fn eval_ir_binary( @@ -71,76 +71,91 @@ fn eval_ir_binary( let a = eval_ir(&ir.lhs, interp, used)?; let b = eval_ir(&ir.rhs, interp, used)?; - let a = a.borrow_kind_ref().with_span(ir)?; - let b = b.borrow_kind_ref().with_span(ir)?; - - let kind = 'out: { - match (&*a, &*b) { - (ValueKind::Integer(a), ValueKind::Integer(b)) => match ir.op { - ir::IrBinaryOp::Add => { - break 'out ValueKind::Integer(a.add(b)); - } - ir::IrBinaryOp::Sub => { - break 'out ValueKind::Integer(a.sub(b)); - } - ir::IrBinaryOp::Mul => { - break 'out ValueKind::Integer(a.mul(b)); - } - ir::IrBinaryOp::Div => { - let number = a - .checked_div(*b) - .ok_or_else(|| compile::Error::msg(span, "division by zero"))?; - break 'out ValueKind::Integer(number); + let a = a.borrow_ref().with_span(ir)?; + let b = b.borrow_ref().with_span(ir)?; + + match (a, b) { + (ValueBorrowRef::Inline(a), ValueBorrowRef::Inline(b)) => { + let out = 'out: { + match (a, b) { + (Inline::Integer(a), Inline::Integer(b)) => match ir.op { + ir::IrBinaryOp::Add => { + break 'out Inline::Integer(a.add(b)); + } + ir::IrBinaryOp::Sub => { + break 'out Inline::Integer(a.sub(b)); + } + ir::IrBinaryOp::Mul => { + break 'out Inline::Integer(a.mul(b)); + } + ir::IrBinaryOp::Div => { + let number = a + .checked_div(*b) + .ok_or_else(|| compile::Error::msg(span, "division by zero"))?; + break 'out Inline::Integer(number); + } + ir::IrBinaryOp::Shl => { + let b = u32::try_from(*b).map_err(|_| { + compile::Error::msg(&ir.rhs, "cannot be converted to shift operand") + })?; + + let n = a.shl(b); + break 'out Inline::Integer(n); + } + ir::IrBinaryOp::Shr => { + let b = u32::try_from(*b).map_err(|_| { + compile::Error::msg(&ir.rhs, "cannot be converted to shift operand") + })?; + + let n = a.shr(b); + break 'out Inline::Integer(n); + } + ir::IrBinaryOp::Lt => break 'out Inline::Bool(a < b), + ir::IrBinaryOp::Lte => break 'out Inline::Bool(a <= b), + ir::IrBinaryOp::Eq => break 'out Inline::Bool(a == b), + ir::IrBinaryOp::Gt => break 'out Inline::Bool(a > b), + ir::IrBinaryOp::Gte => break 'out Inline::Bool(a >= b), + }, + (Inline::Float(a), Inline::Float(b)) => { + #[allow(clippy::float_cmp)] + match ir.op { + ir::IrBinaryOp::Add => break 'out Inline::Float(a + b), + ir::IrBinaryOp::Sub => break 'out Inline::Float(a - b), + ir::IrBinaryOp::Mul => break 'out Inline::Float(a * b), + ir::IrBinaryOp::Div => break 'out Inline::Float(a / b), + ir::IrBinaryOp::Lt => break 'out Inline::Bool(a < b), + ir::IrBinaryOp::Lte => break 'out Inline::Bool(a <= b), + ir::IrBinaryOp::Eq => break 'out Inline::Bool(a == b), + ir::IrBinaryOp::Gt => break 'out Inline::Bool(a > b), + ir::IrBinaryOp::Gte => break 'out Inline::Bool(a >= b), + _ => (), + }; + } + _ => {} } - ir::IrBinaryOp::Shl => { - let b = u32::try_from(*b).map_err(|_| { - compile::Error::msg(&ir.rhs, "cannot be converted to shift operand") - })?; - let n = a.shl(b); - break 'out ValueKind::Integer(n); - } - ir::IrBinaryOp::Shr => { - let b = u32::try_from(*b).map_err(|_| { - compile::Error::msg(&ir.rhs, "cannot be converted to shift operand") - })?; + return Err(EvalOutcome::not_const(span)); + }; - let n = a.shr(b); - break 'out ValueKind::Integer(n); - } - ir::IrBinaryOp::Lt => break 'out ValueKind::Bool(a < b), - ir::IrBinaryOp::Lte => break 'out ValueKind::Bool(a <= b), - ir::IrBinaryOp::Eq => break 'out ValueKind::Bool(a == b), - ir::IrBinaryOp::Gt => break 'out ValueKind::Bool(a > b), - ir::IrBinaryOp::Gte => break 'out ValueKind::Bool(a >= b), - }, - (ValueKind::Float(a), ValueKind::Float(b)) => { - #[allow(clippy::float_cmp)] - match ir.op { - ir::IrBinaryOp::Add => break 'out ValueKind::Float(a + b), - ir::IrBinaryOp::Sub => break 'out ValueKind::Float(a - b), - ir::IrBinaryOp::Mul => break 'out ValueKind::Float(a * b), - ir::IrBinaryOp::Div => break 'out ValueKind::Float(a / b), - ir::IrBinaryOp::Lt => break 'out ValueKind::Bool(a < b), - ir::IrBinaryOp::Lte => break 'out ValueKind::Bool(a <= b), - ir::IrBinaryOp::Eq => break 'out ValueKind::Bool(a == b), - ir::IrBinaryOp::Gt => break 'out ValueKind::Bool(a > b), - ir::IrBinaryOp::Gte => break 'out ValueKind::Bool(a >= b), - _ => (), - }; - } - (ValueKind::String(a), ValueKind::String(b)) => { - if let ir::IrBinaryOp::Add = ir.op { - break 'out ValueKind::String(add_strings(a, b).with_span(span)?); - } - } - _ => (), + 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)?); + } + } - return Err(EvalOutcome::not_const(span)); - }; + return Err(EvalOutcome::not_const(span)); + }; + + return Ok(Value::try_from(out).with_span(span)?); + } + _ => (), + } - Ok(Value::try_from(kind).with_span(span)?) + Err(EvalOutcome::not_const(span)) } fn eval_ir_branches( @@ -170,7 +185,7 @@ fn eval_ir_branches( return eval_ir_scope(branch, interp, used); } - Ok(Value::unit().with_span(ir)?) + Ok(Value::unit()) } fn eval_ir_call( @@ -203,7 +218,7 @@ fn eval_ir_condition( } }; - Ok(Value::try_from(value).with_span(ir)?) + Ok(Value::from(value)) } fn eval_ir_decl( @@ -214,7 +229,7 @@ fn eval_ir_decl( interp.budget.take(ir)?; let value = eval_ir(&ir.value, interp, used)?; interp.scopes.decl(&ir.name, value).with_span(ir)?; - Ok(Value::unit().with_span(ir)?) + Ok(Value::unit()) } fn eval_ir_loop( @@ -265,7 +280,7 @@ fn eval_ir_loop( Ok(value) } else { - Ok(Value::unit().with_span(ir)?) + Ok(Value::unit()) } } @@ -299,7 +314,7 @@ fn eval_ir_scope( let value = if let Some(last) = &ir.last { eval_ir(last, interp, used)? } else { - Value::unit().with_span(ir)? + Value::unit() }; interp.scopes.pop(guard).with_span(ir)?; @@ -314,7 +329,7 @@ fn eval_ir_set( interp.budget.take(ir)?; let value = eval_ir(&ir.value, interp, used)?; interp.scopes.set_target(&ir.target, value)?; - Ok(Value::unit().with_span(ir)?) + Ok(Value::unit()) } fn eval_ir_template( @@ -333,25 +348,32 @@ fn eval_ir_template( } ir::IrTemplateComponent::Ir(ir) => { let const_value = eval_ir(ir, interp, used)?; - let kind = const_value.borrow_kind_ref().with_span(ir)?; - - match &*kind { - ValueKind::Integer(integer) => { - write!(buf, "{integer}")?; - } - ValueKind::Float(float) => { - let mut buffer = ryu::Buffer::new(); - buf.try_push_str(buffer.format(*float))?; - } - ValueKind::Bool(b) => { - write!(buf, "{b}")?; - } - ValueKind::String(s) => { - buf.try_push_str(s)?; - } - _ => { - return Err(EvalOutcome::not_const(ir)); - } + let value = const_value.borrow_ref().with_span(ir)?; + + match value { + ValueBorrowRef::Inline(value) => match value { + Inline::Integer(integer) => { + write!(buf, "{integer}")?; + } + Inline::Float(float) => { + let mut buffer = ryu::Buffer::new(); + buf.try_push_str(buffer.format(*float))?; + } + Inline::Bool(b) => { + write!(buf, "{b}")?; + } + _ => { + return Err(EvalOutcome::not_const(ir)); + } + }, + ValueBorrowRef::Mutable(value) => match &*value { + Mutable::String(s) => { + buf.try_push_str(s)?; + } + _ => { + return Err(EvalOutcome::not_const(ir)); + } + }, } } } diff --git a/crates/rune/src/compile/ir/interpreter.rs b/crates/rune/src/compile/ir/interpreter.rs index 14c2b9241..4a3b8967b 100644 --- a/crates/rune/src/compile/ir/interpreter.rs +++ b/crates/rune/src/compile/ir/interpreter.rs @@ -8,7 +8,7 @@ use crate::compile::{self, IrErrorKind, ItemId, ModId, WithSpan}; use crate::hir; use crate::parse::NonZeroId; use crate::query::{Query, Used}; -use crate::runtime::{ConstValue, Object, OwnedTuple, Value, ValueKind}; +use crate::runtime::{ConstValue, Mutable, Object, OwnedTuple, Value, ValueBorrowRef, ValueRef}; /// The interpreter that executed [Ir][crate::ir::Ir]. pub struct Interpreter<'a, 'arena> { @@ -106,7 +106,7 @@ impl Interpreter<'_, '_> { .alloc_item(base.extended(name.try_to_string()?)?)?; if let Some(const_value) = self.q.consts.get(item) { - return Ok(const_value.as_value().with_span(span)?); + return Ok(const_value.to_value().with_span(span)?); } if let Some(meta) = self.q.query_meta(span, item, used)? { @@ -119,7 +119,7 @@ impl Interpreter<'_, '_> { )); }; - return Ok(const_value.as_value().with_span(span)?); + return Ok(const_value.to_value().with_span(span)?); } _ => { return Err(compile::Error::new( @@ -191,24 +191,36 @@ impl Interpreter<'_, '_> { impl ir::Scopes { /// Get the given target as mut. - pub(crate) fn get_target(&mut self, ir_target: &ir::IrTarget) -> compile::Result { + pub(crate) fn get_target(&self, ir_target: &ir::IrTarget) -> compile::Result { match &ir_target.kind { - ir::IrTargetKind::Name(name) => Ok(self.get_name(name, ir_target)?.try_clone()?), + 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().with_span(ir_target)?; - match &*value.borrow_kind_ref().with_span(ir_target)? { - ValueKind::Object(object) => { - if let Some(value) = object.get(field.as_ref()).try_cloned()? { - return Ok(value); + let value = match value { + ValueBorrowRef::Mutable(value) => value, + actual => { + return Err(compile::Error::expected_type::( + ir_target, + actual.type_info(), + )); + } + }; + + 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::<_, OwnedTuple>( - ir_target, actual, + return Err(compile::Error::expected_type::( + ir_target, + actual.type_info(), )) } - }; + } Err(compile::Error::new( ir_target, @@ -219,24 +231,36 @@ impl ir::Scopes { } ir::IrTargetKind::Index(target, index) => { let value = self.get_target(target)?; - - match &*value.borrow_kind_ref().with_span(ir_target)? { - ValueKind::Vec(vec) => { - if let Some(value) = vec.get(*index).try_cloned()? { - return Ok(value); - } - } - ValueKind::Tuple(tuple) => { - if let Some(value) = tuple.get(*index).try_cloned()? { - return Ok(value); - } + let target = value.borrow_ref().with_span(ir_target)?; + + match target { + ValueBorrowRef::Mutable(value) => { + match &*value { + Mutable::Vec(vec) => { + if let Some(value) = vec.get(*index) { + return Ok(value.clone()); + } + } + Mutable::Tuple(tuple) => { + if let Some(value) = tuple.get(*index) { + return Ok(value.clone()); + } + } + actual => { + return Err(compile::Error::expected_type::( + ir_target, + actual.type_info(), + )) + } + }; } actual => { - return Err(compile::Error::expected_type::<_, OwnedTuple>( - ir_target, actual, + return Err(compile::Error::expected_type::( + ir_target, + actual.type_info(), )) } - }; + } Err(compile::Error::new( ir_target, @@ -258,16 +282,27 @@ impl ir::Scopes { Ok(()) } ir::IrTargetKind::Field(target, field) => { - let current = self.get_target(target)?; + let target = self.get_target(target)?; + + let mut target = match target.value_ref().with_span(ir_target)? { + ValueRef::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 *current.borrow_kind_mut().with_span(ir_target)? { - ValueKind::Object(object) => { + 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::<_, Object>( - ir_target, actual, + return Err(compile::Error::expected_type::( + ir_target, + actual.type_info(), )); } } @@ -275,24 +310,35 @@ impl ir::Scopes { Ok(()) } ir::IrTargetKind::Index(target, index) => { - let current = self.get_target(target)?; + let target = self.get_target(target)?; + + let mut target = match target.value_ref().with_span(ir_target)? { + ValueRef::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 *current.borrow_kind_mut().with_span(ir_target)? { - ValueKind::Vec(vec) => { + match &mut *target { + Mutable::Vec(vec) => { if let Some(current) = vec.get_mut(*index) { *current = value; return Ok(()); } } - ValueKind::Tuple(tuple) => { + Mutable::Tuple(tuple) => { if let Some(current) = tuple.get_mut(*index) { *current = value; return Ok(()); } } actual => { - return Err(compile::Error::expected_type::<_, OwnedTuple>( - ir_target, actual, + return Err(compile::Error::expected_type::( + ir_target, + actual.type_info(), )); } }; @@ -314,11 +360,20 @@ impl ir::Scopes { op(value) } ir::IrTargetKind::Field(target, field) => { - let current = self.get_target(target)?; - let mut kind = current.borrow_kind_mut().with_span(ir_target)?; + let value = self.get_target(target)?; - match &mut *kind { - ValueKind::Object(object) => { + let mut value = match value.value_ref().with_span(ir_target)? { + ValueRef::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)?, + )) + } + }; + + match &mut *value { + Mutable::Object(object) => { let Some(value) = object.get_mut(field.as_ref()) else { return Err(compile::Error::new( ir_target, @@ -330,17 +385,27 @@ impl ir::Scopes { op(value) } - actual => Err(compile::Error::expected_type::<_, Object>( - ir_target, actual, + actual => Err(compile::Error::expected_type::( + ir_target, + actual.type_info(), )), } } ir::IrTargetKind::Index(target, index) => { let current = self.get_target(target)?; - let mut kind = current.borrow_kind_mut().with_span(ir_target)?; - match &mut *kind { - ValueKind::Vec(vec) => { + let mut value = match current.value_ref().with_span(ir_target)? { + ValueRef::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)?, + )); + } + }; + + match &mut *value { + Mutable::Vec(vec) => { let value = vec.get_mut(*index).ok_or_else(|| { compile::Error::new( ir_target, @@ -350,7 +415,7 @@ impl ir::Scopes { op(value) } - ValueKind::Tuple(tuple) => { + Mutable::Tuple(tuple) => { let value = tuple.get_mut(*index).ok_or_else(|| { compile::Error::new( ir_target, @@ -360,8 +425,9 @@ impl ir::Scopes { op(value) } - actual => Err(compile::Error::expected_type::<_, OwnedTuple>( - ir_target, actual, + actual => Err(compile::Error::expected_type::( + ir_target, + actual.type_info(), )), } } diff --git a/crates/rune/src/compile/v1/assemble.rs b/crates/rune/src/compile/v1/assemble.rs index d50222dd8..39ee90384 100644 --- a/crates/rune/src/compile/v1/assemble.rs +++ b/crates/rune/src/compile/v1/assemble.rs @@ -1104,7 +1104,7 @@ fn const_<'a, 'hir>( let out = addr.output(); match *value { - ConstValue::EmptyTuple => { + ConstValue::Unit => { cx.asm.push(Inst::unit(out), span)?; } ConstValue::Byte(v) => { diff --git a/crates/rune/src/hir/lowering.rs b/crates/rune/src/hir/lowering.rs index d4d8413cf..0eef1adf4 100644 --- a/crates/rune/src/hir/lowering.rs +++ b/crates/rune/src/hir/lowering.rs @@ -799,10 +799,10 @@ pub(crate) fn pat_const_value<'hir>( items, })); } - ConstValue::EmptyTuple => { + ConstValue::Unit => { break 'kind hir::PatKind::Sequence(alloc!(hir::PatSequence { kind: hir::PatSequenceKind::Anonymous { - type_check: TypeCheck::EmptyTuple, + type_check: TypeCheck::Unit, count: 0, is_open: false, }, diff --git a/crates/rune/src/macros/format_args.rs b/crates/rune/src/macros/format_args.rs index 989feb564..e321f19cc 100644 --- a/crates/rune/src/macros/format_args.rs +++ b/crates/rune/src/macros/format_args.rs @@ -8,7 +8,7 @@ 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::ValueKind; +use crate::runtime::{Inline, Mutable, OwnedValue}; /// A format specification: A format string followed by arguments to be /// formatted in accordance with that string. @@ -49,7 +49,9 @@ impl FormatArgs { } } - let ValueKind::String(format) = format.take_kind().with_span(&self.format)? else { + 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", @@ -578,8 +580,8 @@ fn expand_format_spec<'a>( let value = cx.eval(expr)?; - let number = match *value.borrow_kind_ref().with_span(expr)? { - ValueKind::Integer(n) => n.to_usize(), + let number = match value.as_inline().with_span(expr)? { + Some(Inline::Integer(n)) => n.to_usize(), _ => None, }; diff --git a/crates/rune/src/modules/char.rs b/crates/rune/src/modules/char.rs index 54b7f1c54..54456fde1 100644 --- a/crates/rune/src/modules/char.rs +++ b/crates/rune/src/modules/char.rs @@ -45,7 +45,7 @@ fn from_i64(value: i64) -> VmResult> { return VmResult::Ok(None); }; - VmResult::Ok(Some(vm_try!(Value::try_from(c)))) + VmResult::Ok(Some(Value::from(c))) } } diff --git a/crates/rune/src/modules/clone.rs b/crates/rune/src/modules/clone.rs index 127df9071..df75b6486 100644 --- a/crates/rune/src/modules/clone.rs +++ b/crates/rune/src/modules/clone.rs @@ -52,7 +52,7 @@ pub fn module() -> Result { /// a += 1; /// /// assert_eq!(a, 43); - /// assert_eq!(b, 43); + /// assert_eq!(b, 42); /// assert_eq!(c, 42); /// ``` })?; @@ -70,8 +70,9 @@ pub fn module() -> Result { /// let c = clone(a); /// /// a += 1; +/// /// assert_eq!(a, 43); -/// assert_eq!(b, 43); +/// assert_eq!(b, 42); /// assert_eq!(c, 42); /// ``` #[rune::function] diff --git a/crates/rune/src/modules/future.rs b/crates/rune/src/modules/future.rs index 230a38dfc..46dabf24f 100644 --- a/crates/rune/src/modules/future.rs +++ b/crates/rune/src/modules/future.rs @@ -2,7 +2,10 @@ use crate as rune; use crate::alloc::Vec; -use crate::runtime::{Future, Mut, SelectFuture, Value, ValueKind, VmErrorKind, VmResult}; +use crate::runtime::{ + Future, Inline, Mut, Mutable, SelectFuture, Value, ValueBorrowRef, ValueRef, VmErrorKind, + VmResult, +}; use crate::{ContextError, Module}; /// Asynchronous computations. @@ -25,10 +28,18 @@ where let mut results = vm_try!(Vec::try_with_capacity(len)); for (index, value) in values.into_iter().enumerate() { - let value = vm_try!(value.clone().into_kind_mut()); + let value = match vm_try!(value.value_ref()) { + ValueRef::Mutable(value) => vm_try!(value.clone().into_mut()), + ValueRef::Inline(actual) => { + return VmResult::err([ + VmErrorKind::expected::(actual.type_info()), + VmErrorKind::bad_argument(index), + ]); + } + }; let future = Mut::try_map(value, |kind| match kind { - ValueKind::Future(future) => Some(future), + Mutable::Future(future) => Some(future), _ => None, }); @@ -84,20 +95,28 @@ where /// ``` #[rune::function] async fn join(value: Value) -> VmResult { - match &*vm_try!(value.borrow_kind_ref()) { - ValueKind::EmptyTuple => VmResult::Ok(vm_try!(Value::unit())), - ValueKind::Tuple(tuple) => VmResult::Ok(vm_try!( - try_join_impl(tuple.iter(), tuple.len(), |vec| VmResult::Ok(vm_try!( - Value::tuple(vec) - ))) - .await - )), - ValueKind::Vec(vec) => VmResult::Ok(vm_try!( - try_join_impl(vec.iter(), vec.len(), Value::vec).await - )), - _ => VmResult::err([ - VmErrorKind::bad_argument(0), - VmErrorKind::expected::(vm_try!(value.type_info())), - ]), + match vm_try!(value.borrow_ref()) { + ValueBorrowRef::Inline(value) => match value { + Inline::Unit => VmResult::Ok(Value::unit()), + value => VmResult::err([ + VmErrorKind::bad_argument(0), + VmErrorKind::expected::(value.type_info()), + ]), + }, + ValueBorrowRef::Mutable(value) => match *value { + Mutable::Tuple(ref tuple) => VmResult::Ok(vm_try!( + try_join_impl(tuple.iter(), tuple.len(), |vec| VmResult::Ok(vm_try!( + Value::tuple(vec) + ))) + .await + )), + Mutable::Vec(ref vec) => VmResult::Ok(vm_try!( + try_join_impl(vec.iter(), vec.len(), Value::vec).await + )), + ref value => VmResult::err([ + VmErrorKind::bad_argument(0), + VmErrorKind::expected::(value.type_info()), + ]), + }, } } diff --git a/crates/rune/src/modules/iter.rs b/crates/rune/src/modules/iter.rs index fc93ae84b..b491e3c9a 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::{ - CoreTypeOf, FromValue, Function, InstAddress, Object, Output, OwnedTuple, Protocol, Value, - ValueKind, Vec, VmErrorKind, VmResult, + CoreTypeOf, FromValue, Function, Inline, InstAddress, Mutable, Object, Output, OwnedTuple, + Protocol, Value, ValueBorrowRef, Vec, VmErrorKind, VmResult, }; use crate::shared::Caller; use crate::{Any, ContextError, Module, Params}; @@ -240,6 +240,8 @@ pub fn module() -> Result { cx.function(Protocol::INTO_ITER, |value: Value| value)?; + cx.function("into_iter", |value: Value| value)?; + { let next = next.clone(); @@ -486,15 +488,20 @@ pub fn module() -> Result { let mut string = String::new(); while let Some(value) = vm_try!(next.call((&iter,))) { - match &*vm_try!(value.borrow_kind_ref()) { - ValueKind::Char(c) => { + match vm_try!(value.borrow_ref()) { + ValueBorrowRef::Inline(Inline::Char(c)) => { vm_try!(string.try_push(*c)); } - ValueKind::String(s) => { - vm_try!(string.try_push_str(s)); - } - value => { - return VmResult::expected::(value.type_info()); + ValueBorrowRef::Mutable(value) => match &*value { + Mutable::String(s) => { + vm_try!(string.try_push_str(s)); + } + actual => { + return VmResult::expected::(actual.type_info()); + } + }, + actual => { + return VmResult::expected::(actual.type_info()); } } } diff --git a/crates/rune/src/modules/mem.rs b/crates/rune/src/modules/mem.rs index 99dc314a6..6e530fc69 100644 --- a/crates/rune/src/modules/mem.rs +++ b/crates/rune/src/modules/mem.rs @@ -1,15 +1,70 @@ //! Working with memory. use crate as rune; -use crate::runtime::{Value, VmResult}; -use crate::{ContextError, Module}; +use crate::alloc::fmt::TryWrite; +use crate::runtime::{self, Formatter, Value, VmResult}; +use crate::{Any, ContextError, Module}; /// Working with memory. #[rune::module(::std::mem)] pub fn module() -> Result { - let mut module = Module::from_meta(self::module_meta)?; - module.function_meta(drop)?; - Ok(module) + let mut m = Module::from_meta(self::module_meta)?; + m.function_meta(drop)?; + m.function_meta(snapshot)?; + + m.ty::()?; + m.function_meta(Snapshot::display)?; + m.function_meta(Snapshot::debug)?; + m.function_meta(Snapshot::shared)?; + + Ok(m) +} + +#[derive(Any)] +#[rune(item = ::std::mem)] +struct Snapshot { + inner: runtime::Snapshot, +} + +impl Snapshot { + /// The number of shared references to the value. + /// + /// # Examples + /// + /// ```rune + /// use std::mem::snapshot; + /// + /// let v = [1, 2, 3]; + /// + /// let s = snapshot(v)?; + /// assert_eq!(s.shared(), 0); + /// + /// // An iterators takes a shared reference to the collection being iterated over. + /// let it = v.iter(); + /// + /// let s = snapshot(v)?; + /// assert_eq!(s.shared(), 1); + /// drop(it); + /// + /// let s = snapshot(v)?; + /// assert_eq!(s.shared(), 0); + /// ``` + #[rune::function] + fn shared(&self) -> usize { + self.inner.shared() + } + + #[rune::function(protocol = STRING_DISPLAY)] + fn display(&self, f: &mut Formatter) -> VmResult<()> { + vm_write!(f, "{}", self.inner); + VmResult::Ok(()) + } + + #[rune::function(protocol = STRING_DEBUG)] + fn debug(&self, f: &mut Formatter) -> VmResult<()> { + vm_write!(f, "{:?}", self.inner); + VmResult::Ok(()) + } } /// Explicitly drop the given value, freeing up any memory associated with it. @@ -30,3 +85,26 @@ fn drop(value: Value) -> VmResult<()> { vm_try!(value.drop()); VmResult::Ok(()) } + +/// Get the usage snapshot of a value. +/// +/// A snapshot can be used to diagnose how many users a given value has. +/// +/// # Examples +/// +/// ```rune +/// use std::mem::snapshot; +/// +/// let v = [1, 2, 3]; +/// +/// let s = snapshot(v)?; +/// +/// assert_eq!(s.shared(), 0); +/// dbg!(s); +/// ``` +#[rune::function] +fn snapshot(value: Value) -> Option { + value + .snapshot() + .map(|snapshot| Snapshot { inner: snapshot }) +} diff --git a/crates/rune/src/modules/string.rs b/crates/rune/src/modules/string.rs index a8b931475..a291d354f 100644 --- a/crates/rune/src/modules/string.rs +++ b/crates/rune/src/modules/string.rs @@ -11,8 +11,8 @@ use crate::alloc::string::FromUtf8Error; use crate::alloc::{String, Vec}; use crate::compile::Named; use crate::runtime::{ - Bytes, FromValue, Function, MaybeTypeOf, Panic, Ref, ToValue, TypeOf, Value, ValueKind, - VmErrorKind, VmResult, + Bytes, FromValue, Function, Inline, MaybeTypeOf, Mutable, Panic, Ref, ToValue, TypeOf, Value, + ValueBorrowRef, VmErrorKind, VmResult, }; use crate::{Any, ContextError, Module}; @@ -546,9 +546,8 @@ fn reserve_exact(this: &mut String, additional: usize) -> VmResult<()> { /// /// ```rune /// let s = "hello"; -/// /// assert_eq!(b"hello", s.into_bytes()); -/// assert!(!is_readable(s)); +/// assert!(is_readable(s)); /// ``` #[rune::function(instance)] fn into_bytes(s: String) -> Bytes { @@ -757,20 +756,28 @@ fn shrink_to_fit(s: &mut String) -> VmResult<()> { /// [`split_whitespace`]: str::split_whitespace #[rune::function(instance, deprecated = "Use String::split instead")] fn split(this: Ref, value: Value) -> VmResult { - let split = match *vm_try!(value.borrow_kind_ref()) { - ValueKind::String(ref s) => { - vm_try!(rune::to_value(Split::new( - this, - vm_try!(Box::try_from(s.as_str())) - ))) - } - ValueKind::Char(c) => { - vm_try!(rune::to_value(Split::new(this, c))) + let split = match vm_try!(value.borrow_ref()) { + ValueBorrowRef::Inline(Inline::Char(c)) => { + vm_try!(rune::to_value(Split::new(this, *c))) } - ValueKind::Function(ref f) => { - vm_try!(rune::to_value(Split::new(this, vm_try!(f.try_clone())))) - } - ref actual => { + 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())))) + } + actual => { + return VmResult::err([ + VmErrorKind::expected::(actual.type_info()), + VmErrorKind::bad_argument(0), + ]) + } + }, + actual => { return VmResult::err([ VmErrorKind::expected::(actual.type_info()), VmErrorKind::bad_argument(0), @@ -794,29 +801,37 @@ fn split(this: Ref, value: Value) -> VmResult { /// ``` #[rune::function(instance)] fn split_once(this: &str, value: Value) -> VmResult> { - let outcome = match *vm_try!(value.borrow_kind_ref()) { - ValueKind::String(ref s) => this.split_once(s.as_str()), - ValueKind::Char(pat) => this.split_once(pat), - ValueKind::Function(ref f) => { - let mut err = None; - - let outcome = this.split_once(|c: char| match f.call::((c,)) { - VmResult::Ok(b) => b, - VmResult::Err(e) => { - if err.is_none() { - err = Some(e); + 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; + + let outcome = this.split_once(|c: char| match f.call::((c,)) { + VmResult::Ok(b) => b, + VmResult::Err(e) => { + if err.is_none() { + err = Some(e); + } + + false } + }); - false + if let Some(e) = err.take() { + return VmResult::Err(e); } - }); - if let Some(e) = err.take() { - return VmResult::Err(e); + outcome } - - outcome - } + actual => { + return VmResult::err([ + VmErrorKind::expected::(actual.type_info()), + VmErrorKind::bad_argument(0), + ]) + } + }, ref actual => { return VmResult::err([ VmErrorKind::expected::(actual.type_info()), @@ -1007,30 +1022,38 @@ fn chars(s: Ref) -> Chars { fn get(this: &str, key: Value) -> VmResult> { use crate::runtime::TypeOf; - let slice = match &*vm_try!(key.borrow_kind_ref()) { - ValueKind::RangeFrom(range) => { - let start = vm_try!(range.start.as_usize()); - this.get(start..) - } - ValueKind::RangeFull(..) => this.get(..), - ValueKind::RangeInclusive(range) => { - let start = vm_try!(range.start.as_usize()); - let end = vm_try!(range.end.as_usize()); - this.get(start..=end) - } - ValueKind::RangeToInclusive(range) => { - let end = vm_try!(range.end.as_usize()); - this.get(..=end) - } - ValueKind::RangeTo(range) => { - let end = vm_try!(range.end.as_usize()); - this.get(..end) - } - ValueKind::Range(range) => { - let start = vm_try!(range.start.as_usize()); - let end = vm_try!(range.end.as_usize()); - this.get(start..end) - } + let slice = match vm_try!(key.borrow_ref()) { + ValueBorrowRef::Mutable(value) => match &*value { + Mutable::RangeFrom(range) => { + let start = vm_try!(range.start.as_usize()); + this.get(start..) + } + Mutable::RangeFull(..) => this.get(..), + Mutable::RangeInclusive(range) => { + let start = vm_try!(range.start.as_usize()); + let end = vm_try!(range.end.as_usize()); + this.get(start..=end) + } + Mutable::RangeToInclusive(range) => { + let end = vm_try!(range.end.as_usize()); + this.get(..=end) + } + Mutable::RangeTo(range) => { + let end = vm_try!(range.end.as_usize()); + this.get(..end) + } + Mutable::Range(range) => { + let start = vm_try!(range.start.as_usize()); + let end = vm_try!(range.end.as_usize()); + this.get(start..end) + } + index => { + return VmResult::err(VmErrorKind::UnsupportedIndexGet { + target: String::type_info(), + index: index.type_info(), + }) + } + }, index => { return VmResult::err(VmErrorKind::UnsupportedIndexGet { target: String::type_info(), diff --git a/crates/rune/src/modules/vec.rs b/crates/rune/src/modules/vec.rs index a007bf58a..af82bf2d2 100644 --- a/crates/rune/src/modules/vec.rs +++ b/crates/rune/src/modules/vec.rs @@ -77,8 +77,9 @@ pub fn module() -> Result { m.function_meta(sort_by)?; m.function_meta(sort)?; m.function_meta(into_iter__meta)?; - m.function_meta(index_set)?; m.function_meta(index_get)?; + m.function_meta(index_set)?; + m.function_meta(resize)?; m.function_meta(string_debug)?; m.function_meta(clone__meta)?; @@ -559,6 +560,33 @@ fn index_set(this: &mut Vec, index: usize, value: Value) -> VmResult<()> { Vec::set(this, index, value) } +/// Resizes the `Vec` in-place so that `len` is equal to `new_len`. +/// +/// If `new_len` is greater than `len`, the `Vec` is extended by the difference, +/// with each additional slot filled with `value`. If `new_len` is less than +/// `len`, the `Vec` is simply truncated. +/// +/// This method requires `T` to implement [`Clone`], in order to be able to +/// clone the passed value. If you need more flexibility (or want to rely on +/// [`Default`] instead of [`Clone`]), use [`Vec::resize_with`]. If you only +/// need to resize to a smaller size, use [`Vec::truncate`]. +/// +/// # Examples +/// +/// ```rune +/// let vec = ["hello"]; +/// vec.resize(3, "world"); +/// assert_eq!(vec, ["hello", "world", "world"]); +/// +/// let vec = [1, 2, 3, 4]; +/// vec.resize(2, 0); +/// assert_eq!(vec, [1, 2]); +/// ``` +#[rune::function(instance)] +fn resize(this: &mut Vec, new_len: usize, value: Value) -> VmResult<()> { + Vec::resize(this, new_len, value) +} + /// Write a debug representation to a string. /// /// This calls the [`STRING_DEBUG`] protocol over all elements of the diff --git a/crates/rune/src/runtime/access.rs b/crates/rune/src/runtime/access.rs index e9c819be4..301d504cf 100644 --- a/crates/rune/src/runtime/access.rs +++ b/crates/rune/src/runtime/access.rs @@ -175,11 +175,16 @@ impl Snapshot { pub(crate) fn is_readable(&self) -> bool { self.0 & MASK == 0 } + + /// The number of times a value is shared. + pub(crate) fn shared(&self) -> usize { + self.0 & !MASK + } } impl fmt::Display for Snapshot { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 >> 1 { + match self.0 { 0 => write!(f, "fully accessible")?, EXCLUSIVE => write!(f, "exclusively accessed")?, MOVED => write!(f, "moved")?, @@ -386,11 +391,19 @@ impl<'a, T: ?Sized> BorrowRef<'a, T> { impl ops::Deref for BorrowRef<'_, T> { type Target = T; + #[inline] fn deref(&self) -> &Self::Target { self.data } } +impl AsRef for BorrowRef<'_, T> { + #[inline] + fn as_ref(&self) -> &T { + self.data + } +} + impl fmt::Debug for BorrowRef<'_, T> where T: fmt::Debug, diff --git a/crates/rune/src/runtime/budget.rs b/crates/rune/src/runtime/budget.rs index 75a14682e..2017bc9aa 100644 --- a/crates/rune/src/runtime/budget.rs +++ b/crates/rune/src/runtime/budget.rs @@ -111,11 +111,11 @@ impl BudgetGuard { return true; } - let Some(new) = self.0.checked_sub(1) else { + if self.0 == 0 { return false; - }; + } - self.0 = new; + self.0 -= 1; true } } diff --git a/crates/rune/src/runtime/bytes.rs b/crates/rune/src/runtime/bytes.rs index 21ffbc950..239e44fb6 100644 --- a/crates/rune/src/runtime/bytes.rs +++ b/crates/rune/src/runtime/bytes.rs @@ -12,7 +12,7 @@ use serde::ser; use crate as rune; use crate::alloc::prelude::*; use crate::alloc::{self, Box, Vec}; -use crate::runtime::{RawRef, Ref, UnsafeToRef, Value, ValueKind, VmErrorKind, VmResult}; +use crate::runtime::{Mutable, RawRef, Ref, UnsafeToRef, Value, ValueRepr, VmErrorKind, VmResult}; use crate::Any; /// A vector of bytes. @@ -359,8 +359,15 @@ impl UnsafeToRef for [u8] { type Guard = RawRef; unsafe fn unsafe_to_ref<'a>(value: Value) -> VmResult<(&'a Self, Self::Guard)> { - let result = Ref::try_map(vm_try!(value.into_kind_ref()), |kind| match kind { - ValueKind::Bytes(bytes) => Some(bytes.as_slice()), + let value = match vm_try!(value.into_repr()) { + ValueRepr::Mutable(value) => vm_try!(value.into_ref()), + ValueRepr::Inline(actual) => { + return VmResult::expected::(actual.type_info()); + } + }; + + let result = Ref::try_map(value, |value| match value { + Mutable::Bytes(bytes) => Some(bytes.as_slice()), _ => None, }); diff --git a/crates/rune/src/runtime/const_value.rs b/crates/rune/src/runtime/const_value.rs index dfebbb49b..8a33b1b6d 100644 --- a/crates/rune/src/runtime/const_value.rs +++ b/crates/rune/src/runtime/const_value.rs @@ -3,15 +3,15 @@ use serde::{Deserialize, Serialize}; use crate::alloc::prelude::*; use crate::alloc::{self, Box, HashMap, String, Vec}; use crate::runtime::{ - self, Bytes, FromValue, Object, OwnedTuple, ToValue, TypeInfo, Value, ValueKind, VmErrorKind, - VmResult, + self, Bytes, FromValue, Inline, Mutable, Object, OwnedTuple, ToValue, TypeInfo, Value, + ValueBorrowRef, VmErrorKind, VmResult, }; /// A constant value. #[derive(Debug, Deserialize, Serialize)] pub enum ConstValue { /// A constant unit. - EmptyTuple, + Unit, /// A byte. Byte(u8), /// A character. @@ -37,30 +37,91 @@ pub enum ConstValue { } impl ConstValue { + /// Construct a constant value from a reference to a value.. + pub(crate) fn from_value_ref(value: &Value) -> VmResult { + VmResult::Ok(match vm_try!(value.borrow_ref()) { + ValueBorrowRef::Inline(value) => match *value { + Inline::Unit => Self::Unit, + Inline::Byte(value) => Self::Byte(value), + Inline::Char(value) => Self::Char(value), + Inline::Bool(value) => Self::Bool(value), + Inline::Integer(value) => Self::Integer(value), + Inline::Float(value) => Self::Float(value), + ref actual => { + return VmResult::err(VmErrorKind::ConstNotSupported { + actual: actual.type_info(), + }) + } + }, + 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, + }), + Mutable::Bytes(ref bytes) => Self::Bytes(vm_try!(bytes.try_clone())), + Mutable::Vec(ref vec) => { + let mut const_vec = vm_try!(Vec::try_with_capacity(vec.len())); + + for value in vec { + vm_try!(const_vec.try_push(vm_try!(Self::from_value_ref(value)))); + } + + Self::Vec(const_vec) + } + Mutable::Tuple(ref tuple) => { + let mut const_tuple = vm_try!(Vec::try_with_capacity(tuple.len())); + + for value in tuple.iter() { + vm_try!(const_tuple.try_push(vm_try!(Self::from_value_ref(value)))); + } + + Self::Tuple(vm_try!(const_tuple.try_into_boxed_slice())) + } + Mutable::Object(ref object) => { + let mut const_object = vm_try!(HashMap::try_with_capacity(object.len())); + + for (key, value) in object { + let key = vm_try!(key.try_clone()); + let value = vm_try!(Self::from_value_ref(value)); + vm_try!(const_object.try_insert(key, value)); + } + + Self::Object(const_object) + } + actual => { + return VmResult::err(VmErrorKind::ConstNotSupported { + actual: actual.type_info(), + }) + } + }, + }) + } + /// Convert into virtual machine value. /// /// We provide this associated method since a constant value can be /// converted into a value infallibly, which is not captured by the trait /// otherwise. - pub fn as_value(&self) -> alloc::Result { + pub(crate) fn to_value(&self) -> alloc::Result { Ok(match self { - Self::EmptyTuple => Value::unit()?, - Self::Byte(b) => Value::try_from(*b)?, - Self::Char(c) => Value::try_from(*c)?, - Self::Bool(b) => Value::try_from(*b)?, - Self::Integer(n) => Value::try_from(*n)?, - Self::Float(n) => Value::try_from(*n)?, + Self::Unit => Value::unit(), + Self::Byte(b) => Value::from(*b), + Self::Char(c) => Value::from(*c), + Self::Bool(b) => Value::from(*b), + Self::Integer(n) => Value::from(*n), + Self::Float(n) => Value::from(*n), Self::String(string) => Value::try_from(string.try_clone()?)?, Self::Bytes(b) => Value::try_from(b.try_clone()?)?, Self::Option(option) => Value::try_from(match option { - Some(some) => Some(some.as_value()?), + Some(some) => Some(Self::to_value(some)?), None => None, })?, Self::Vec(vec) => { let mut v = runtime::Vec::with_capacity(vec.len())?; for value in vec { - v.push(value.as_value()?)?; + v.push(Self::to_value(value)?)?; } Value::try_from(v)? @@ -69,7 +130,7 @@ impl ConstValue { let mut t = Vec::try_with_capacity(tuple.len())?; for value in tuple.iter() { - t.try_push(value.as_value()?)?; + t.try_push(Self::to_value(value)?)?; } Value::try_from(OwnedTuple::try_from(t)?)? @@ -79,7 +140,8 @@ impl ConstValue { for (key, value) in object { let key = key.try_clone()?; - o.insert(key, value.as_value()?)?; + let value = Self::to_value(value)?; + o.insert(key, value)?; } Value::try_from(o)? @@ -98,7 +160,7 @@ impl ConstValue { /// Get the type information of the value. pub fn type_info(&self) -> TypeInfo { match self { - Self::EmptyTuple => TypeInfo::StaticType(crate::runtime::static_type::TUPLE), + Self::Unit => TypeInfo::StaticType(crate::runtime::static_type::TUPLE), Self::Byte(..) => TypeInfo::StaticType(crate::runtime::static_type::BYTE), Self::Char(..) => TypeInfo::StaticType(crate::runtime::static_type::CHAR), Self::Bool(..) => TypeInfo::StaticType(crate::runtime::static_type::BOOL), @@ -117,7 +179,7 @@ impl ConstValue { impl TryClone for ConstValue { fn try_clone(&self) -> alloc::Result { Ok(match self { - ConstValue::EmptyTuple => ConstValue::EmptyTuple, + ConstValue::Unit => ConstValue::Unit, ConstValue::Byte(byte) => ConstValue::Byte(*byte), ConstValue::Char(char) => ConstValue::Char(*char), ConstValue::Bool(bool) => ConstValue::Bool(*bool), @@ -135,57 +197,12 @@ impl TryClone for ConstValue { impl FromValue for ConstValue { fn from_value(value: Value) -> VmResult { - VmResult::Ok(match vm_try!(value.take_kind()) { - ValueKind::EmptyTuple => Self::EmptyTuple, - ValueKind::Byte(b) => Self::Byte(b), - ValueKind::Char(c) => Self::Char(c), - ValueKind::Bool(b) => Self::Bool(b), - ValueKind::Integer(n) => Self::Integer(n), - ValueKind::Float(f) => Self::Float(f), - ValueKind::String(s) => Self::String(s), - ValueKind::Option(option) => Self::Option(match option { - Some(some) => Some(vm_try!(Box::try_new(vm_try!(Self::from_value(some))))), - None => None, - }), - ValueKind::Bytes(b) => Self::Bytes(b), - ValueKind::Vec(vec) => { - let mut const_vec = vm_try!(Vec::try_with_capacity(vec.len())); - - for value in vec { - vm_try!(const_vec.try_push(vm_try!(Self::from_value(value)))); - } - - Self::Vec(const_vec) - } - ValueKind::Tuple(tuple) => { - let mut const_tuple = vm_try!(Vec::try_with_capacity(tuple.len())); - - for value in Vec::from(tuple.into_inner()) { - vm_try!(const_tuple.try_push(vm_try!(Self::from_value(value)))); - } - - Self::Tuple(vm_try!(const_tuple.try_into_boxed_slice())) - } - ValueKind::Object(object) => { - let mut const_object = vm_try!(HashMap::try_with_capacity(object.len())); - - for (key, value) in object { - vm_try!(const_object.try_insert(key, vm_try!(Self::from_value(value)))); - } - - Self::Object(const_object) - } - actual => { - return VmResult::err(VmErrorKind::ConstNotSupported { - actual: actual.type_info(), - }) - } - }) + ConstValue::from_value_ref(&value) } } impl ToValue for ConstValue { fn to_value(self) -> VmResult { - VmResult::Ok(vm_try!(ConstValue::as_value(&self))) + VmResult::Ok(vm_try!(ConstValue::to_value(&self))) } } diff --git a/crates/rune/src/runtime/format.rs b/crates/rune/src/runtime/format.rs index c52bd14c2..6a5ba7799 100644 --- a/crates/rune/src/runtime/format.rs +++ b/crates/rune/src/runtime/format.rs @@ -13,7 +13,9 @@ use crate as rune; use crate::alloc::clone::TryClone; use crate::alloc::fmt::TryWrite; use crate::alloc::String; -use crate::runtime::{Formatter, ProtocolCaller, Value, ValueKind, VmErrorKind, VmResult}; +use crate::runtime::{ + Formatter, Inline, Mutable, ProtocolCaller, Value, ValueRef, VmErrorKind, VmResult, +}; use crate::Any; /// Error raised when trying to parse a type string and it fails. @@ -208,31 +210,42 @@ impl FormatSpec { f: &mut Formatter, caller: &mut dyn ProtocolCaller, ) -> VmResult<()> { - match *vm_try!(value.borrow_kind_ref()) { - ValueKind::Char(c) => { - vm_try!(f.buf_mut().try_push(c)); - vm_try!(self.format_fill(f, self.align, self.fill, None)); - } - ValueKind::String(ref s) => { - vm_try!(f.buf_mut().try_push_str(s)); - vm_try!(self.format_fill(f, self.align, self.fill, None)); - } - ValueKind::Integer(n) => { - let (n, align, fill, sign) = self.int_traits(n); - vm_try!(self.format_number(f.buf_mut(), n)); - vm_try!(self.format_fill(f, align, fill, sign)); - } - ValueKind::Float(n) => { - let (n, align, fill, sign) = self.float_traits(n); - vm_try!(self.format_float(f.buf_mut(), n)); - vm_try!(self.format_fill(f, align, fill, sign)); - } - _ => { - vm_try!(value.string_display_with(f, caller)); + 'fallback: { + match vm_try!(value.value_ref()) { + ValueRef::Inline(value) => match value { + Inline::Char(c) => { + vm_try!(f.buf_mut().try_push(*c)); + vm_try!(self.format_fill(f, self.align, self.fill, None)); + } + Inline::Integer(n) => { + let (n, align, fill, sign) = self.int_traits(*n); + vm_try!(self.format_number(f.buf_mut(), n)); + vm_try!(self.format_fill(f, align, fill, sign)); + } + Inline::Float(n) => { + let (n, align, fill, sign) = self.float_traits(*n); + vm_try!(self.format_float(f.buf_mut(), n)); + vm_try!(self.format_fill(f, align, fill, sign)); + } + _ => { + break 'fallback; + } + }, + ValueRef::Mutable(value) => match &*vm_try!(value.borrow_ref()) { + Mutable::String(s) => { + vm_try!(f.buf_mut().try_push_str(s)); + vm_try!(self.format_fill(f, self.align, self.fill, None)); + } + _ => { + break 'fallback; + } + }, } + + return VmResult::Ok(()); } - VmResult::Ok(()) + value.string_display_with(f, caller) } fn format_debug( @@ -241,32 +254,43 @@ impl FormatSpec { f: &mut Formatter, caller: &mut dyn ProtocolCaller, ) -> VmResult<()> { - match *vm_try!(value.borrow_kind_ref()) { - ValueKind::String(ref s) => { - vm_write!(f, "{s:?}"); - } - ValueKind::Integer(n) => { - let (n, align, fill, sign) = self.int_traits(n); - vm_try!(self.format_number(f.buf_mut(), n)); - vm_try!(self.format_fill(f, align, fill, sign)); - } - ValueKind::Float(n) => { - let (n, align, fill, sign) = self.float_traits(n); - vm_try!(self.format_float(f.buf_mut(), n)); - vm_try!(self.format_fill(f, align, fill, sign)); - } - _ => { - vm_try!(value.string_debug_with(f, caller)); + 'fallback: { + match vm_try!(value.value_ref()) { + ValueRef::Inline(value) => match value { + Inline::Integer(n) => { + let (n, align, fill, sign) = self.int_traits(*n); + vm_try!(self.format_number(f.buf_mut(), n)); + vm_try!(self.format_fill(f, align, fill, sign)); + } + Inline::Float(n) => { + let (n, align, fill, sign) = self.float_traits(*n); + vm_try!(self.format_float(f.buf_mut(), n)); + vm_try!(self.format_fill(f, align, fill, sign)); + } + _ => { + break 'fallback; + } + }, + ValueRef::Mutable(value) => match &*vm_try!(value.borrow_ref()) { + Mutable::String(s) => { + vm_write!(f, "{s:?}"); + } + _ => { + break 'fallback; + } + }, } - } - VmResult::Ok(()) + return VmResult::Ok(()); + }; + + value.string_debug_with(f, caller) } fn format_upper_hex(&self, value: &Value, f: &mut Formatter) -> VmResult<()> { - match *vm_try!(value.borrow_kind_ref()) { - ValueKind::Integer(n) => { - let (n, align, fill, sign) = self.int_traits(n); + match vm_try!(value.as_inline()) { + Some(Inline::Integer(n)) => { + let (n, align, fill, sign) = self.int_traits(*n); vm_write!(f.buf_mut(), "{:X}", n); vm_try!(self.format_fill(f, align, fill, sign)); } @@ -279,9 +303,9 @@ impl FormatSpec { } fn format_lower_hex(&self, value: &Value, f: &mut Formatter) -> VmResult<()> { - match *vm_try!(value.borrow_kind_ref()) { - ValueKind::Integer(n) => { - let (n, align, fill, sign) = self.int_traits(n); + match vm_try!(value.as_inline()) { + Some(Inline::Integer(n)) => { + let (n, align, fill, sign) = self.int_traits(*n); vm_write!(f.buf_mut(), "{:x}", n); vm_try!(self.format_fill(f, align, fill, sign)); } @@ -294,9 +318,9 @@ impl FormatSpec { } fn format_binary(&self, value: &Value, f: &mut Formatter) -> VmResult<()> { - match *vm_try!(value.borrow_kind_ref()) { - ValueKind::Integer(n) => { - let (n, align, fill, sign) = self.int_traits(n); + match vm_try!(value.as_inline()) { + Some(Inline::Integer(n)) => { + let (n, align, fill, sign) = self.int_traits(*n); vm_write!(f.buf_mut(), "{:b}", n); vm_try!(self.format_fill(f, align, fill, sign)); } @@ -309,9 +333,9 @@ impl FormatSpec { } fn format_pointer(&self, value: &Value, f: &mut Formatter) -> VmResult<()> { - match *vm_try!(value.borrow_kind_ref()) { - ValueKind::Integer(n) => { - let (n, align, fill, sign) = self.int_traits(n); + match vm_try!(value.as_inline()) { + Some(Inline::Integer(n)) => { + let (n, align, fill, sign) = self.int_traits(*n); vm_write!(f.buf_mut(), "{:p}", n as *const ()); vm_try!(self.format_fill(f, align, fill, sign)); } diff --git a/crates/rune/src/runtime/from_value.rs b/crates/rune/src/runtime/from_value.rs index 3a0049e7c..5f4b9ef1b 100644 --- a/crates/rune/src/runtime/from_value.rs +++ b/crates/rune/src/runtime/from_value.rs @@ -2,7 +2,7 @@ use core::cmp::Ordering; use crate::alloc; use crate::runtime::{ - AnyObj, Mut, RawMut, RawRef, Ref, Value, ValueKind, VmError, VmErrorKind, VmResult, + AnyObj, Mut, Mutable, RawMut, RawRef, Ref, Value, ValueRepr, VmError, VmResult, }; use crate::Any; @@ -259,89 +259,96 @@ from_value_ref!(Option, into_option_ref, into_option_mut, into_option); impl FromValue for alloc::String { fn from_value(value: Value) -> VmResult { - match vm_try!(value.take_kind()) { - ValueKind::String(string) => VmResult::Ok(string), - actual => VmResult::err(VmErrorKind::expected::(actual.type_info())), - } + 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 { - VmResult::Ok(::rust_alloc::string::String::from(vm_try!( - alloc::String::from_value(value) - ))) + let string = vm_try!(alloc::String::from_value(value)); + let string = ::rust_alloc::string::String::from(string); + VmResult::Ok(string) } } impl FromValue for alloc::Box { fn from_value(value: Value) -> VmResult { - match vm_try!(value.take_kind()) { - ValueKind::String(string) => { - let string = vm_try!(string.try_into_boxed_str()); - VmResult::Ok(string) - } - actual => VmResult::err(VmErrorKind::expected::(actual.type_info())), - } + let string = vm_try!(value.borrow_string_ref()); + let string = vm_try!(alloc::Box::try_from(string.as_ref())); + VmResult::Ok(string) } } #[cfg(feature = "alloc")] impl FromValue for ::rust_alloc::boxed::Box { fn from_value(value: Value) -> VmResult { - match vm_try!(value.take_kind()) { - ValueKind::String(string) => { - VmResult::Ok(::rust_alloc::boxed::Box::::from(string.as_str())) - } - actual => VmResult::err(VmErrorKind::expected::(actual.type_info())), - } + let string = vm_try!(value.borrow_string_ref()); + let string = ::rust_alloc::boxed::Box::::from(string.as_ref()); + VmResult::Ok(string) } } impl FromValue for Mut { fn from_value(value: Value) -> VmResult { - let result = Mut::try_map(vm_try!(value.into_kind_mut()), |kind| match kind { - ValueKind::String(string) => Some(string), + let value = match vm_try!(value.into_repr()) { + ValueRepr::Mutable(value) => vm_try!(value.into_mut()), + ValueRepr::Inline(actual) => { + return VmResult::expected::(actual.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::err(VmErrorKind::expected::(actual.type_info())) - } + Err(actual) => VmResult::expected::(actual.type_info()), } } } impl FromValue for Ref { fn from_value(value: Value) -> VmResult { - let result = Ref::try_map(vm_try!(value.into_kind_ref()), |kind| match kind { - ValueKind::String(string) => Some(string), + let value = match vm_try!(value.into_repr()) { + ValueRepr::Mutable(value) => vm_try!(value.into_ref()), + ValueRepr::Inline(actual) => { + return VmResult::expected::(actual.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::err(VmErrorKind::expected::(actual.type_info())) - } + Err(actual) => VmResult::expected::(actual.type_info()), } } } impl FromValue for Ref { fn from_value(value: Value) -> VmResult { - let result = Ref::try_map(vm_try!(value.into_kind_ref()), |kind| match kind { - ValueKind::String(string) => Some(string.as_str()), + let value = match vm_try!(value.into_repr()) { + ValueRepr::Mutable(value) => vm_try!(value.into_ref()), + ValueRepr::Inline(actual) => { + return VmResult::expected::(actual.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::err(VmErrorKind::expected::(actual.type_info())) - } + Err(actual) => VmResult::expected::(actual.type_info()), } } } @@ -350,8 +357,15 @@ impl UnsafeToRef for str { type Guard = RawRef; unsafe fn unsafe_to_ref<'a>(value: Value) -> VmResult<(&'a Self, Self::Guard)> { - let result = Ref::try_map(vm_try!(value.into_kind_ref()), |kind| match kind { - ValueKind::String(string) => Some(string.as_str()), + let value = match vm_try!(value.into_repr()) { + ValueRepr::Mutable(value) => vm_try!(value.into_ref()), + ValueRepr::Inline(actual) => { + return VmResult::expected::(actual.type_info()); + } + }; + + let result = Ref::try_map(value, |value| match value { + Mutable::String(string) => Some(string.as_str()), _ => None, }); @@ -360,9 +374,7 @@ impl UnsafeToRef for str { let (string, guard) = Ref::into_raw(string); VmResult::Ok((string.as_ref(), guard)) } - Err(actual) => { - VmResult::err(VmErrorKind::expected::(actual.type_info())) - } + Err(actual) => VmResult::expected::(actual.type_info()), } } } @@ -371,8 +383,15 @@ impl UnsafeToMut for str { type Guard = RawMut; unsafe fn unsafe_to_mut<'a>(value: Value) -> VmResult<(&'a mut Self, Self::Guard)> { - let result = Mut::try_map(vm_try!(value.into_kind_mut()), |kind| match kind { - ValueKind::String(string) => Some(string.as_mut_str()), + let value = match vm_try!(value.into_repr()) { + ValueRepr::Mutable(value) => vm_try!(value.into_mut()), + ValueRepr::Inline(actual) => { + return VmResult::expected::(actual.type_info()); + } + }; + + let result = Mut::try_map(value, |kind| match kind { + Mutable::String(string) => Some(string.as_mut_str()), _ => None, }); @@ -381,9 +400,7 @@ impl UnsafeToMut for str { let (mut string, guard) = Mut::into_raw(string); VmResult::Ok((string.as_mut(), guard)) } - Err(actual) => { - VmResult::err(VmErrorKind::expected::(actual.type_info())) - } + Err(actual) => VmResult::expected::(actual.type_info()), } } } @@ -392,8 +409,15 @@ impl UnsafeToRef for alloc::String { type Guard = RawRef; unsafe fn unsafe_to_ref<'a>(value: Value) -> VmResult<(&'a Self, Self::Guard)> { - let result = Ref::try_map(vm_try!(value.into_kind_ref()), |kind| match kind { - ValueKind::String(string) => Some(string), + let value = match vm_try!(value.into_repr()) { + ValueRepr::Mutable(value) => vm_try!(value.into_ref()), + ValueRepr::Inline(actual) => { + return VmResult::expected::(actual.type_info()); + } + }; + + let result = Ref::try_map(value, |value| match value { + Mutable::String(string) => Some(string), _ => None, }); @@ -402,9 +426,7 @@ impl UnsafeToRef for alloc::String { let (string, guard) = Ref::into_raw(string); VmResult::Ok((string.as_ref(), guard)) } - Err(actual) => { - VmResult::err(VmErrorKind::expected::(actual.type_info())) - } + Err(actual) => VmResult::expected::(actual.type_info()), } } } @@ -413,10 +435,15 @@ impl UnsafeToMut for alloc::String { type Guard = RawMut; unsafe fn unsafe_to_mut<'a>(value: Value) -> VmResult<(&'a mut Self, Self::Guard)> { - let kind = vm_try!(value.into_kind_mut()); + let value = match vm_try!(value.into_repr()) { + ValueRepr::Mutable(value) => vm_try!(value.into_mut()), + ValueRepr::Inline(actual) => { + return VmResult::expected::(actual.type_info()); + } + }; - let result = Mut::try_map(kind, |kind| match kind { - ValueKind::String(string) => Some(string), + let result = Mut::try_map(value, |value| match value { + Mutable::String(string) => Some(string), _ => None, }); @@ -425,9 +452,7 @@ impl UnsafeToMut for alloc::String { let (mut string, guard) = Mut::into_raw(string); VmResult::Ok((string.as_mut(), guard)) } - Err(actual) => { - VmResult::err(VmErrorKind::expected::(actual.type_info())) - } + Err(actual) => VmResult::expected::(actual.type_info()), } } } diff --git a/crates/rune/src/runtime/function.rs b/crates/rune/src/runtime/function.rs index 8019f18e4..4cbfd9777 100644 --- a/crates/rune/src/runtime/function.rs +++ b/crates/rune/src/runtime/function.rs @@ -9,9 +9,9 @@ use crate::alloc::{self, Box, Vec}; use crate::function; use crate::runtime::vm::Isolated; use crate::runtime::{ - Args, Call, ConstValue, FromValue, FunctionHandler, InstAddress, Output, OwnedTuple, Rtti, - RuntimeContext, Stack, Unit, Value, ValueKind, VariantRtti, Vm, VmCall, VmErrorKind, VmHalt, - VmResult, + Args, Call, ConstValue, FromValue, FunctionHandler, InstAddress, Mutable, Output, OwnedTuple, + Rtti, RuntimeContext, Stack, Unit, Value, ValueRef, VariantRtti, Vm, VmCall, VmErrorKind, + VmHalt, VmResult, }; use crate::shared::AssertSend; use crate::Any; @@ -570,8 +570,12 @@ where let value: Value = vm_try!(self.call(args)); let value = 'out: { - if let ValueKind::Future(future) = &mut *vm_try!(value.borrow_kind_mut()) { - break 'out vm_try!(future.await); + if let ValueRef::Mutable(value) = vm_try!(value.value_ref()) { + let mut value = vm_try!(value.borrow_mut()); + + if let Mutable::Future(future) = &mut *value { + break 'out vm_try!(future.await); + } } value diff --git a/crates/rune/src/runtime/inst.rs b/crates/rune/src/runtime/inst.rs index 7bf5225fa..f23f593f3 100644 --- a/crates/rune/src/runtime/inst.rs +++ b/crates/rune/src/runtime/inst.rs @@ -6,9 +6,8 @@ use rune_macros::InstDisplay; use serde::{Deserialize, Serialize}; use crate as rune; -use crate::alloc; use crate::alloc::prelude::*; -use crate::runtime::{Call, FormatSpec, Memory, Type, Value, ValueKind, VmError, VmResult}; +use crate::runtime::{Call, FormatSpec, Memory, Mutable, Type, Value, VmError, VmResult}; use crate::Hash; /// Pre-canned panic reasons. @@ -58,7 +57,7 @@ impl fmt::Display for PanicReason { #[non_exhaustive] pub enum TypeCheck { /// Matches a unit type. - EmptyTuple, + Unit, /// Matches an anonymous tuple. Tuple, /// Matches an anonymous object. @@ -79,7 +78,7 @@ pub enum TypeCheck { impl fmt::Display for TypeCheck { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::EmptyTuple => write!(fmt, "Unit"), + Self::Unit => write!(fmt, "Unit"), Self::Tuple => write!(fmt, "Tuple"), Self::Object => write!(fmt, "Object"), Self::Vec => write!(fmt, "Vec"), @@ -1103,7 +1102,7 @@ impl Inst { /// Construct an instruction to push a unit. pub fn unit(out: Output) -> Self { Self::Store { - value: InstValue::EmptyTuple, + value: InstValue::Unit, out, } } @@ -1308,8 +1307,8 @@ impl IntoOutput for Value { } } -impl IntoOutput for ValueKind { - type Output = ValueKind; +impl IntoOutput for Mutable { + type Output = Mutable; #[inline] fn into_output(self) -> VmResult { @@ -1715,7 +1714,7 @@ impl fmt::Display for InstOp { #[non_exhaustive] pub enum InstValue { /// An empty tuple. - EmptyTuple, + Unit, /// A boolean. #[musli(packed)] Bool(bool), @@ -1738,15 +1737,15 @@ pub enum InstValue { impl InstValue { /// Convert into a value that can be pushed onto the stack. - pub fn into_value(self) -> alloc::Result { + pub fn into_value(self) -> Value { match self { - Self::EmptyTuple => Value::unit(), - Self::Bool(v) => Value::try_from(v), - Self::Byte(v) => Value::try_from(v), - Self::Char(v) => Value::try_from(v), - Self::Integer(v) => Value::try_from(v), - Self::Float(v) => Value::try_from(v), - Self::Type(v) => Value::try_from(v), + Self::Unit => Value::unit(), + Self::Bool(v) => Value::from(v), + Self::Byte(v) => Value::from(v), + Self::Char(v) => Value::from(v), + Self::Integer(v) => Value::from(v), + Self::Float(v) => Value::from(v), + Self::Type(v) => Value::from(v), } } } @@ -1754,7 +1753,7 @@ impl InstValue { impl fmt::Display for InstValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::EmptyTuple => write!(f, "()")?, + Self::Unit => write!(f, "()")?, Self::Bool(v) => write!(f, "{}", v)?, Self::Byte(v) => { if v.is_ascii_graphic() { diff --git a/crates/rune/src/runtime/mod.rs b/crates/rune/src/runtime/mod.rs index 12d29f7a4..90b435d0e 100644 --- a/crates/rune/src/runtime/mod.rs +++ b/crates/rune/src/runtime/mod.rs @@ -124,6 +124,7 @@ pub(crate) use self::shared::Shared; pub use self::shared::{Mut, RawMut, RawRef, Ref, SharedPointerGuard}; mod stack; +pub(crate) use self::stack::Pair; pub use self::stack::{Memory, SliceError, Stack, StackError}; mod static_string; @@ -152,8 +153,10 @@ pub(crate) use self::unit::UnitFn; pub use self::unit::{Unit, UnitStorage}; mod value; -pub(crate) use self::value::ValueKind; pub use self::value::{EmptyStruct, Rtti, Struct, TupleStruct, TypeValue, Value, VariantRtti}; +pub(crate) use self::value::{ + Inline, Mutable, OwnedValue, ValueBorrowRef, ValueMut, ValueRef, ValueRepr, ValueShared, +}; mod variant; pub use self::variant::{Variant, VariantData}; diff --git a/crates/rune/src/runtime/range.rs b/crates/rune/src/runtime/range.rs index c11a7883d..d65d6d7de 100644 --- a/crates/rune/src/runtime/range.rs +++ b/crates/rune/src/runtime/range.rs @@ -5,7 +5,8 @@ use core::ops; use crate as rune; use crate::alloc::clone::TryClone; use crate::runtime::{ - EnvProtocolCaller, FromValue, ProtocolCaller, ToValue, Value, ValueKind, VmErrorKind, VmResult, + EnvProtocolCaller, FromValue, Inline, ProtocolCaller, ToValue, Value, ValueRef, VmErrorKind, + VmResult, }; use crate::Any; @@ -95,22 +96,22 @@ impl Range { #[rune::function(keep)] pub fn iter(&self) -> VmResult { let value = match ( - &*vm_try!(self.start.borrow_kind_ref()), - &*vm_try!(self.end.borrow_kind_ref()), + &vm_try!(self.start.value_ref()), + vm_try!(self.end.value_ref()), ) { - (ValueKind::Byte(start), ValueKind::Byte(end)) => { + (ValueRef::Inline(Inline::Byte(start)), ValueRef::Inline(Inline::Byte(end))) => { vm_try!(rune::to_value(RangeIter::new(*start..*end))) } - (ValueKind::Char(start), ValueKind::Char(end)) => { + (ValueRef::Inline(Inline::Char(start)), ValueRef::Inline(Inline::Char(end))) => { vm_try!(rune::to_value(RangeIter::new(*start..*end))) } - (ValueKind::Integer(start), ValueKind::Integer(end)) => { + (ValueRef::Inline(Inline::Integer(start)), ValueRef::Inline(Inline::Integer(end))) => { vm_try!(rune::to_value(RangeIter::new(*start..*end))) } (start, end) => { return VmResult::err(VmErrorKind::UnsupportedIterRange { - start: start.type_info(), - end: end.type_info(), + start: vm_try!(start.type_info()), + end: vm_try!(end.type_info()), }) } }; diff --git a/crates/rune/src/runtime/range_from.rs b/crates/rune/src/runtime/range_from.rs index cceccaabb..9ba922cc4 100644 --- a/crates/rune/src/runtime/range_from.rs +++ b/crates/rune/src/runtime/range_from.rs @@ -5,7 +5,8 @@ use core::ops; use crate as rune; use crate::alloc::clone::TryClone; use crate::runtime::{ - EnvProtocolCaller, FromValue, ProtocolCaller, ToValue, Value, ValueKind, VmErrorKind, VmResult, + EnvProtocolCaller, FromValue, Inline, ProtocolCaller, ToValue, Value, ValueRef, VmErrorKind, + VmResult, }; use crate::Any; @@ -86,13 +87,19 @@ impl RangeFrom { /// ``` #[rune::function(keep)] pub fn iter(&self) -> VmResult { - let value = match *vm_try!(self.start.borrow_kind_ref()) { - ValueKind::Byte(start) => vm_try!(crate::to_value(RangeFromIter::new(start..))), - ValueKind::Char(start) => vm_try!(crate::to_value(RangeFromIter::new(start..))), - ValueKind::Integer(start) => vm_try!(crate::to_value(RangeFromIter::new(start..))), - ref start => { + let value = match vm_try!(self.start.value_ref()) { + ValueRef::Inline(Inline::Byte(start)) => { + vm_try!(crate::to_value(RangeFromIter::new(*start..))) + } + ValueRef::Inline(Inline::Char(start)) => { + vm_try!(crate::to_value(RangeFromIter::new(*start..))) + } + ValueRef::Inline(Inline::Integer(start)) => { + vm_try!(crate::to_value(RangeFromIter::new(*start..))) + } + start => { return VmResult::err(VmErrorKind::UnsupportedIterRangeFrom { - start: start.type_info(), + start: vm_try!(start.type_info()), }) } }; diff --git a/crates/rune/src/runtime/range_inclusive.rs b/crates/rune/src/runtime/range_inclusive.rs index fa4f5e8a6..e588af48d 100644 --- a/crates/rune/src/runtime/range_inclusive.rs +++ b/crates/rune/src/runtime/range_inclusive.rs @@ -5,7 +5,8 @@ use core::ops; use crate as rune; use crate::alloc::clone::TryClone; use crate::runtime::{ - EnvProtocolCaller, FromValue, ProtocolCaller, ToValue, Value, ValueKind, VmErrorKind, VmResult, + EnvProtocolCaller, FromValue, Inline, ProtocolCaller, ToValue, Value, ValueRef, VmErrorKind, + VmResult, }; use crate::Any; @@ -96,22 +97,22 @@ impl RangeInclusive { #[rune::function(keep)] pub fn iter(&self) -> VmResult { let value = match ( - &*vm_try!(self.start.borrow_kind_ref()), - &*vm_try!(self.end.borrow_kind_ref()), + vm_try!(self.start.value_ref()), + vm_try!(self.end.value_ref()), ) { - (ValueKind::Byte(start), ValueKind::Byte(end)) => { + (ValueRef::Inline(Inline::Byte(start)), ValueRef::Inline(Inline::Byte(end))) => { vm_try!(rune::to_value(RangeInclusiveIter::new(*start..=*end))) } - (ValueKind::Char(start), ValueKind::Char(end)) => { + (ValueRef::Inline(Inline::Char(start)), ValueRef::Inline(Inline::Char(end))) => { vm_try!(rune::to_value(RangeInclusiveIter::new(*start..=*end))) } - (ValueKind::Integer(start), ValueKind::Integer(end)) => { + (ValueRef::Inline(Inline::Integer(start)), ValueRef::Inline(Inline::Integer(end))) => { vm_try!(rune::to_value(RangeInclusiveIter::new(*start..=*end))) } (start, end) => { return VmResult::err(VmErrorKind::UnsupportedIterRangeInclusive { - start: start.type_info(), - end: end.type_info(), + start: vm_try!(start.type_info()), + end: vm_try!(end.type_info()), }) } }; diff --git a/crates/rune/src/runtime/stack.rs b/crates/rune/src/runtime/stack.rs index a66696dfd..4bb0203b8 100644 --- a/crates/rune/src/runtime/stack.rs +++ b/crates/rune/src/runtime/stack.rs @@ -49,6 +49,11 @@ cfg_std! { impl std::error::Error for SliceError {} } +pub(crate) enum Pair<'a> { + Same(&'a mut Value), + Pair(&'a mut Value, &'a Value), +} + /// Memory access. pub trait Memory { /// Get the slice at the given address with the given length. @@ -481,6 +486,32 @@ impl Stack { self.top = top; Ok(()) } + + /// Get a pair of addresses. + pub(crate) fn pair(&mut self, a: InstAddress, b: InstAddress) -> Result, StackError> { + if a == b { + return Ok(Pair::Same(self.at_mut(a)?)); + } + + let a = self + .top + .checked_add(a.offset()) + .filter(|&n| n < self.stack.len()) + .ok_or(StackError { addr: a })?; + + let b = self + .top + .checked_add(b.offset()) + .filter(|&n| n < self.stack.len()) + .ok_or(StackError { addr: b })?; + + let pair = unsafe { + let ptr = self.stack.as_mut_ptr(); + Pair::Pair(&mut *ptr.add(a), &*ptr.add(b).cast_const()) + }; + + Ok(pair) + } } impl Memory for Stack { diff --git a/crates/rune/src/runtime/to_value.rs b/crates/rune/src/runtime/to_value.rs index e745f8c45..7eeb9372a 100644 --- a/crates/rune/src/runtime/to_value.rs +++ b/crates/rune/src/runtime/to_value.rs @@ -260,7 +260,7 @@ number_value_trait!(isize); impl ToValue for f32 { #[inline] fn to_value(self) -> VmResult { - VmResult::Ok(vm_try!(Value::try_from(self as f64))) + VmResult::Ok(Value::from(self as f64)) } } diff --git a/crates/rune/src/runtime/tuple.rs b/crates/rune/src/runtime/tuple.rs index 5827068ee..4dd1e4991 100644 --- a/crates/rune/src/runtime/tuple.rs +++ b/crates/rune/src/runtime/tuple.rs @@ -6,13 +6,15 @@ use crate as rune; use crate::alloc::clone::TryClone; use crate::alloc::{self, Box}; use crate::runtime::{ - ConstValue, FromValue, Mut, RawMut, RawRef, Ref, ToValue, UnsafeToMut, UnsafeToRef, Value, - ValueKind, VmErrorKind, VmResult, + ConstValue, FromValue, Mut, Mutable, OwnedValue, RawMut, RawRef, Ref, ToValue, UnsafeToMut, + UnsafeToRef, Value, ValueShared, VmErrorKind, VmResult, }; #[cfg(feature = "alloc")] use crate::runtime::{Hasher, ProtocolCaller}; use crate::Any; +use super::Inline; + /// The type of a tuple slice. #[derive(Any)] #[rune(builtin, static_type = TUPLE)] @@ -234,7 +236,7 @@ impl TryFrom> for OwnedTuple { let mut out = alloc::Vec::try_with_capacity(inner.len())?; for value in inner.iter() { - out.try_push(value.as_value()?)?; + out.try_push(value.to_value()?)?; } Ok(Self { @@ -267,7 +269,7 @@ impl TryFrom<::rust_alloc::boxed::Box<[ConstValue]>> for OwnedTuple { let mut out = alloc::Vec::try_with_capacity(inner.len())?; for value in inner.iter() { - out.try_push(value.as_value()?)?; + out.try_push(value.to_value()?)?; } Ok(Self { @@ -278,10 +280,15 @@ impl TryFrom<::rust_alloc::boxed::Box<[ConstValue]>> for OwnedTuple { impl FromValue for OwnedTuple { fn from_value(value: Value) -> VmResult { - match vm_try!(value.take_kind()) { - ValueKind::EmptyTuple => VmResult::Ok(Self::new()), - ValueKind::Tuple(tuple) => VmResult::Ok(tuple), - actual => VmResult::err(VmErrorKind::expected::(actual.type_info())), + match vm_try!(value.take_value()) { + OwnedValue::Inline(value) => match value { + Inline::Unit => VmResult::Ok(Self::new()), + actual => VmResult::expected::(actual.type_info()), + }, + OwnedValue::Mutable(value) => match value { + Mutable::Tuple(tuple) => VmResult::Ok(tuple), + actual => VmResult::expected::(actual.type_info()), + }, } } } @@ -299,7 +306,7 @@ macro_rules! impl_tuple { impl ToValue for () { fn to_value(self) -> VmResult { - VmResult::Ok(vm_try!(Value::unit())) + VmResult::Ok(Value::unit()) } } }; @@ -344,30 +351,58 @@ repeat_macro!(impl_tuple); impl FromValue for Ref { fn from_value(value: Value) -> VmResult { - let result = Ref::try_map(vm_try!(value.into_kind_ref()), |kind| match kind { - ValueKind::EmptyTuple => Some(Tuple::new(&[])), - ValueKind::Tuple(tuple) => Some(&**tuple), - _ => None, - }); + let result = match vm_try!(value.into_value_shared()) { + ValueShared::Inline(value) => match value { + Inline::Unit => Ok(Ref::from_static(Tuple::new(&[]))), + actual => Err(actual.type_info()), + }, + ValueShared::Mutable(value) => { + let value = vm_try!(value.into_ref()); + + let result = Ref::try_map(value, |value| match value { + Mutable::Tuple(tuple) => Some(&**tuple), + _ => None, + }); + + match result { + Ok(tuple) => Ok(tuple), + Err(actual) => Err(actual.type_info()), + } + } + }; match result { Ok(tuple) => VmResult::Ok(tuple), - Err(actual) => VmResult::err(VmErrorKind::expected::(actual.type_info())), + Err(actual) => VmResult::expected::(actual), } } } impl FromValue for Mut { fn from_value(value: Value) -> VmResult { - let result = Mut::try_map(vm_try!(value.into_kind_mut()), |kind| match kind { - ValueKind::EmptyTuple => Some(Tuple::new_mut(&mut [])), - ValueKind::Tuple(tuple) => Some(&mut **tuple), - _ => None, - }); + let result = match vm_try!(value.into_value_shared()) { + ValueShared::Inline(value) => match value { + Inline::Unit => Ok(Mut::from_static(Tuple::new_mut(&mut []))), + actual => Err(actual.type_info()), + }, + ValueShared::Mutable(value) => { + let value = vm_try!(value.clone().into_mut()); + + let result = Mut::try_map(value, |kind| match kind { + Mutable::Tuple(tuple) => Some(&mut **tuple), + _ => None, + }); + + match result { + Ok(value) => Ok(value), + Err(actual) => Err(actual.type_info()), + } + } + }; match result { Ok(tuple) => VmResult::Ok(tuple), - Err(actual) => VmResult::err(VmErrorKind::expected::(actual.type_info())), + Err(actual) => VmResult::expected::(actual), } } } diff --git a/crates/rune/src/runtime/value.rs b/crates/rune/src/runtime/value.rs index 8ccfa7040..903379f1a 100644 --- a/crates/rune/src/runtime/value.rs +++ b/crates/rune/src/runtime/value.rs @@ -1,18 +1,25 @@ +#[macro_use] +mod macros; + mod serde; +mod rtti; +pub use self::rtti::{Rtti, VariantRtti}; + +mod data; +pub use self::data::{EmptyStruct, Struct, TupleStruct}; + use core::any; -use core::borrow::Borrow; -use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}; +use core::cmp::{Ord, Ordering, PartialOrd}; use core::fmt; -use core::hash; use ::rust_alloc::sync::Arc; -use crate as rune; use crate::alloc::fmt::TryWrite; use crate::alloc::prelude::*; use crate::alloc::{self, String}; use crate::compile::meta; +use crate::runtime::static_type; use crate::runtime::vm::CallResultOnly; use crate::runtime::{ AccessError, AccessErrorKind, AnyObj, AnyObjError, BorrowMut, BorrowRef, Bytes, ConstValue, @@ -24,199 +31,7 @@ use crate::runtime::{ }; #[cfg(feature = "alloc")] use crate::runtime::{Hasher, Tuple}; -use crate::{Any, Hash, ItemBuf}; - -use ::serde::{Deserialize, Serialize}; - -/// Macro used to generate coersions for [`Value`]. -macro_rules! into_base { - ( - $(#[$($meta:meta)*])* - $kind:ident($ty:ty), - $into_ref:ident, - $into_mut:ident, - $borrow_ref:ident, - $borrow_mut:ident, - ) => { - $(#[$($meta)*])* - /// - /// This ensures that the value has read access to the underlying value - /// and does not consume it. - #[inline] - pub fn $into_ref(self) -> Result, RuntimeError> { - let result = Ref::try_map(self.into_kind_ref()?, |kind| match kind { - ValueKind::$kind(bytes) => Some(bytes), - _ => None, - }); - - match result { - Ok(bytes) => Ok(bytes), - Err(actual) => { - Err(RuntimeError::expected::<$ty>(actual.type_info())) - } - } - } - - $(#[$($meta)*])* - /// - /// This ensures that the value has write access to the underlying value - /// and does not consume it. - #[inline] - pub fn $into_mut(self) -> Result, RuntimeError> { - let result = Mut::try_map(self.into_kind_mut()?, |kind| match kind { - ValueKind::$kind(bytes) => Some(bytes), - _ => None, - }); - - match result { - Ok(bytes) => Ok(bytes), - Err(actual) => { - Err(RuntimeError::expected::<$ty>(actual.type_info())) - } - } - } - - $(#[$($meta)*])* - /// - /// This ensures that the value has read access to the underlying value - /// and does not consume it. - #[inline] - pub fn $borrow_ref(&self) -> Result, RuntimeError> { - let result = BorrowRef::try_map(self.borrow_kind_ref()?, |kind| match kind { - ValueKind::$kind(bytes) => Some(bytes), - _ => None, - }); - - match result { - Ok(bytes) => Ok(bytes), - Err(actual) => { - Err(RuntimeError::expected::<$ty>(actual.type_info())) - } - } - } - - $(#[$($meta)*])* - /// - /// This ensures that the value has write access to the underlying value - /// and does not consume it. - #[inline] - pub fn $borrow_mut(&self) -> Result, RuntimeError> { - let result = BorrowMut::try_map(self.borrow_kind_mut()?, |kind| match kind { - ValueKind::$kind(bytes) => Some(bytes), - _ => None, - }); - - match result { - Ok(bytes) => Ok(bytes), - Err(actual) => { - Err(RuntimeError::expected::<$ty>(actual.type_info())) - } - } - } - } -} - -macro_rules! into { - ( - $(#[$($meta:meta)*])* - $kind:ident($ty:ty), - $into_ref:ident, - $into_mut:ident, - $borrow_ref:ident, - $borrow_mut:ident, - $into:ident, - ) => { - into_base! { - $(#[$($meta)*])* - $kind($ty), - $into_ref, - $into_mut, - $borrow_ref, - $borrow_mut, - } - - $(#[$($meta)*])* - /// - /// This consumes the underlying value. - #[inline] - pub fn $into(self) -> Result<$ty, RuntimeError> { - match self.take_kind()? { - ValueKind::$kind(value) => Ok(value), - actual => { - Err(RuntimeError::expected::<$ty>(actual.type_info())) - } - } - } - } -} - -macro_rules! copy_into { - ( - $(#[$($meta:meta)*])* - $kind:ident($ty:ty), - $into_ref:ident, - $into_mut:ident, - $borrow_ref:ident, - $borrow_mut:ident, - $as:ident, - ) => { - into_base! { - $(#[$($meta)*])* - $kind($ty), - $into_ref, - $into_mut, - $borrow_ref, - $borrow_mut, - } - - $(#[$($meta)*])* - /// - /// This copied the underlying value. - #[inline] - pub fn $as(&self) -> Result<$ty, RuntimeError> { - match *self.borrow_kind_ref()? { - ValueKind::$kind(value) => Ok(value), - ref actual => { - Err(RuntimeError::expected::<$ty>(actual.type_info())) - } - } - } - } -} - -macro_rules! clone_into { - ( - $(#[$($meta:meta)*])* - $kind:ident($ty:ty), - $into_ref:ident, - $into_mut:ident, - $borrow_ref:ident, - $borrow_mut:ident, - $as:ident, - ) => { - into_base! { - $(#[$($meta)*])* - $kind($ty), - $into_ref, - $into_mut, - $borrow_ref, - $borrow_mut, - } - - $(#[$($meta)*])* - /// - /// This clones the underlying value. - #[inline] - pub fn $as(&self) -> Result<$ty, RuntimeError> { - match &*self.borrow_kind_ref()? { - ValueKind::$kind(value) => Ok(value.clone()), - actual => { - Err(RuntimeError::expected::<$ty>(actual.type_info())) - } - } - } - } -} +use crate::{Any, Hash}; // Small helper function to build errors. fn err(error: E) -> VmResult @@ -226,233 +41,78 @@ where VmResult::err(error) } -/// A empty with a well-defined type. -#[derive(TryClone)] -#[try_clone(crate)] -pub struct EmptyStruct { - /// The type hash of the empty. - pub(crate) rtti: Arc, -} - -impl EmptyStruct { - /// Access runtime type information. - pub fn rtti(&self) -> &Arc { - &self.rtti - } - - /// Get type info for the typed tuple. - pub fn type_info(&self) -> TypeInfo { - TypeInfo::Typed(self.rtti.clone()) - } -} - -impl fmt::Debug for EmptyStruct { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.rtti.item) - } -} - -/// A tuple with a well-defined type. -#[derive(TryClone)] -pub struct TupleStruct { - /// The type hash of the tuple. - pub(crate) rtti: Arc, - /// Content of the tuple. - pub(crate) data: OwnedTuple, -} - -impl TupleStruct { - /// Access runtime type information. - pub fn rtti(&self) -> &Arc { - &self.rtti - } - - /// Access underlying data. - pub fn data(&self) -> &OwnedTuple { - &self.data - } - - /// Access underlying data mutably. - pub fn data_mut(&mut self) -> &mut OwnedTuple { - &mut self.data - } - - /// Get type info for the typed tuple. - pub fn type_info(&self) -> TypeInfo { - TypeInfo::Typed(self.rtti.clone()) - } - - /// Get the value at the given index in the tuple. - pub fn get(&self, index: usize) -> Option<&Value> { - self.data.get(index) - } - - /// Get the mutable value at the given index in the tuple. - pub fn get_mut(&mut self, index: usize) -> Option<&mut Value> { - self.data.get_mut(index) - } +#[derive(Clone)] +enum Repr { + Empty, + Inline(Inline), + Mutable(Shared), } -impl fmt::Debug for TupleStruct { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}{:?}", self.rtti.item, self.data) - } +pub(crate) enum ValueRepr { + Inline(Inline), + Mutable(Shared), } -/// An object with a well-defined type. -#[derive(TryClone)] -pub struct Struct { - /// The type hash of the object. - pub(crate) rtti: Arc, - /// Content of the object. - pub(crate) data: Object, +pub(crate) enum OwnedValue { + Inline(Inline), + Mutable(Mutable), } -impl Struct { - /// Access runtime type information. - pub fn rtti(&self) -> &Arc { - &self.rtti - } - - /// Access underlying data. - pub fn data(&self) -> &Object { - &self.data - } - - /// Access underlying data mutably. - pub fn data_mut(&mut self) -> &mut Object { - &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. +impl OwnedValue { #[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> - where - String: Borrow, - Q: ?Sized + hash::Hash + Eq + Ord, - { - self.data.get(k) - } - - /// Get the given mutable value by key in the object. - pub fn get_mut(&mut self, k: &Q) -> Option<&mut Value> - where - String: Borrow, - Q: ?Sized + hash::Hash + Eq + Ord, - { - self.data.get_mut(k) - } -} - -impl fmt::Debug for Struct { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.data.debug_struct(&self.rtti.item)) - } -} - -/// Runtime information on variant. -#[derive(Debug, Serialize, Deserialize)] -#[non_exhaustive] -pub struct VariantRtti { - /// The type hash of the enum. - pub enum_hash: Hash, - /// The type variant hash. - pub hash: Hash, - /// The name of the variant. - pub item: ItemBuf, -} - -impl PartialEq for VariantRtti { - fn eq(&self, other: &Self) -> bool { - self.hash == other.hash - } -} - -impl Eq for VariantRtti {} - -impl hash::Hash for VariantRtti { - fn hash(&self, state: &mut H) { - self.hash.hash(state) - } -} - -impl PartialOrd for VariantRtti { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for VariantRtti { - fn cmp(&self, other: &Self) -> Ordering { - self.hash.cmp(&other.hash) + pub(crate) fn type_info(&self) -> TypeInfo { + match self { + OwnedValue::Inline(value) => value.type_info(), + OwnedValue::Mutable(value) => value.type_info(), + } } } -/// Runtime information on variant. -#[derive(Debug, Serialize, Deserialize)] -#[non_exhaustive] -pub struct Rtti { - /// The type hash of the type. - pub hash: Hash, - /// The item of the type. - pub item: ItemBuf, -} - -impl PartialEq for Rtti { - fn eq(&self, other: &Self) -> bool { - self.hash == other.hash - } +pub(crate) enum ValueRef<'a> { + Inline(&'a Inline), + Mutable(&'a Shared), } -impl Eq for Rtti {} - -impl hash::Hash for Rtti { - fn hash(&self, state: &mut H) { - self.hash.hash(state) +impl ValueRef<'_> { + #[inline] + pub(crate) fn type_info(&self) -> Result { + match self { + ValueRef::Inline(value) => Ok(value.type_info()), + ValueRef::Mutable(value) => Ok(value.borrow_ref()?.type_info()), + } } } -impl PartialOrd for Rtti { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } +pub(crate) enum ValueBorrowRef<'a> { + Inline(&'a Inline), + Mutable(BorrowRef<'a, Mutable>), } -impl Ord for Rtti { - fn cmp(&self, other: &Self) -> Ordering { - self.hash.cmp(&other.hash) +impl<'a> ValueBorrowRef<'a> { + #[inline] + pub(crate) fn type_info(&self) -> TypeInfo { + match self { + ValueBorrowRef::Inline(value) => value.type_info(), + ValueBorrowRef::Mutable(value) => value.type_info(), + } } } -#[derive(Clone)] -enum ValueRepr { - Empty, - Value(Shared), +/// Access the internals of a value mutably. +pub(crate) enum ValueMut<'a> { + Inline(&'a mut Inline), + Mutable(#[allow(unused)] &'a mut Shared), } -impl fmt::Pointer for ValueRepr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Empty => write!(f, "0x0"), - Self::Value(value) => write!(f, "{:p}", value), - } - } +pub(crate) enum ValueShared { + Inline(Inline), + Mutable(Shared), } /// An entry on the stack. #[derive(Clone)] pub struct Value { - repr: ValueRepr, + repr: Repr, } impl Value { @@ -504,16 +164,24 @@ impl Value { where T: Any, { - let value = Shared::new(ValueKind::Any(AnyObj::from_ref(data)))?; + let value = Shared::new(Mutable::Any(AnyObj::from_ref(data)))?; let (value, guard) = Shared::into_drop_guard(value); Ok(( Self { - repr: ValueRepr::Value(value), + repr: Repr::Mutable(value), }, guard, )) } + /// Optionally get the snapshot of the value if available. + pub(crate) fn snapshot(&self) -> Option { + match &self.repr { + Repr::Mutable(value) => Some(value.snapshot()), + _ => None, + } + } + /// Construct a value that wraps a mutable pointer. /// /// # Safety @@ -566,11 +234,11 @@ impl Value { T: Any, { let obj = AnyObj::from_mut(data); - let value = Shared::new(ValueKind::Any(obj))?; + let value = Shared::new(Mutable::Any(obj))?; let (value, guard) = Shared::into_drop_guard(value); Ok(( Self { - repr: ValueRepr::Value(value), + repr: Repr::Mutable(value), }, guard, )) @@ -579,63 +247,31 @@ impl Value { /// Test if the value is writable. pub fn is_writable(&self) -> bool { match self.repr { - ValueRepr::Empty => false, - ValueRepr::Value(ref value) => value.is_writable(), + Repr::Empty => false, + Repr::Inline(..) => true, + Repr::Mutable(ref value) => value.is_writable(), } } /// Test if the value is readable. pub fn is_readable(&self) -> bool { - match self.repr { - ValueRepr::Empty => false, - ValueRepr::Value(ref value) => value.is_readable(), + match &self.repr { + Repr::Empty => false, + Repr::Inline(..) => true, + Repr::Mutable(ref value) => value.is_readable(), } } - /// Get snapshot of value. - /// - /// The snapshot details how the value is currently being access. - pub fn snapshot(&self) -> Result { - Ok(self.as_value_kind()?.snapshot()) - } - /// Construct a unit value. - pub(crate) fn unit() -> alloc::Result { - Ok(Self { - repr: ValueRepr::Value(Shared::new(ValueKind::EmptyTuple)?), - }) - } - - /// Construct an empty value. - pub const fn empty() -> Self { + pub(crate) const fn unit() -> Self { Self { - repr: ValueRepr::Empty, + repr: Repr::Inline(Inline::Unit), } } - /// Take the kind of the value. - pub(crate) fn take_kind(self) -> Result { - self.into_value_kind()?.take() - } - - /// Borrow the kind of the value as a mutable reference. - pub(crate) fn borrow_kind_mut(&self) -> Result, AccessError> { - self.as_value_kind()?.borrow_mut() - } - - /// Take the kind of the value as an owned mutable reference. - pub(crate) fn into_kind_mut(self) -> Result, AccessError> { - self.into_value_kind()?.into_mut() - } - - /// Borrow the kind of the value as a reference. - pub(crate) fn borrow_kind_ref(&self) -> Result, AccessError> { - self.as_value_kind()?.borrow_ref() - } - - /// Take the kind of the value as an owned reference. - pub(crate) fn into_kind_ref(self) -> Result, AccessError> { - self.into_value_kind()?.into_ref() + /// Construct an empty value. + pub const fn empty() -> Self { + Self { repr: Repr::Empty } } /// Format the value using the [Protocol::STRING_DISPLAY] protocol. @@ -655,50 +291,59 @@ impl Value { } /// Internal impl of string_display with a customizable caller. + #[cfg_attr(feature = "bench", inline(never))] pub(crate) fn string_display_with( &self, f: &mut Formatter, caller: &mut dyn ProtocolCaller, ) -> VmResult<()> { - match &*vm_try!(self.borrow_kind_ref()) { - ValueKind::Char(c) => { - vm_try!(f.push(*c)); - } - ValueKind::Format(format) => { - vm_try!(format.spec.format(&format.value, f, caller)); - } - ValueKind::Integer(integer) => { - let mut buffer = itoa::Buffer::new(); - vm_try!(f.push_str(buffer.format(*integer))); - } - ValueKind::Float(float) => { - let mut buffer = ryu::Buffer::new(); - vm_try!(f.push_str(buffer.format(*float))); - } - ValueKind::Bool(bool) => { - vm_write!(f, "{bool}"); - } - ValueKind::Byte(byte) => { - let mut buffer = itoa::Buffer::new(); - vm_try!(f.push_str(buffer.format(*byte))); - } - ValueKind::String(string) => { - vm_try!(f.push_str(string)); + 'fallback: { + match vm_try!(self.borrow_ref()) { + ValueBorrowRef::Inline(value) => match value { + Inline::Char(c) => { + vm_try!(f.push(*c)); + } + Inline::Integer(integer) => { + let mut buffer = itoa::Buffer::new(); + vm_try!(f.push_str(buffer.format(*integer))); + } + Inline::Float(float) => { + let mut buffer = ryu::Buffer::new(); + vm_try!(f.push_str(buffer.format(*float))); + } + Inline::Bool(bool) => { + vm_write!(f, "{bool}"); + } + Inline::Byte(byte) => { + let mut buffer = itoa::Buffer::new(); + vm_try!(f.push_str(buffer.format(*byte))); + } + _ => { + break 'fallback; + } + }, + ValueBorrowRef::Mutable(value) => match &*value { + Mutable::Format(format) => { + vm_try!(format.spec.format(&format.value, f, caller)); + } + Mutable::String(string) => { + vm_try!(f.push_str(string)); + } + _ => { + break 'fallback; + } + }, } - _ => { - let mut args = DynGuardedArgs::new((f,)); - let result = vm_try!(caller.call_protocol_fn( - Protocol::STRING_DISPLAY, - self.clone(), - &mut args - )); + return VmResult::Ok(()); + }; - return <()>::from_value(result); - } - } + let mut args = DynGuardedArgs::new((f,)); - VmResult::Ok(()) + let result = + vm_try!(caller.call_protocol_fn(Protocol::STRING_DISPLAY, self.clone(), &mut args)); + + <()>::from_value(result) } /// Perform a shallow clone of the value using the [`CLONE`] protocol. @@ -718,56 +363,59 @@ impl Value { } pub(crate) fn clone_with(&self, caller: &mut dyn ProtocolCaller) -> VmResult { - let kind = match &*vm_try!(self.borrow_kind_ref()) { - ValueKind::EmptyTuple => ValueKind::EmptyTuple, - ValueKind::Bool(value) => ValueKind::Bool(*value), - ValueKind::Byte(value) => ValueKind::Byte(*value), - ValueKind::Char(value) => ValueKind::Char(*value), - ValueKind::Integer(value) => ValueKind::Integer(*value), - ValueKind::Float(value) => ValueKind::Float(*value), - ValueKind::Type(value) => ValueKind::Type(*value), - ValueKind::Ordering(value) => ValueKind::Ordering(*value), - ValueKind::String(value) => ValueKind::String(vm_try!(value.try_clone())), - ValueKind::Bytes(value) => ValueKind::Bytes(vm_try!(value.try_clone())), - ValueKind::Vec(value) => ValueKind::Vec(vm_try!(value.try_clone())), - ValueKind::Tuple(value) => ValueKind::Tuple(vm_try!(value.try_clone())), - ValueKind::Object(value) => ValueKind::Object(vm_try!(value.try_clone())), - ValueKind::RangeFrom(value) => ValueKind::RangeFrom(vm_try!(value.try_clone())), - ValueKind::RangeFull(value) => ValueKind::RangeFull(vm_try!(value.try_clone())), - ValueKind::RangeInclusive(value) => { - ValueKind::RangeInclusive(vm_try!(value.try_clone())) - } - ValueKind::RangeToInclusive(value) => { - ValueKind::RangeToInclusive(vm_try!(value.try_clone())) - } - ValueKind::RangeTo(value) => ValueKind::RangeTo(vm_try!(value.try_clone())), - ValueKind::Range(value) => ValueKind::Range(vm_try!(value.try_clone())), - ValueKind::ControlFlow(value) => ValueKind::ControlFlow(vm_try!(value.try_clone())), - ValueKind::Stream(value) => ValueKind::Stream(vm_try!(value.try_clone())), - ValueKind::Generator(value) => ValueKind::Generator(vm_try!(value.try_clone())), - ValueKind::GeneratorState(value) => { - ValueKind::GeneratorState(vm_try!(value.try_clone())) - } - ValueKind::Option(value) => ValueKind::Option(vm_try!(value.try_clone())), - ValueKind::Result(value) => ValueKind::Result(vm_try!(value.try_clone())), - ValueKind::EmptyStruct(value) => ValueKind::EmptyStruct(vm_try!(value.try_clone())), - ValueKind::TupleStruct(value) => ValueKind::TupleStruct(vm_try!(value.try_clone())), - ValueKind::Struct(value) => ValueKind::Struct(vm_try!(value.try_clone())), - ValueKind::Variant(value) => ValueKind::Variant(vm_try!(value.try_clone())), - ValueKind::Function(value) => ValueKind::Function(vm_try!(value.try_clone())), - ValueKind::Format(value) => ValueKind::Format(vm_try!(value.try_clone())), - _ => { - return VmResult::Ok(vm_try!(caller.call_protocol_fn( - Protocol::CLONE, - self.clone(), - &mut () - ))); - } + 'fallback: { + let value = match vm_try!(self.borrow_ref()) { + ValueBorrowRef::Inline(value) => { + return VmResult::Ok(Self { + repr: Repr::Inline(*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())), + Mutable::Object(value) => Mutable::Object(vm_try!(value.try_clone())), + Mutable::RangeFrom(value) => Mutable::RangeFrom(vm_try!(value.try_clone())), + Mutable::RangeFull(value) => Mutable::RangeFull(vm_try!(value.try_clone())), + Mutable::RangeInclusive(value) => { + Mutable::RangeInclusive(vm_try!(value.try_clone())) + } + Mutable::RangeToInclusive(value) => { + Mutable::RangeToInclusive(vm_try!(value.try_clone())) + } + Mutable::RangeTo(value) => Mutable::RangeTo(vm_try!(value.try_clone())), + Mutable::Range(value) => Mutable::Range(vm_try!(value.try_clone())), + Mutable::ControlFlow(value) => Mutable::ControlFlow(vm_try!(value.try_clone())), + Mutable::Stream(value) => Mutable::Stream(vm_try!(value.try_clone())), + Mutable::Generator(value) => Mutable::Generator(vm_try!(value.try_clone())), + Mutable::GeneratorState(value) => { + Mutable::GeneratorState(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())), + Mutable::TupleStruct(value) => Mutable::TupleStruct(vm_try!(value.try_clone())), + Mutable::Struct(value) => Mutable::Struct(vm_try!(value.try_clone())), + Mutable::Variant(value) => Mutable::Variant(vm_try!(value.try_clone())), + Mutable::Function(value) => Mutable::Function(vm_try!(value.try_clone())), + Mutable::Format(value) => Mutable::Format(vm_try!(value.try_clone())), + _ => { + break 'fallback; + } + }, + }; + + return VmResult::Ok(Self { + repr: Repr::Mutable(vm_try!(Shared::new(value))), + }); }; - VmResult::Ok(Self { - repr: ValueRepr::Value(vm_try!(Shared::new(kind))), - }) + VmResult::Ok(vm_try!(caller.call_protocol_fn( + Protocol::CLONE, + self.clone(), + &mut () + ))) } /// Debug format the value using the [`STRING_DEBUG`] protocol. @@ -790,127 +438,121 @@ impl Value { f: &mut Formatter, caller: &mut dyn ProtocolCaller, ) -> VmResult<()> { - let value = match self.repr { - ValueRepr::Empty => { - vm_write!(f, ""); - return VmResult::Ok(()); - } - ValueRepr::Value(ref value) => value, + 'fallback: { + let value = match self.repr { + Repr::Empty => { + vm_write!(f, ""); + return VmResult::Ok(()); + } + Repr::Inline(value) => { + vm_write!(f, "{value:?}"); + return VmResult::Ok(()); + } + Repr::Mutable(ref value) => value, + }; + + match &*vm_try!(value.borrow_ref()) { + Mutable::String(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::Bytes(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::Vec(value) => { + vm_try!(Vec::string_debug_with(value, f, caller)); + } + Mutable::Tuple(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::Object(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::RangeFrom(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::RangeFull(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::RangeInclusive(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::RangeToInclusive(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::RangeTo(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::Range(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::ControlFlow(value) => { + vm_try!(ControlFlow::string_debug_with(value, f, caller)); + } + Mutable::Future(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::Stream(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::Generator(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::GeneratorState(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::Option(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::Result(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::EmptyStruct(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::TupleStruct(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::Struct(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::Variant(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::Function(value) => { + vm_write!(f, "{:?}", value); + } + Mutable::Format(value) => { + vm_write!(f, "{:?}", value); + } + _ => { + break 'fallback; + } + }; + + return VmResult::Ok(()); }; - match &*vm_try!(value.borrow_ref()) { - ValueKind::EmptyTuple => { - vm_write!(f, "()"); - } - ValueKind::Bool(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Byte(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Char(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Integer(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Float(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Type(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::String(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Bytes(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Vec(value) => { - vm_try!(Vec::string_debug_with(value, f, caller)); - } - ValueKind::Tuple(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Object(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::RangeFrom(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::RangeFull(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::RangeInclusive(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::RangeToInclusive(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::RangeTo(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Range(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::ControlFlow(value) => { - vm_try!(ControlFlow::string_debug_with(value, f, caller)); - } - ValueKind::Future(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Stream(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Generator(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::GeneratorState(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Option(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Result(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::EmptyStruct(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::TupleStruct(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Struct(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Variant(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Function(value) => { - vm_write!(f, "{:?}", value); - } - ValueKind::Format(value) => { - vm_write!(f, "{:?}", value); + // reborrow f to avoid moving it + let mut args = DynGuardedArgs::new((&mut *f,)); + + match vm_try!(caller.try_call_protocol_fn(Protocol::STRING_DEBUG, self.clone(), &mut args)) + { + CallResultOnly::Ok(value) => { + vm_try!(<()>::from_value(value)); } - _ => { - // reborrow f to avoid moving it - let mut args = DynGuardedArgs::new((&mut *f,)); - - match vm_try!(caller.try_call_protocol_fn( - Protocol::STRING_DEBUG, - self.clone(), - &mut args - )) { - CallResultOnly::Ok(value) => { - vm_try!(<()>::from_value(value)); - } - CallResultOnly::Unsupported(value) => { - let type_info = vm_try!(value.borrow_kind_ref()).type_info(); - vm_write!(f, "<{} object at {:p}>", type_info, value.repr); - } + CallResultOnly::Unsupported(value) => match &value.repr { + Repr::Empty => { + vm_write!(f, ""); } - } - }; + Repr::Inline(value) => { + vm_write!(f, "{value:?}"); + } + Repr::Mutable(value) => { + let ty = vm_try!(value.borrow_ref()).type_info(); + vm_write!(f, "<{ty} object at {value:p}>"); + } + }, + } VmResult::Ok(()) } @@ -1008,7 +650,7 @@ impl Value { /// Drop the interior value. pub(crate) fn drop(self) -> VmResult<()> { - if let ValueRepr::Value(value) = self.repr { + if let Repr::Mutable(value) = self.repr { drop(vm_try!(value.take())); } @@ -1018,10 +660,10 @@ impl Value { /// Move the interior value. pub(crate) fn move_(self) -> VmResult { match self.repr { - ValueRepr::Empty => VmResult::Ok(Self::empty()), - ValueRepr::Value(value) => VmResult::Ok(Value { - repr: ValueRepr::Value(vm_try!(Shared::new(vm_try!(value.take())))), + Repr::Mutable(value) => VmResult::Ok(Value { + repr: Repr::Mutable(vm_try!(Shared::new(vm_try!(value.take())))), }), + repr => VmResult::Ok(Value { repr }), } } @@ -1041,24 +683,30 @@ impl Value { } /// Borrow the value of a string as a reference. - #[inline] pub fn borrow_string_ref(&self) -> Result, RuntimeError> { - let result = BorrowRef::try_map(self.borrow_kind_ref()?, |kind| match kind { - ValueKind::String(string) => Some(string.as_str()), + 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())), + Err(actual) => Err(RuntimeError::expected::(actual.type_info())), } } /// Take the current value as a string. #[inline] pub fn into_string(self) -> Result { - match self.take_kind()? { - ValueKind::String(string) => Ok(string), + match self.take_value()? { + OwnedValue::Mutable(Mutable::String(string)) => Ok(string), actual => Err(RuntimeError::expected::(actual.type_info())), } } @@ -1067,95 +715,82 @@ impl Value { #[doc(hidden)] #[inline] pub fn into_type_value(self) -> Result { - match self.take_kind()? { - ValueKind::EmptyTuple => Ok(TypeValue::EmptyTuple), - ValueKind::Tuple(tuple) => Ok(TypeValue::Tuple(tuple)), - ValueKind::Object(object) => Ok(TypeValue::Object(object)), - ValueKind::EmptyStruct(empty) => Ok(TypeValue::EmptyStruct(empty)), - ValueKind::TupleStruct(tuple) => Ok(TypeValue::TupleStruct(tuple)), - ValueKind::Struct(object) => Ok(TypeValue::Struct(object)), - ValueKind::Variant(object) => Ok(TypeValue::Variant(object)), - kind => Ok(TypeValue::NotTyped(NotTypedValueKind(kind))), + match self.take_value()? { + OwnedValue::Inline(value) => match value { + Inline::Unit => Ok(TypeValue::Unit), + actual => Ok(TypeValue::NotTypedInline(NotTypedInlineValue(actual))), + }, + OwnedValue::Mutable(value) => match value { + Mutable::Tuple(tuple) => Ok(TypeValue::Tuple(tuple)), + 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)), + Mutable::Variant(object) => Ok(TypeValue::Variant(object)), + actual => Ok(TypeValue::NotTypedMutable(NotTypedMutableValue(actual))), + }, } } /// Coerce into a unit. #[inline] pub fn into_unit(&self) -> Result<(), RuntimeError> { - match *self.borrow_kind_ref()? { - ValueKind::EmptyTuple => Ok(()), - ref actual => Err(RuntimeError::expected::<()>(actual.type_info())), + match self.borrow_ref()? { + ValueBorrowRef::Inline(Inline::Unit) => Ok(()), + ValueBorrowRef::Inline(actual) => Err(RuntimeError::expected::<()>(actual.type_info())), + ValueBorrowRef::Mutable(actual) => { + Err(RuntimeError::expected::<()>(actual.type_info())) + } } } - copy_into! { + inline_into! { /// Coerce into [`Ordering`]. Ordering(Ordering), - into_ordering_ref, - into_ordering_mut, - borrow_ordering_ref, - borrow_ordering_mut, as_ordering, + as_ordering_mut, } - copy_into! { + inline_into! { /// Coerce into [`bool`]. Bool(bool), - into_bool_ref, - into_bool_mut, - borrow_bool_ref, - borrow_bool_mut, as_bool, + as_bool_mut, } - copy_into! { + inline_into! { /// Coerce into [`u8`] byte. Byte(u8), - into_byte_ref, - into_byte_mut, - borrow_byte_ref, - borrow_byte_mut, as_byte, + as_byte_mut, } - copy_into! { + inline_into! { /// Coerce into [`char`]. Char(char), - into_char_ref, - into_char_mut, - borrow_char_ref, - borrow_char_mut, as_char, + as_char_mut, } - copy_into! { + inline_into! { /// Coerce into [`i64`] integer. Integer(i64), - into_integer_ref, - into_integer_mut, - borrow_integer_ref, - borrow_integer_mut, as_integer, + as_integer_mut, } - copy_into! { + inline_into! { /// Coerce into [`f64`] float. Float(f64), - into_float_ref, - into_float_mut, - borrow_float_ref, - borrow_float_mut, as_float, + as_float_mut, } - copy_into! { + inline_into! { /// Coerce into [`Type`]. Type(Type), - into_type_ref, - into_type_mut, - borrow_type_ref, - borrow_type_mut, as_type, + as_type_mut, } clone_into! { @@ -1362,8 +997,8 @@ impl Value { /// This consumes the underlying value. #[inline] pub fn into_any_obj(self) -> Result { - match self.take_kind()? { - ValueKind::Any(value) => Ok(value), + match self.take_value()? { + OwnedValue::Mutable(Mutable::Any(value)) => Ok(value), ref actual => Err(RuntimeError::expected_any(actual.type_info())), } } @@ -1381,13 +1016,15 @@ impl Value { /// environment. #[inline] pub fn into_future(self) -> VmResult { - let target = match vm_try!(self.take_kind()) { - ValueKind::Future(future) => return VmResult::Ok(future), - target => vm_try!(Value::try_from(target)), + let target = match vm_try!(self.take_value()) { + OwnedValue::Mutable(Mutable::Future(future)) => return VmResult::Ok(future), + OwnedValue::Inline(value) => Value::from(value), + OwnedValue::Mutable(value) => vm_try!(Value::try_from(value)), }; let value = vm_try!(EnvProtocolCaller.call_protocol_fn(Protocol::INTO_FUTURE, target, &mut ())); + VmResult::Ok(vm_try!(Future::from_value(value))) } @@ -1397,8 +1034,15 @@ impl Value { where T: Any, { - let result = Ref::try_map(self.into_kind_ref()?, |kind| match kind { - ValueKind::Any(any) => Some(any), + let value = match self.into_repr()? { + ValueRepr::Mutable(value) => value.into_ref()?, + ValueRepr::Inline(actual) => { + return Err(RuntimeError::expected_any(actual.type_info())); + } + }; + + let result = Ref::try_map(value, |value| match value { + Mutable::Any(any) => Some(any), _ => None, }); @@ -1427,8 +1071,15 @@ impl Value { where T: Any, { - let result = Mut::try_map(self.into_kind_mut()?, |kind| match kind { - ValueKind::Any(any) => Some(any), + let value = match self.into_repr()? { + ValueRepr::Mutable(value) => value.into_mut()?, + ValueRepr::Inline(actual) => { + return Err(RuntimeError::expected_any(actual.type_info())); + } + }; + + let result = Mut::try_map(value, |value| match value { + Mutable::Any(any) => Some(any), _ => None, }); @@ -1457,8 +1108,15 @@ impl Value { where T: Any, { - let result = BorrowRef::try_map(self.borrow_kind_ref()?, |kind| match kind { - ValueKind::Any(any) => any.downcast_borrow_ref().ok(), + let value = match self.value_ref()? { + ValueRef::Mutable(value) => value.borrow_ref()?, + ValueRef::Inline(actual) => { + return Err(RuntimeError::expected_any(actual.type_info())); + } + }; + + let result = BorrowRef::try_map(value, |value| match value { + Mutable::Any(any) => any.downcast_borrow_ref().ok(), _ => None, }); @@ -1474,8 +1132,15 @@ impl Value { where T: Any, { - let result = BorrowMut::try_map(self.borrow_kind_mut()?, |kind| match kind { - ValueKind::Any(any) => any.downcast_borrow_mut().ok(), + let value = match self.value_ref()? { + ValueRef::Mutable(value) => value.borrow_mut()?, + ValueRef::Inline(actual) => { + return Err(RuntimeError::expected_any(actual.type_info())); + } + }; + + let result = BorrowMut::try_map(value, |value| match value { + Mutable::Any(any) => any.downcast_borrow_mut().ok(), _ => None, }); @@ -1491,10 +1156,8 @@ impl Value { where T: Any, { - let value = self.into_value_kind()?; - - let any = match value.take()? { - ValueKind::Any(any) => any, + let any = match self.take_value()? { + OwnedValue::Mutable(Mutable::Any(any)) => any, actual => return Err(RuntimeError::expected_any(actual.type_info())), }; @@ -1515,12 +1178,20 @@ impl Value { /// One notable feature is that the type of a variant is its container /// *enum*, and not the type hash of the variant itself. pub fn type_hash(&self) -> Result { - Ok(self.borrow_kind_ref()?.type_hash()) + match &self.repr { + Repr::Inline(value) => Ok(value.type_hash()), + Repr::Mutable(value) => Ok(value.borrow_ref()?.type_hash()), + Repr::Empty => Err(AccessError::empty()), + } } /// Get the type information for the current value. pub fn type_info(&self) -> Result { - Ok(self.borrow_kind_ref()?.type_info()) + match &self.repr { + Repr::Inline(value) => Ok(value.type_info()), + Repr::Mutable(value) => Ok(value.borrow_ref()?.type_info()), + Repr::Empty => Err(AccessError::empty()), + } } /// Perform a partial equality test between two values. @@ -1540,95 +1211,116 @@ impl Value { /// Perform a total equality test between two values. /// /// This is the basis for the eq operation (`partial_eq` / '=='). + #[cfg_attr(feature = "bench", inline(never))] pub(crate) fn partial_eq_with( &self, b: &Value, caller: &mut dyn ProtocolCaller, ) -> VmResult { - { - 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::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); + 'fallback: { + let a = vm_try!(self.borrow_ref()); + + let a = match (&a, vm_try!(b.borrow_ref())) { + (ValueBorrowRef::Inline(a), ValueBorrowRef::Inline(b)) => { + return a.partial_eq(b); } - (ValueKind::ControlFlow(a), ValueKind::ControlFlow(b)) => { - return ControlFlow::partial_eq_with(a, b, caller); + (ValueBorrowRef::Inline(lhs), rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: Protocol::PARTIAL_EQ.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); } - (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); + (ValueBorrowRef::Mutable(a), ValueBorrowRef::Mutable(b2)) => match (&**a, &*b2) { + (Mutable::Bytes(a), Mutable::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); + (Mutable::RangeFrom(a), Mutable::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); + (Mutable::RangeFull(a), Mutable::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); + (Mutable::RangeInclusive(a), Mutable::RangeInclusive(b)) => { + return RangeInclusive::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), + (Mutable::RangeToInclusive(a), Mutable::RangeToInclusive(b)) => { + return RangeToInclusive::partial_eq_with(a, b, caller); + } + (Mutable::RangeTo(a), Mutable::RangeTo(b)) => { + return RangeTo::partial_eq_with(a, b, caller); + } + (Mutable::Range(a), Mutable::Range(b)) => { + return Range::partial_eq_with(a, b, caller); + } + (Mutable::ControlFlow(a), Mutable::ControlFlow(b)) => { + return ControlFlow::partial_eq_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 + // the VmError below since it's otherwise a comparison + // between two incompatible types. + // + // Other than that, all units are equal. + return VmResult::Ok(true); + } + + break 'fallback; + } + (Mutable::TupleStruct(a), Mutable::TupleStruct(b)) => { + if a.rtti.hash == b.rtti.hash { + return Vec::eq_with(&a.data, &b.data, Value::partial_eq_with, caller); + } + + break 'fallback; + } + (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, + ); + } + + break 'fallback; + } + (Mutable::Variant(a), Mutable::Variant(b)) => { + if a.rtti().enum_hash == b.rtti().enum_hash { + return Variant::partial_eq_with(a, b, caller); + } + + 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), + _ => return VmResult::Ok(false), + }, + (Mutable::Result(a), Mutable::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), + }, + (a, _) => a, }, - _ => {} - } + _ => break 'fallback, + }; - match &*a { - ValueKind::Vec(a) => { + // Special cases. + match a { + Mutable::Vec(a) => { return Vec::partial_eq_with(a, b.clone(), caller); } - ValueKind::Tuple(a) => { + Mutable::Tuple(a) => { return Vec::partial_eq_with(a, b.clone(), caller); } - ValueKind::Object(a) => { + Mutable::Object(a) => { return Object::partial_eq_with(a, b.clone(), caller); } _ => {} @@ -1644,7 +1336,7 @@ impl Value { } err(VmErrorKind::UnsupportedBinaryOperation { - op: "partial_eq", + op: Protocol::PARTIAL_EQ.name, lhs: vm_try!(self.type_info()), rhs: vm_try!(b.type_info()), }) @@ -1657,47 +1349,58 @@ impl Value { } /// Hash the current value. + #[cfg_attr(feature = "bench", inline(never))] pub(crate) fn hash_with( &self, hasher: &mut Hasher, caller: &mut dyn ProtocolCaller, ) -> VmResult<()> { - match &*vm_try!(self.borrow_kind_ref()) { - ValueKind::Integer(value) => { - hasher.write_i64(*value); - return VmResult::Ok(()); - } - ValueKind::Byte(value) => { - hasher.write_u8(*value); - return VmResult::Ok(()); - } - // Care must be taken whan hashing floats, to ensure that `hash(v1) - // === hash(v2)` if `eq(v1) === eq(v2)`. Hopefully we accomplish - // this by rejecting NaNs and rectifying subnormal values of zero. - ValueKind::Float(value) => { - if value.is_nan() { - return VmResult::err(VmErrorKind::IllegalFloatOperation { value: *value }); + match vm_try!(self.borrow_ref()) { + ValueBorrowRef::Inline(value) => match value { + Inline::Integer(value) => { + hasher.write_i64(*value); + return VmResult::Ok(()); + } + Inline::Byte(value) => { + hasher.write_u8(*value); + return VmResult::Ok(()); } + // Care must be taken whan hashing floats, to ensure that `hash(v1) + // === hash(v2)` if `eq(v1) === eq(v2)`. Hopefully we accomplish + // this by rejecting NaNs and rectifying subnormal values of zero. + Inline::Float(value) => { + if value.is_nan() { + return VmResult::err(VmErrorKind::IllegalFloatOperation { value: *value }); + } - let zero = *value == 0.0; - hasher.write_f64((zero as u8 as f64) * 0.0 + (!zero as u8 as f64) * *value); - return VmResult::Ok(()); - } - ValueKind::String(string) => { - hasher.write_str(string); - return VmResult::Ok(()); - } - ValueKind::Bytes(bytes) => { - hasher.write(bytes); - return VmResult::Ok(()); - } - ValueKind::Tuple(tuple) => { - return Tuple::hash_with(tuple, hasher, caller); - } - ValueKind::Vec(vec) => { - return Vec::hash_with(vec, hasher, caller); - } - _ => {} + let zero = *value == 0.0; + hasher.write_f64((zero as u8 as f64) * 0.0 + (!zero as u8 as f64) * *value); + return VmResult::Ok(()); + } + operand => { + return err(VmErrorKind::UnsupportedUnaryOperation { + op: Protocol::HASH.name, + operand: operand.type_info(), + }); + } + }, + ValueBorrowRef::Mutable(value) => match &*value { + Mutable::String(string) => { + hasher.write_str(string); + return VmResult::Ok(()); + } + Mutable::Bytes(bytes) => { + hasher.write(bytes); + return VmResult::Ok(()); + } + Mutable::Tuple(tuple) => { + return Tuple::hash_with(tuple, hasher, caller); + } + Mutable::Vec(vec) => { + return Vec::hash_with(vec, hasher, caller); + } + _ => {} + }, } let mut args = DynGuardedArgs::new((hasher,)); @@ -1709,7 +1412,7 @@ impl Value { } err(VmErrorKind::UnsupportedUnaryOperation { - op: "hash", + op: Protocol::HASH.name, operand: vm_try!(self.type_info()), }) } @@ -1731,94 +1434,92 @@ impl Value { /// Perform a total equality test between two values. /// /// This is the basis for the eq operation (`==`). + #[cfg_attr(feature = "bench", inline(never))] pub(crate) fn eq_with(&self, b: &Value, caller: &mut dyn ProtocolCaller) -> VmResult { - match ( - &*vm_try!(self.borrow_kind_ref()), - &*vm_try!(b.borrow_kind_ref()), - ) { - (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::Float(a), ValueKind::Float(b)) => { - let Some(ordering) = a.partial_cmp(b) else { - return VmResult::err(VmErrorKind::IllegalFloatComparison { lhs: *a, rhs: *b }); - }; - - return VmResult::Ok(matches!(ordering, Ordering::Equal)); - } - (ValueKind::Integer(a), ValueKind::Integer(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), ValueKind::Vec(b)) => { - return Vec::eq_with(a, b, Value::eq_with, caller); - } - (ValueKind::EmptyTuple, ValueKind::EmptyTuple) => return VmResult::Ok(true), - (ValueKind::Tuple(a), ValueKind::Tuple(b)) => { - return Vec::eq_with(a, b, Value::eq_with, caller); - } - (ValueKind::Object(a), ValueKind::Object(b)) => { - return Object::eq_with(a, b, Value::eq_with, caller); - } - (ValueKind::RangeFrom(a), ValueKind::RangeFrom(b)) => { - return RangeFrom::eq_with(a, b, caller); - } - (ValueKind::RangeFull(a), ValueKind::RangeFull(b)) => { - return RangeFull::eq_with(a, b, caller); - } - (ValueKind::RangeInclusive(a), ValueKind::RangeInclusive(b)) => { - return RangeInclusive::eq_with(a, b, caller); - } - (ValueKind::RangeToInclusive(a), ValueKind::RangeToInclusive(b)) => { - return RangeToInclusive::eq_with(a, b, caller); - } - (ValueKind::RangeTo(a), ValueKind::RangeTo(b)) => { - return RangeTo::eq_with(a, b, caller); - } - (ValueKind::Range(a), ValueKind::Range(b)) => { - return Range::eq_with(a, b, caller); - } - (ValueKind::ControlFlow(a), ValueKind::ControlFlow(b)) => { - return ControlFlow::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); + match (vm_try!(self.borrow_ref()), vm_try!(b.borrow_ref())) { + (ValueBorrowRef::Inline(a), ValueBorrowRef::Inline(b)) => { + return a.eq(b); + } + (ValueBorrowRef::Inline(lhs), rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: Protocol::EQ.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + (ValueBorrowRef::Mutable(a), ValueBorrowRef::Mutable(b)) => match (&*a, &*b) { + (Mutable::Bytes(a), Mutable::Bytes(b)) => { + return VmResult::Ok(*a == *b); + } + (Mutable::Vec(a), Mutable::Vec(b)) => { + return Vec::eq_with(a, b, Value::eq_with, caller); + } + (Mutable::Tuple(a), Mutable::Tuple(b)) => { + return Vec::eq_with(a, b, Value::eq_with, caller); + } + (Mutable::Object(a), Mutable::Object(b)) => { + return Object::eq_with(a, b, Value::eq_with, caller); + } + (Mutable::RangeFrom(a), Mutable::RangeFrom(b)) => { + return RangeFrom::eq_with(a, b, caller); + } + (Mutable::RangeFull(a), Mutable::RangeFull(b)) => { + return RangeFull::eq_with(a, b, caller); } - } - (ValueKind::TupleStruct(a), ValueKind::TupleStruct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Vec::eq_with(&a.data, &b.data, Value::eq_with, caller); + (Mutable::RangeInclusive(a), Mutable::RangeInclusive(b)) => { + return RangeInclusive::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::eq_with, caller); + (Mutable::RangeToInclusive(a), Mutable::RangeToInclusive(b)) => { + return RangeToInclusive::eq_with(a, b, caller); } - } - (ValueKind::Variant(a), ValueKind::Variant(b)) => { - if a.rtti().enum_hash == b.rtti().enum_hash { - return Variant::eq_with(a, b, caller); + (Mutable::RangeTo(a), Mutable::RangeTo(b)) => { + return RangeTo::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::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::eq_with(a, b, caller), - (Err(a), Err(b)) => return Value::eq_with(a, b, caller), - _ => return VmResult::Ok(false), + (Mutable::Range(a), Mutable::Range(b)) => { + return Range::eq_with(a, b, caller); + } + (Mutable::ControlFlow(a), Mutable::ControlFlow(b)) => { + return ControlFlow::eq_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 + // the VmError below since it's otherwise a comparison + // between two incompatible types. + // + // Other than that, all units are equal. + return VmResult::Ok(true); + } + } + (Mutable::TupleStruct(a), Mutable::TupleStruct(b)) => { + if a.rtti.hash == b.rtti.hash { + return Vec::eq_with(&a.data, &b.data, Value::eq_with, caller); + } + } + (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); + } + } + (Mutable::Variant(a), Mutable::Variant(b)) => { + if a.rtti().enum_hash == b.rtti().enum_hash { + 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), + _ => return VmResult::Ok(false), + }, + (Mutable::Result(a), Mutable::Result(b)) => match (a, b) { + (Ok(a), Ok(b)) => return Value::eq_with(a, b, caller), + (Err(a), Err(b)) => return Value::eq_with(a, b, caller), + _ => return VmResult::Ok(false), + }, + _ => {} }, _ => {} } @@ -1832,7 +1533,7 @@ impl Value { } err(VmErrorKind::UnsupportedBinaryOperation { - op: "eq", + op: Protocol::EQ.name, lhs: vm_try!(self.type_info()), rhs: vm_try!(b.type_info()), }) @@ -1855,95 +1556,93 @@ impl Value { /// Perform a partial ordering comparison between two values. /// /// This is the basis for the comparison operation. + #[cfg_attr(feature = "bench", inline(never))] pub(crate) fn partial_cmp_with( &self, b: &Value, caller: &mut dyn ProtocolCaller, ) -> VmResult> { - match ( - &*vm_try!(self.borrow_kind_ref()), - &*vm_try!(b.borrow_kind_ref()), - ) { - (ValueKind::EmptyTuple, ValueKind::EmptyTuple) => { - return VmResult::Ok(Some(Ordering::Equal)) - } - (ValueKind::Bool(a), ValueKind::Bool(b)) => return VmResult::Ok(a.partial_cmp(b)), - (ValueKind::Byte(a), ValueKind::Byte(b)) => return VmResult::Ok(a.partial_cmp(b)), - (ValueKind::Char(a), ValueKind::Char(b)) => return VmResult::Ok(a.partial_cmp(b)), - (ValueKind::Float(a), ValueKind::Float(b)) => return VmResult::Ok(a.partial_cmp(b)), - (ValueKind::Integer(a), ValueKind::Integer(b)) => { - return VmResult::Ok(a.partial_cmp(b)); - } - (ValueKind::Type(a), ValueKind::Type(b)) => return VmResult::Ok(a.partial_cmp(b)), - (ValueKind::Bytes(a), ValueKind::Bytes(b)) => { - return VmResult::Ok(a.partial_cmp(b)); - } - (ValueKind::Vec(a), ValueKind::Vec(b)) => { - return Vec::partial_cmp_with(a, b, caller); - } - (ValueKind::Tuple(a), ValueKind::Tuple(b)) => { - return Vec::partial_cmp_with(a, b, caller); - } - (ValueKind::Object(a), ValueKind::Object(b)) => { - return Object::partial_cmp_with(a, b, caller); - } - (ValueKind::RangeFrom(a), ValueKind::RangeFrom(b)) => { - return RangeFrom::partial_cmp_with(a, b, caller); - } - (ValueKind::RangeFull(a), ValueKind::RangeFull(b)) => { - return RangeFull::partial_cmp_with(a, b, caller); - } - (ValueKind::RangeInclusive(a), ValueKind::RangeInclusive(b)) => { - return RangeInclusive::partial_cmp_with(a, b, caller); - } - (ValueKind::RangeToInclusive(a), ValueKind::RangeToInclusive(b)) => { - return RangeToInclusive::partial_cmp_with(a, b, caller); - } - (ValueKind::RangeTo(a), ValueKind::RangeTo(b)) => { - return RangeTo::partial_cmp_with(a, b, caller); - } - (ValueKind::Range(a), ValueKind::Range(b)) => { - return Range::partial_cmp_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(Some(Ordering::Equal)); + match (vm_try!(self.borrow_ref()), vm_try!(b.borrow_ref())) { + (ValueBorrowRef::Inline(a), ValueBorrowRef::Inline(b)) => return a.partial_cmp(b), + (ValueBorrowRef::Inline(lhs), rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: Protocol::PARTIAL_CMP.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }) + } + (ValueBorrowRef::Mutable(a), ValueBorrowRef::Mutable(b)) => match (&*a, &*b) { + (Mutable::Bytes(a), Mutable::Bytes(b)) => { + return VmResult::Ok(a.partial_cmp(b)); } - } - (ValueKind::TupleStruct(a), ValueKind::TupleStruct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Vec::partial_cmp_with(&a.data, &b.data, caller); + (Mutable::Vec(a), Mutable::Vec(b)) => { + return Vec::partial_cmp_with(a, b, caller); } - } - (ValueKind::Struct(a), ValueKind::Struct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Object::partial_cmp_with(&a.data, &b.data, caller); + (Mutable::Tuple(a), Mutable::Tuple(b)) => { + return Vec::partial_cmp_with(a, b, caller); } - } - (ValueKind::Variant(a), ValueKind::Variant(b)) => { - if a.rtti().enum_hash == b.rtti().enum_hash { - return Variant::partial_cmp_with(a, b, caller); + (Mutable::Object(a), Mutable::Object(b)) => { + return Object::partial_cmp_with(a, b, caller); } - } - (ValueKind::String(a), ValueKind::String(b)) => { - return VmResult::Ok(a.partial_cmp(b)); - } - (ValueKind::Option(a), ValueKind::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)), - (Some(..), None) => return VmResult::Ok(Some(Ordering::Greater)), - (None, Some(..)) => return VmResult::Ok(Some(Ordering::Less)), - }, - (ValueKind::Result(a), ValueKind::Result(b)) => match (a, b) { - (Ok(a), Ok(b)) => return Value::partial_cmp_with(a, b, caller), - (Err(a), Err(b)) => return Value::partial_cmp_with(a, b, caller), - (Ok(..), Err(..)) => return VmResult::Ok(Some(Ordering::Greater)), - (Err(..), Ok(..)) => return VmResult::Ok(Some(Ordering::Less)), + (Mutable::RangeFrom(a), Mutable::RangeFrom(b)) => { + return RangeFrom::partial_cmp_with(a, b, caller); + } + (Mutable::RangeFull(a), Mutable::RangeFull(b)) => { + return RangeFull::partial_cmp_with(a, b, caller); + } + (Mutable::RangeInclusive(a), Mutable::RangeInclusive(b)) => { + return RangeInclusive::partial_cmp_with(a, b, caller); + } + (Mutable::RangeToInclusive(a), Mutable::RangeToInclusive(b)) => { + return RangeToInclusive::partial_cmp_with(a, b, caller); + } + (Mutable::RangeTo(a), Mutable::RangeTo(b)) => { + return RangeTo::partial_cmp_with(a, b, caller); + } + (Mutable::Range(a), Mutable::Range(b)) => { + return Range::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 + // the VmError below since it's otherwise a comparison + // between two incompatible types. + // + // Other than that, all units are equal. + return VmResult::Ok(Some(Ordering::Equal)); + } + } + (Mutable::TupleStruct(a), Mutable::TupleStruct(b)) => { + if a.rtti.hash == b.rtti.hash { + return Vec::partial_cmp_with(&a.data, &b.data, caller); + } + } + (Mutable::Struct(a), Mutable::Struct(b)) => { + if a.rtti.hash == b.rtti.hash { + return Object::partial_cmp_with(&a.data, &b.data, caller); + } + } + (Mutable::Variant(a), Mutable::Variant(b)) => { + if a.rtti().enum_hash == b.rtti().enum_hash { + 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)), + (Some(..), None) => return VmResult::Ok(Some(Ordering::Greater)), + (None, Some(..)) => return VmResult::Ok(Some(Ordering::Less)), + }, + (Mutable::Result(a), Mutable::Result(b)) => match (a, b) { + (Ok(a), Ok(b)) => return Value::partial_cmp_with(a, b, caller), + (Err(a), Err(b)) => return Value::partial_cmp_with(a, b, caller), + (Ok(..), Err(..)) => return VmResult::Ok(Some(Ordering::Greater)), + (Err(..), Ok(..)) => return VmResult::Ok(Some(Ordering::Less)), + }, + _ => {} }, _ => {} } @@ -1957,7 +1656,7 @@ impl Value { } err(VmErrorKind::UnsupportedBinaryOperation { - op: "partial_cmp", + op: Protocol::PARTIAL_CMP.name, lhs: vm_try!(self.type_info()), rhs: vm_try!(b.type_info()), }) @@ -1980,98 +1679,94 @@ impl Value { /// Perform a total ordering comparison between two values. /// /// This is the basis for the comparison operation (`cmp`). + #[cfg_attr(feature = "bench", inline(never))] pub(crate) fn cmp_with( &self, b: &Value, caller: &mut dyn ProtocolCaller, ) -> VmResult { - match ( - &*vm_try!(self.borrow_kind_ref()), - &*vm_try!(b.borrow_kind_ref()), - ) { - (ValueKind::EmptyTuple, ValueKind::EmptyTuple) => return VmResult::Ok(Ordering::Equal), - (ValueKind::Bool(a), ValueKind::Bool(b)) => return VmResult::Ok(a.cmp(b)), - (ValueKind::Byte(a), ValueKind::Byte(b)) => return VmResult::Ok(a.cmp(b)), - (ValueKind::Char(a), ValueKind::Char(b)) => return VmResult::Ok(a.cmp(b)), - (ValueKind::Float(a), ValueKind::Float(b)) => { - let Some(ordering) = a.partial_cmp(b) else { - return VmResult::err(VmErrorKind::IllegalFloatComparison { lhs: *a, rhs: *b }); - }; - - return VmResult::Ok(ordering); - } - (ValueKind::Integer(a), ValueKind::Integer(b)) => return VmResult::Ok(a.cmp(b)), - (ValueKind::Type(a), ValueKind::Type(b)) => return VmResult::Ok(a.cmp(b)), - (ValueKind::Bytes(a), ValueKind::Bytes(b)) => { - return VmResult::Ok(a.cmp(b)); - } - (ValueKind::Vec(a), ValueKind::Vec(b)) => { - return Vec::cmp_with(a, b, caller); - } - (ValueKind::Tuple(a), ValueKind::Tuple(b)) => { - return Vec::cmp_with(a, b, caller); - } - (ValueKind::Object(a), ValueKind::Object(b)) => { - return Object::cmp_with(a, b, caller); - } - (ValueKind::RangeFrom(a), ValueKind::RangeFrom(b)) => { - return RangeFrom::cmp_with(a, b, caller); - } - (ValueKind::RangeFull(a), ValueKind::RangeFull(b)) => { - return RangeFull::cmp_with(a, b, caller); - } - (ValueKind::RangeInclusive(a), ValueKind::RangeInclusive(b)) => { - return RangeInclusive::cmp_with(a, b, caller); - } - (ValueKind::RangeToInclusive(a), ValueKind::RangeToInclusive(b)) => { - return RangeToInclusive::cmp_with(a, b, caller); - } - (ValueKind::RangeTo(a), ValueKind::RangeTo(b)) => { - return RangeTo::cmp_with(a, b, caller); - } - (ValueKind::Range(a), ValueKind::Range(b)) => { - return Range::cmp_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(Ordering::Equal); + match (vm_try!(self.borrow_ref()), vm_try!(b.borrow_ref())) { + (ValueBorrowRef::Inline(a), ValueBorrowRef::Inline(b)) => return a.cmp(b), + (ValueBorrowRef::Mutable(a), ValueBorrowRef::Mutable(b)) => match (&*a, &*b) { + (Mutable::Bytes(a), Mutable::Bytes(b)) => { + return VmResult::Ok(a.cmp(b)); } - } - (ValueKind::TupleStruct(a), ValueKind::TupleStruct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Vec::cmp_with(&a.data, &b.data, caller); + (Mutable::Vec(a), Mutable::Vec(b)) => { + return Vec::cmp_with(a, b, caller); } - } - (ValueKind::Struct(a), ValueKind::Struct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Object::cmp_with(&a.data, &b.data, caller); + (Mutable::Tuple(a), Mutable::Tuple(b)) => { + return Vec::cmp_with(a, b, caller); } - } - (ValueKind::Variant(a), ValueKind::Variant(b)) => { - if a.rtti().enum_hash == b.rtti().enum_hash { - return Variant::cmp_with(a, b, caller); + (Mutable::Object(a), Mutable::Object(b)) => { + return Object::cmp_with(a, b, caller); } - } - (ValueKind::String(a), ValueKind::String(b)) => { - return VmResult::Ok(a.cmp(b)); - } - (ValueKind::Option(a), ValueKind::Option(b)) => match (a, b) { - (Some(a), Some(b)) => return Value::cmp_with(a, b, caller), - (None, None) => return VmResult::Ok(Ordering::Equal), - (Some(..), None) => return VmResult::Ok(Ordering::Greater), - (None, Some(..)) => return VmResult::Ok(Ordering::Less), - }, - (ValueKind::Result(a), ValueKind::Result(b)) => match (a, b) { - (Ok(a), Ok(b)) => return Value::cmp_with(a, b, caller), - (Err(a), Err(b)) => return Value::cmp_with(a, b, caller), - (Ok(..), Err(..)) => return VmResult::Ok(Ordering::Greater), - (Err(..), Ok(..)) => return VmResult::Ok(Ordering::Less), + (Mutable::RangeFrom(a), Mutable::RangeFrom(b)) => { + return RangeFrom::cmp_with(a, b, caller); + } + (Mutable::RangeFull(a), Mutable::RangeFull(b)) => { + return RangeFull::cmp_with(a, b, caller); + } + (Mutable::RangeInclusive(a), Mutable::RangeInclusive(b)) => { + return RangeInclusive::cmp_with(a, b, caller); + } + (Mutable::RangeToInclusive(a), Mutable::RangeToInclusive(b)) => { + return RangeToInclusive::cmp_with(a, b, caller); + } + (Mutable::RangeTo(a), Mutable::RangeTo(b)) => { + return RangeTo::cmp_with(a, b, caller); + } + (Mutable::Range(a), Mutable::Range(b)) => { + return Range::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 + // the VmError below since it's otherwise a comparison + // between two incompatible types. + // + // Other than that, all units are equal. + return VmResult::Ok(Ordering::Equal); + } + } + (Mutable::TupleStruct(a), Mutable::TupleStruct(b)) => { + if a.rtti.hash == b.rtti.hash { + return Vec::cmp_with(&a.data, &b.data, caller); + } + } + (Mutable::Struct(a), Mutable::Struct(b)) => { + if a.rtti.hash == b.rtti.hash { + return Object::cmp_with(&a.data, &b.data, caller); + } + } + (Mutable::Variant(a), Mutable::Variant(b)) => { + if a.rtti().enum_hash == b.rtti().enum_hash { + 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), + (Some(..), None) => return VmResult::Ok(Ordering::Greater), + (None, Some(..)) => return VmResult::Ok(Ordering::Less), + }, + (Mutable::Result(a), Mutable::Result(b)) => match (a, b) { + (Ok(a), Ok(b)) => return Value::cmp_with(a, b, caller), + (Err(a), Err(b)) => return Value::cmp_with(a, b, caller), + (Ok(..), Err(..)) => return VmResult::Ok(Ordering::Greater), + (Err(..), Ok(..)) => return VmResult::Ok(Ordering::Less), + }, + _ => {} }, + (ValueBorrowRef::Inline(lhs), rhs) => { + return VmResult::err(VmErrorKind::UnsupportedBinaryOperation { + op: Protocol::CMP.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } _ => {} } @@ -2084,7 +1779,7 @@ impl Value { } err(VmErrorKind::UnsupportedBinaryOperation { - op: "cmp", + op: Protocol::CMP.name, lhs: vm_try!(self.type_info()), rhs: vm_try!(b.type_info()), }) @@ -2122,17 +1817,74 @@ impl Value { } } - fn into_value_kind(self) -> Result, AccessError> { + pub(crate) fn as_inline_unchecked(&self) -> Option<&Inline> { + match &self.repr { + Repr::Inline(value) => Some(value), + _ => None, + } + } + + pub(crate) fn as_inline(&self) -> Result, AccessError> { + match &self.repr { + Repr::Inline(value) => Ok(Some(value)), + Repr::Mutable(..) => Ok(None), + Repr::Empty => Err(AccessError::empty()), + } + } + + pub(crate) fn as_inline_mut(&mut self) -> Result, AccessError> { + match &mut self.repr { + Repr::Inline(value) => Ok(Some(value)), + Repr::Mutable(..) => Ok(None), + Repr::Empty => Err(AccessError::empty()), + } + } + + pub(crate) fn take_value(self) -> Result { + match self.repr { + Repr::Inline(value) => Ok(OwnedValue::Inline(value)), + Repr::Mutable(value) => Ok(OwnedValue::Mutable(value.take()?)), + Repr::Empty => Err(AccessError::empty()), + } + } + + pub(crate) fn into_repr(self) -> Result { match self.repr { - ValueRepr::Value(value) => Ok(value), - ValueRepr::Empty => Err(AccessError::empty()), + Repr::Inline(value) => Ok(ValueRepr::Inline(value)), + Repr::Mutable(value) => Ok(ValueRepr::Mutable(value)), + Repr::Empty => Err(AccessError::empty()), } } - fn as_value_kind(&self) -> Result<&Shared, AccessError> { + pub(crate) fn borrow_ref(&self) -> Result, AccessError> { match &self.repr { - ValueRepr::Value(value) => Ok(value), - ValueRepr::Empty => Err(AccessError::empty()), + Repr::Inline(value) => Ok(ValueBorrowRef::Inline(value)), + Repr::Mutable(value) => Ok(ValueBorrowRef::Mutable(value.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)), + Repr::Mutable(value) => Ok(ValueRef::Mutable(value)), + Repr::Empty => Err(AccessError::empty()), + } + } + + pub(crate) fn value_mut(&mut self) -> Result, AccessError> { + match &mut self.repr { + Repr::Inline(value) => Ok(ValueMut::Inline(value)), + Repr::Mutable(mutable) => Ok(ValueMut::Mutable(mutable)), + Repr::Empty => Err(AccessError::empty()), + } + } + + pub(crate) fn into_value_shared(self) -> Result { + match self.repr { + Repr::Inline(value) => Ok(ValueShared::Inline(value)), + Repr::Mutable(value) => Ok(ValueShared::Mutable(value)), + Repr::Empty => Err(AccessError::empty()), } } @@ -2182,11 +1934,15 @@ impl Value { impl fmt::Debug for Value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let snapshot = match &self.repr { - ValueRepr::Empty => { + Repr::Empty => { write!(f, "")?; return Ok(()); } - ValueRepr::Value(value) => value.snapshot(), + Repr::Inline(value) => { + write!(f, "{value:?}")?; + return Ok(()); + } + Repr::Mutable(value) => value.snapshot(), }; if !snapshot.is_readable() { @@ -2196,14 +1952,23 @@ impl fmt::Debug for Value { let mut o = Formatter::new(); - if self.string_debug(&mut o).is_err() { - match self.type_info() { - Ok(type_info) => { - write!(f, "<{} object at {:p}>", type_info, self.repr)?; + if let Err(e) = self.string_debug(&mut o).into_result() { + match &self.repr { + Repr::Empty => { + write!(f, "")?; } - Err(e) => { - write!(f, "", self.repr, e)?; + Repr::Inline(value) => { + write!(f, "<{value:?}: {e}>")?; } + Repr::Mutable(value) => match value.borrow_ref() { + Ok(v) => { + let ty = v.type_info(); + write!(f, "<{ty} object at {value:p}: {e}>")?; + } + Err(e2) => { + write!(f, "")?; + } + }, } return Ok(()); @@ -2214,12 +1979,10 @@ impl fmt::Debug for Value { } } -impl TryFrom<()> for Value { - type Error = alloc::Error; - +impl From<()> for Value { #[inline] - fn try_from((): ()) -> Result { - Value::try_from(ValueKind::EmptyTuple) + fn from((): ()) -> Self { + Value::from(Inline::Unit) } } @@ -2232,13 +1995,22 @@ impl IntoOutput for () { } } -impl TryFrom for Value { +impl From for Value { + #[inline] + fn from(value: Inline) -> Self { + Self { + repr: Repr::Inline(value), + } + } +} + +impl TryFrom for Value { type Error = alloc::Error; #[inline] - fn try_from(kind: ValueKind) -> Result { + fn try_from(value: Mutable) -> Result { Ok(Self { - repr: ValueRepr::Value(Shared::new(kind)?), + repr: Repr::Mutable(Shared::new(value)?), }) } } @@ -2250,62 +2022,7 @@ impl ToValue for Value { } } -macro_rules! impl_from { - ($($variant:ident => $ty:ty),* $(,)*) => { - $( - impl TryFrom<$ty> for Value { - type Error = alloc::Error; - - #[inline] - fn try_from(value: $ty) -> Result { - Value::try_from(ValueKind::$variant(value)) - } - } - - impl IntoOutput for $ty { - type Output = $ty; - - #[inline] - fn into_output(self) -> VmResult { - VmResult::Ok(self) - } - } - - impl ToValue for $ty { - #[inline] - fn to_value(self) -> VmResult { - VmResult::Ok(vm_try!(Value::try_from(self))) - } - } - )* - }; -} - -macro_rules! impl_custom_from_wrapper { - ($($variant:ident => $ty:ty),* $(,)?) => { - $( - impl TryFrom<$ty> for Value { - type Error = alloc::Error; - - #[inline] - fn try_from(value: $ty) -> Result { - Value::try_from(ValueKind::$variant(value)) - } - } - - impl IntoOutput for $ty { - type Output = $ty; - - #[inline] - fn into_output(self) -> VmResult { - VmResult::Ok(self) - } - } - )* - }; -} - -impl_from! { +inline_from! { Byte => u8, Bool => bool, Char => char, @@ -2313,6 +2030,9 @@ impl_from! { Float => f64, Type => Type, Ordering => Ordering, +} + +from! { String => String, Bytes => Bytes, ControlFlow => ControlFlow, @@ -2338,7 +2058,7 @@ impl_from! { Any => AnyObj, } -impl_custom_from_wrapper! { +from_container! { Option => Option, Result => Result, } @@ -2359,14 +2079,18 @@ impl TryClone for Value { /// Wrapper for a value kind. #[doc(hidden)] -pub struct NotTypedValueKind(ValueKind); +pub struct NotTypedInlineValue(Inline); + +/// Wrapper for a value kind. +#[doc(hidden)] +pub struct NotTypedMutableValue(Mutable); /// The coersion of a value into a typed value. #[doc(hidden)] #[non_exhaustive] pub enum TypeValue { /// The unit value. - EmptyTuple, + Unit, /// A tuple. Tuple(OwnedTuple), /// An object. @@ -2379,9 +2103,12 @@ pub enum TypeValue { Struct(Struct), /// The variant of an enum. Variant(Variant), + /// Not a typed immutable value. + #[doc(hidden)] + NotTypedInline(NotTypedInlineValue), /// Not a typed value. #[doc(hidden)] - NotTyped(NotTypedValueKind), + NotTypedMutable(NotTypedMutableValue), } impl TypeValue { @@ -2389,23 +2116,23 @@ impl TypeValue { #[doc(hidden)] pub fn type_info(&self) -> TypeInfo { match self { - TypeValue::EmptyTuple => TypeInfo::StaticType(crate::runtime::static_type::TUPLE), - TypeValue::Tuple(..) => TypeInfo::StaticType(crate::runtime::static_type::TUPLE), - TypeValue::Object(..) => TypeInfo::StaticType(crate::runtime::static_type::OBJECT), + TypeValue::Unit => TypeInfo::StaticType(static_type::TUPLE), + TypeValue::Tuple(..) => TypeInfo::StaticType(static_type::TUPLE), + TypeValue::Object(..) => TypeInfo::StaticType(static_type::OBJECT), 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::NotTyped(kind) => kind.0.type_info(), + TypeValue::NotTypedInline(value) => value.0.type_info(), + TypeValue::NotTypedMutable(value) => value.0.type_info(), } } } -#[doc(hidden)] -#[non_exhaustive] -pub(crate) enum ValueKind { +#[derive(Clone, Copy)] +pub(crate) enum Inline { /// The unit value. - EmptyTuple, + Unit, /// A boolean. Bool(bool), /// A single byte. @@ -2420,6 +2147,146 @@ pub(crate) enum ValueKind { Type(Type), /// Ordering. Ordering(Ordering), +} + +impl Inline { + /// Perform a partial equality check over two inline values. + pub(crate) fn partial_eq(&self, other: &Self) -> VmResult { + match (self, other) { + (Inline::Unit, Inline::Unit) => VmResult::Ok(true), + (Inline::Bool(a), Inline::Bool(b)) => VmResult::Ok(*a == *b), + (Inline::Byte(a), Inline::Byte(b)) => VmResult::Ok(*a == *b), + (Inline::Char(a), Inline::Char(b)) => VmResult::Ok(*a == *b), + (Inline::Integer(a), Inline::Integer(b)) => VmResult::Ok(*a == *b), + (Inline::Float(a), Inline::Float(b)) => VmResult::Ok(*a == *b), + (Inline::Type(a), Inline::Type(b)) => VmResult::Ok(*a == *b), + (Inline::Ordering(a), Inline::Ordering(b)) => VmResult::Ok(*a == *b), + (lhs, rhs) => err(VmErrorKind::UnsupportedBinaryOperation { + op: Protocol::PARTIAL_EQ.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }), + } + } + + /// Perform a total equality check over two inline values. + pub(crate) fn eq(&self, other: &Self) -> VmResult { + match (self, other) { + (Inline::Unit, Inline::Unit) => VmResult::Ok(true), + (Inline::Bool(a), Inline::Bool(b)) => VmResult::Ok(*a == *b), + (Inline::Byte(a), Inline::Byte(b)) => VmResult::Ok(*a == *b), + (Inline::Char(a), Inline::Char(b)) => VmResult::Ok(*a == *b), + (Inline::Float(a), Inline::Float(b)) => { + let Some(ordering) = a.partial_cmp(b) else { + return VmResult::err(VmErrorKind::IllegalFloatComparison { lhs: *a, rhs: *b }); + }; + + VmResult::Ok(matches!(ordering, Ordering::Equal)) + } + (Inline::Integer(a), Inline::Integer(b)) => VmResult::Ok(*a == *b), + (Inline::Type(a), Inline::Type(b)) => VmResult::Ok(*a == *b), + (Inline::Ordering(a), Inline::Ordering(b)) => VmResult::Ok(*a == *b), + (lhs, rhs) => err(VmErrorKind::UnsupportedBinaryOperation { + op: Protocol::EQ.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }), + } + } + + /// Partial comparison implementation for inline. + pub(crate) fn partial_cmp(&self, other: &Self) -> VmResult> { + match (self, other) { + (Inline::Unit, Inline::Unit) => VmResult::Ok(Some(Ordering::Equal)), + (Inline::Bool(lhs), Inline::Bool(rhs)) => VmResult::Ok(lhs.partial_cmp(rhs)), + (Inline::Byte(lhs), Inline::Byte(rhs)) => VmResult::Ok(lhs.partial_cmp(rhs)), + (Inline::Char(lhs), Inline::Char(rhs)) => VmResult::Ok(lhs.partial_cmp(rhs)), + (Inline::Float(lhs), Inline::Float(rhs)) => VmResult::Ok(lhs.partial_cmp(rhs)), + (Inline::Integer(lhs), Inline::Integer(rhs)) => VmResult::Ok(lhs.partial_cmp(rhs)), + (Inline::Type(lhs), Inline::Type(rhs)) => VmResult::Ok(lhs.partial_cmp(rhs)), + (Inline::Ordering(lhs), Inline::Ordering(rhs)) => VmResult::Ok(lhs.partial_cmp(rhs)), + (lhs, rhs) => err(VmErrorKind::UnsupportedBinaryOperation { + op: Protocol::PARTIAL_CMP.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }), + } + } + + /// Total comparison implementation for inline. + pub(crate) fn cmp(&self, other: &Self) -> VmResult { + match (self, other) { + (Inline::Unit, Inline::Unit) => VmResult::Ok(Ordering::Equal), + (Inline::Bool(a), Inline::Bool(b)) => VmResult::Ok(a.cmp(b)), + (Inline::Byte(a), Inline::Byte(b)) => VmResult::Ok(a.cmp(b)), + (Inline::Char(a), Inline::Char(b)) => VmResult::Ok(a.cmp(b)), + (Inline::Float(a), Inline::Float(b)) => { + let Some(ordering) = a.partial_cmp(b) else { + return VmResult::err(VmErrorKind::IllegalFloatComparison { lhs: *a, rhs: *b }); + }; + + VmResult::Ok(ordering) + } + (Inline::Integer(a), Inline::Integer(b)) => VmResult::Ok(a.cmp(b)), + (Inline::Type(a), Inline::Type(b)) => VmResult::Ok(a.cmp(b)), + (Inline::Ordering(a), Inline::Ordering(b)) => VmResult::Ok(a.cmp(b)), + (lhs, rhs) => VmResult::err(VmErrorKind::UnsupportedBinaryOperation { + op: Protocol::CMP.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }), + } + } +} + +impl fmt::Debug for Inline { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Inline::Unit => write!(f, "()"), + Inline::Bool(value) => value.fmt(f), + Inline::Byte(value) => value.fmt(f), + Inline::Char(value) => value.fmt(f), + Inline::Integer(value) => value.fmt(f), + Inline::Float(value) => value.fmt(f), + Inline::Type(value) => value.fmt(f), + Inline::Ordering(value) => value.fmt(f), + } + } +} + +impl Inline { + pub(crate) fn type_info(&self) -> TypeInfo { + match self { + Inline::Unit => TypeInfo::StaticType(static_type::TUPLE), + Inline::Bool(..) => TypeInfo::StaticType(static_type::BOOL), + Inline::Byte(..) => TypeInfo::StaticType(static_type::BYTE), + Inline::Char(..) => TypeInfo::StaticType(static_type::CHAR), + Inline::Integer(..) => TypeInfo::StaticType(static_type::INTEGER), + Inline::Float(..) => TypeInfo::StaticType(static_type::FLOAT), + Inline::Type(..) => TypeInfo::StaticType(static_type::TYPE), + Inline::Ordering(..) => TypeInfo::StaticType(static_type::ORDERING), + } + } + + /// Get the type hash for the current value. + /// + /// One notable feature is that the type of a variant is its container + /// *enum*, and not the type hash of the variant itself. + pub(crate) fn type_hash(&self) -> Hash { + match self { + Inline::Unit => static_type::TUPLE.hash, + Inline::Bool(..) => static_type::BOOL.hash, + Inline::Byte(..) => static_type::BYTE.hash, + Inline::Char(..) => static_type::CHAR.hash, + Inline::Integer(..) => static_type::INTEGER.hash, + Inline::Float(..) => static_type::FLOAT.hash, + Inline::Type(..) => static_type::TYPE.hash, + Inline::Ordering(..) => static_type::ORDERING.hash, + } + } +} + +pub(crate) enum Mutable { /// A UTF-8 string. String(String), /// A byte string. @@ -2472,56 +2339,34 @@ pub(crate) enum ValueKind { Any(AnyObj), } -impl ValueKind { +impl Mutable { pub(crate) fn type_info(&self) -> TypeInfo { match self { - ValueKind::Bool(..) => TypeInfo::StaticType(crate::runtime::static_type::BOOL), - ValueKind::Byte(..) => TypeInfo::StaticType(crate::runtime::static_type::BYTE), - ValueKind::Char(..) => TypeInfo::StaticType(crate::runtime::static_type::CHAR), - ValueKind::Integer(..) => TypeInfo::StaticType(crate::runtime::static_type::INTEGER), - ValueKind::Float(..) => TypeInfo::StaticType(crate::runtime::static_type::FLOAT), - ValueKind::Type(..) => TypeInfo::StaticType(crate::runtime::static_type::TYPE), - ValueKind::Ordering(..) => TypeInfo::StaticType(crate::runtime::static_type::ORDERING), - ValueKind::String(..) => TypeInfo::StaticType(crate::runtime::static_type::STRING), - ValueKind::Bytes(..) => TypeInfo::StaticType(crate::runtime::static_type::BYTES), - ValueKind::Vec(..) => TypeInfo::StaticType(crate::runtime::static_type::VEC), - ValueKind::EmptyTuple => TypeInfo::StaticType(crate::runtime::static_type::TUPLE), - ValueKind::Tuple(..) => TypeInfo::StaticType(crate::runtime::static_type::TUPLE), - ValueKind::Object(..) => TypeInfo::StaticType(crate::runtime::static_type::OBJECT), - ValueKind::RangeFrom(..) => { - TypeInfo::StaticType(crate::runtime::static_type::RANGE_FROM) - } - ValueKind::RangeFull(..) => { - TypeInfo::StaticType(crate::runtime::static_type::RANGE_FULL) - } - ValueKind::RangeInclusive(..) => { - TypeInfo::StaticType(crate::runtime::static_type::RANGE_INCLUSIVE) - } - ValueKind::RangeToInclusive(..) => { - TypeInfo::StaticType(crate::runtime::static_type::RANGE_TO_INCLUSIVE) - } - ValueKind::RangeTo(..) => TypeInfo::StaticType(crate::runtime::static_type::RANGE_TO), - ValueKind::Range(..) => TypeInfo::StaticType(crate::runtime::static_type::RANGE), - ValueKind::ControlFlow(..) => { - TypeInfo::StaticType(crate::runtime::static_type::CONTROL_FLOW) - } - ValueKind::Future(..) => TypeInfo::StaticType(crate::runtime::static_type::FUTURE), - ValueKind::Stream(..) => TypeInfo::StaticType(crate::runtime::static_type::STREAM), - ValueKind::Generator(..) => { - TypeInfo::StaticType(crate::runtime::static_type::GENERATOR) - } - ValueKind::GeneratorState(..) => { - TypeInfo::StaticType(crate::runtime::static_type::GENERATOR_STATE) - } - ValueKind::Option(..) => TypeInfo::StaticType(crate::runtime::static_type::OPTION), - ValueKind::Result(..) => TypeInfo::StaticType(crate::runtime::static_type::RESULT), - ValueKind::Function(..) => TypeInfo::StaticType(crate::runtime::static_type::FUNCTION), - ValueKind::Format(..) => TypeInfo::StaticType(crate::runtime::static_type::FORMAT), - ValueKind::EmptyStruct(empty) => empty.type_info(), - ValueKind::TupleStruct(tuple) => tuple.type_info(), - ValueKind::Struct(object) => object.type_info(), - ValueKind::Variant(empty) => empty.type_info(), - ValueKind::Any(any) => any.type_info(), + Mutable::String(..) => TypeInfo::StaticType(static_type::STRING), + Mutable::Bytes(..) => TypeInfo::StaticType(static_type::BYTES), + Mutable::Vec(..) => TypeInfo::StaticType(static_type::VEC), + Mutable::Tuple(..) => TypeInfo::StaticType(static_type::TUPLE), + Mutable::Object(..) => TypeInfo::StaticType(static_type::OBJECT), + Mutable::RangeFrom(..) => TypeInfo::StaticType(static_type::RANGE_FROM), + Mutable::RangeFull(..) => TypeInfo::StaticType(static_type::RANGE_FULL), + Mutable::RangeInclusive(..) => TypeInfo::StaticType(static_type::RANGE_INCLUSIVE), + Mutable::RangeToInclusive(..) => TypeInfo::StaticType(static_type::RANGE_TO_INCLUSIVE), + Mutable::RangeTo(..) => TypeInfo::StaticType(static_type::RANGE_TO), + Mutable::Range(..) => TypeInfo::StaticType(static_type::RANGE), + Mutable::ControlFlow(..) => TypeInfo::StaticType(static_type::CONTROL_FLOW), + Mutable::Future(..) => TypeInfo::StaticType(static_type::FUTURE), + Mutable::Stream(..) => TypeInfo::StaticType(static_type::STREAM), + Mutable::Generator(..) => TypeInfo::StaticType(static_type::GENERATOR), + Mutable::GeneratorState(..) => TypeInfo::StaticType(static_type::GENERATOR_STATE), + Mutable::Option(..) => TypeInfo::StaticType(static_type::OPTION), + Mutable::Result(..) => TypeInfo::StaticType(static_type::RESULT), + Mutable::Function(..) => TypeInfo::StaticType(static_type::FUNCTION), + Mutable::Format(..) => TypeInfo::StaticType(static_type::FORMAT), + Mutable::EmptyStruct(empty) => empty.type_info(), + Mutable::TupleStruct(tuple) => tuple.type_info(), + Mutable::Struct(object) => object.type_info(), + Mutable::Variant(empty) => empty.type_info(), + Mutable::Any(any) => any.type_info(), } } @@ -2531,52 +2376,39 @@ impl ValueKind { /// *enum*, and not the type hash of the variant itself. pub(crate) fn type_hash(&self) -> Hash { match self { - ValueKind::Bool(..) => crate::runtime::static_type::BOOL.hash, - ValueKind::Byte(..) => crate::runtime::static_type::BYTE.hash, - ValueKind::Char(..) => crate::runtime::static_type::CHAR.hash, - ValueKind::Integer(..) => crate::runtime::static_type::INTEGER.hash, - ValueKind::Float(..) => crate::runtime::static_type::FLOAT.hash, - ValueKind::Type(..) => crate::runtime::static_type::TYPE.hash, - ValueKind::Ordering(..) => crate::runtime::static_type::ORDERING.hash, - ValueKind::String(..) => crate::runtime::static_type::STRING.hash, - ValueKind::Bytes(..) => crate::runtime::static_type::BYTES.hash, - ValueKind::Vec(..) => crate::runtime::static_type::VEC.hash, - ValueKind::EmptyTuple => crate::runtime::static_type::TUPLE.hash, - ValueKind::Tuple(..) => crate::runtime::static_type::TUPLE.hash, - ValueKind::Object(..) => crate::runtime::static_type::OBJECT.hash, - ValueKind::RangeFrom(..) => crate::runtime::static_type::RANGE_FROM.hash, - ValueKind::RangeFull(..) => crate::runtime::static_type::RANGE_FULL.hash, - ValueKind::RangeInclusive(..) => crate::runtime::static_type::RANGE_INCLUSIVE.hash, - ValueKind::RangeToInclusive(..) => crate::runtime::static_type::RANGE_TO_INCLUSIVE.hash, - ValueKind::RangeTo(..) => crate::runtime::static_type::RANGE_TO.hash, - ValueKind::Range(..) => crate::runtime::static_type::RANGE.hash, - ValueKind::ControlFlow(..) => crate::runtime::static_type::CONTROL_FLOW.hash, - ValueKind::Future(..) => crate::runtime::static_type::FUTURE.hash, - ValueKind::Stream(..) => crate::runtime::static_type::STREAM.hash, - ValueKind::Generator(..) => crate::runtime::static_type::GENERATOR.hash, - ValueKind::GeneratorState(..) => crate::runtime::static_type::GENERATOR_STATE.hash, - ValueKind::Result(..) => crate::runtime::static_type::RESULT.hash, - ValueKind::Option(..) => crate::runtime::static_type::OPTION.hash, - ValueKind::Function(..) => crate::runtime::static_type::FUNCTION.hash, - ValueKind::Format(..) => crate::runtime::static_type::FORMAT.hash, - ValueKind::EmptyStruct(empty) => empty.rtti.hash, - ValueKind::TupleStruct(tuple) => tuple.rtti.hash, - ValueKind::Struct(object) => object.rtti.hash, - ValueKind::Variant(variant) => variant.rtti().enum_hash, - ValueKind::Any(any) => any.type_hash(), + Mutable::String(..) => static_type::STRING.hash, + Mutable::Bytes(..) => static_type::BYTES.hash, + Mutable::Vec(..) => static_type::VEC.hash, + Mutable::Tuple(..) => static_type::TUPLE.hash, + Mutable::Object(..) => static_type::OBJECT.hash, + Mutable::RangeFrom(..) => static_type::RANGE_FROM.hash, + Mutable::RangeFull(..) => static_type::RANGE_FULL.hash, + Mutable::RangeInclusive(..) => static_type::RANGE_INCLUSIVE.hash, + Mutable::RangeToInclusive(..) => static_type::RANGE_TO_INCLUSIVE.hash, + Mutable::RangeTo(..) => static_type::RANGE_TO.hash, + Mutable::Range(..) => static_type::RANGE.hash, + Mutable::ControlFlow(..) => static_type::CONTROL_FLOW.hash, + Mutable::Future(..) => static_type::FUTURE.hash, + Mutable::Stream(..) => static_type::STREAM.hash, + Mutable::Generator(..) => static_type::GENERATOR.hash, + Mutable::GeneratorState(..) => static_type::GENERATOR_STATE.hash, + Mutable::Result(..) => static_type::RESULT.hash, + Mutable::Option(..) => static_type::OPTION.hash, + Mutable::Function(..) => static_type::FUNCTION.hash, + Mutable::Format(..) => static_type::FORMAT.hash, + Mutable::EmptyStruct(empty) => empty.rtti.hash, + Mutable::TupleStruct(tuple) => tuple.rtti.hash, + Mutable::Struct(object) => object.rtti.hash, + Mutable::Variant(variant) => variant.rtti().enum_hash, + Mutable::Any(any) => any.type_hash(), } } } -#[cfg(test)] -mod tests { - use super::Value; - - #[test] - fn test_size() { - assert_eq! { - std::mem::size_of::(), - std::mem::size_of::(), - }; - } +#[test] +fn size_of_value() { + use core::mem::size_of; + assert_eq!(size_of::(), size_of::()); + assert_eq!(size_of::(), size_of::()); + assert_eq!(size_of::>(), size_of::()); } diff --git a/crates/rune/src/runtime/value/data.rs b/crates/rune/src/runtime/value/data.rs new file mode 100644 index 000000000..ee16cdde4 --- /dev/null +++ b/crates/rune/src/runtime/value/data.rs @@ -0,0 +1,145 @@ +use core::borrow::Borrow; +use core::fmt; +use core::hash; + +use rust_alloc::sync::Arc; + +use crate as rune; +use crate::alloc::prelude::*; +use crate::runtime::{Object, OwnedTuple, TypeInfo}; + +use super::{Hash, Rtti, Value}; + +/// A empty with a well-defined type. +#[derive(TryClone)] +#[try_clone(crate)] +pub struct EmptyStruct { + /// The type hash of the empty. + pub(crate) rtti: Arc, +} + +impl EmptyStruct { + /// Access runtime type information. + pub fn rtti(&self) -> &Arc { + &self.rtti + } + + /// Get type info for the typed tuple. + pub fn type_info(&self) -> TypeInfo { + TypeInfo::Typed(self.rtti.clone()) + } +} + +impl fmt::Debug for EmptyStruct { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.rtti.item) + } +} + +/// A tuple with a well-defined type. +#[derive(TryClone)] +pub struct TupleStruct { + /// The type hash of the tuple. + pub(crate) rtti: Arc, + /// Content of the tuple. + pub(crate) data: OwnedTuple, +} + +impl TupleStruct { + /// Access runtime type information. + pub fn rtti(&self) -> &Arc { + &self.rtti + } + + /// Access underlying data. + pub fn data(&self) -> &OwnedTuple { + &self.data + } + + /// Access underlying data mutably. + pub fn data_mut(&mut self) -> &mut OwnedTuple { + &mut self.data + } + + /// Get type info for the typed tuple. + pub fn type_info(&self) -> TypeInfo { + TypeInfo::Typed(self.rtti.clone()) + } + + /// Get the value at the given index in the tuple. + pub fn get(&self, index: usize) -> Option<&Value> { + self.data.get(index) + } + + /// Get the mutable value at the given index in the tuple. + pub fn get_mut(&mut self, index: usize) -> Option<&mut Value> { + self.data.get_mut(index) + } +} + +impl fmt::Debug for TupleStruct { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}{:?}", self.rtti.item, self.data) + } +} + +/// An object with a well-defined type. +#[derive(TryClone)] +pub struct Struct { + /// The type hash of the object. + pub(crate) rtti: Arc, + /// Content of the object. + pub(crate) data: Object, +} + +impl Struct { + /// Access runtime type information. + pub fn rtti(&self) -> &Arc { + &self.rtti + } + + /// Access underlying data. + pub fn data(&self) -> &Object { + &self.data + } + + /// Access underlying data mutably. + pub fn data_mut(&mut self) -> &mut Object { + &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> + where + String: Borrow, + Q: ?Sized + hash::Hash + Eq + Ord, + { + self.data.get(k) + } + + /// Get the given mutable value by key in the object. + pub fn get_mut(&mut self, k: &Q) -> Option<&mut Value> + where + String: Borrow, + Q: ?Sized + hash::Hash + Eq + Ord, + { + self.data.get_mut(k) + } +} + +impl fmt::Debug for Struct { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.data.debug_struct(&self.rtti.item)) + } +} diff --git a/crates/rune/src/runtime/value/macros.rs b/crates/rune/src/runtime/value/macros.rs new file mode 100644 index 000000000..bf65f15fc --- /dev/null +++ b/crates/rune/src/runtime/value/macros.rs @@ -0,0 +1,317 @@ +/// Macro used to generate coersions for [`Value`]. +macro_rules! into_base { + ( + $(#[$($meta:meta)*])* + $kind:ident($ty:ty), + $into_ref:ident, + $into_mut:ident, + $borrow_ref:ident, + $borrow_mut:ident, + ) => { + $(#[$($meta)*])* + /// + /// This ensures that the value has read access to the underlying value + /// and does not consume it. + #[inline] + pub fn $into_ref(self) -> Result, RuntimeError> { + let value = match self.into_repr()? { + ValueRepr::Mutable(value) => value.into_ref()?, + ValueRepr::Inline(actual) => { + return Err(RuntimeError::expected_any(actual.type_info())); + }, + }; + + let result = Ref::try_map(value, |value| match value { + Mutable::$kind(bytes) => Some(bytes), + _ => None, + }); + + match result { + Ok(bytes) => Ok(bytes), + Err(actual) => { + Err(RuntimeError::expected::<$ty>(actual.type_info())) + } + } + } + + $(#[$($meta)*])* + /// + /// This ensures that the value has write access to the underlying value + /// and does not consume it. + #[inline] + pub fn $into_mut(self) -> Result, RuntimeError> { + let value = match self.into_repr()? { + ValueRepr::Mutable(value) => value.into_mut()?, + ValueRepr::Inline(actual) => { + return Err(RuntimeError::expected_any(actual.type_info())); + }, + }; + + let result = Mut::try_map(value, |value| match value { + Mutable::$kind(bytes) => Some(bytes), + _ => None, + }); + + match result { + Ok(bytes) => Ok(bytes), + Err(actual) => { + Err(RuntimeError::expected::<$ty>(actual.type_info())) + } + } + } + + $(#[$($meta)*])* + /// + /// This ensures that the value has read access to the underlying value + /// and does not consume it. + #[inline] + pub fn $borrow_ref(&self) -> Result, RuntimeError> { + let value = match self.value_ref()? { + ValueRef::Inline(actual) => { + return Err(RuntimeError::expected::<$ty>(actual.type_info())); + } + ValueRef::Mutable(value) => value, + }; + + let result = BorrowRef::try_map(value.borrow_ref()?, |kind| match kind { + Mutable::$kind(bytes) => Some(bytes), + _ => None, + }); + + match result { + Ok(bytes) => Ok(bytes), + Err(actual) => { + Err(RuntimeError::expected::<$ty>(actual.type_info())) + } + } + } + + $(#[$($meta)*])* + /// + /// This ensures that the value has write access to the underlying value + /// and does not consume it. + #[inline] + pub fn $borrow_mut(&self) -> Result, RuntimeError> { + let value = match self.value_ref()? { + ValueRef::Inline(actual) => { + return Err(RuntimeError::expected::<$ty>(actual.type_info())); + } + ValueRef::Mutable(value) => value, + }; + + let result = BorrowMut::try_map(value.borrow_mut()?, |kind| match kind { + Mutable::$kind(bytes) => Some(bytes), + _ => None, + }); + + match result { + Ok(bytes) => Ok(bytes), + Err(actual) => { + Err(RuntimeError::expected::<$ty>(actual.type_info())) + } + } + } + } +} + +macro_rules! into { + ( + $(#[$($meta:meta)*])* + $kind:ident($ty:ty), + $into_ref:ident, + $into_mut:ident, + $borrow_ref:ident, + $borrow_mut:ident, + $into:ident, + ) => { + into_base! { + $(#[$($meta)*])* + $kind($ty), + $into_ref, + $into_mut, + $borrow_ref, + $borrow_mut, + } + + $(#[$($meta)*])* + /// + /// This consumes the underlying value. + #[inline] + pub fn $into(self) -> Result<$ty, RuntimeError> { + match self.take_value()? { + OwnedValue::Mutable(Mutable::$kind(value)) => Ok(value), + actual => { + Err(RuntimeError::expected::<$ty>(actual.type_info())) + } + } + } + } +} + +macro_rules! inline_into { + ( + $(#[$($meta:meta)*])* + $kind:ident($ty:ty), + $as:ident, + $as_mut:ident, + ) => { + $(#[$($meta)*])* + /// + /// This gets a copy of the value. + #[inline] + pub fn $as(&self) -> Result<$ty, RuntimeError> { + match &self.repr { + Repr::Empty => Err(RuntimeError::from(AccessError::empty())), + Repr::Inline(Inline::$kind(value)) => Ok(*value), + Repr::Inline(actual) => { + Err(RuntimeError::expected::<$ty>(actual.type_info())) + } + Repr::Mutable(actual) => { + Err(RuntimeError::expected::<$ty>(actual.borrow_ref()?.type_info())) + } + } + } + + $(#[$($meta)*])* + /// + /// This gets the value by mutable reference. + #[inline] + pub fn $as_mut(&mut self) -> Result<&mut $ty, RuntimeError> { + match &mut self.repr { + Repr::Empty => Err(RuntimeError::from(AccessError::empty())), + Repr::Inline(Inline::$kind(value)) => Ok(value), + Repr::Inline(actual) => { + Err(RuntimeError::expected::<$ty>(actual.type_info())) + } + Repr::Mutable(actual) => { + Err(RuntimeError::expected::<$ty>(actual.borrow_ref()?.type_info())) + } + } + } + } +} + +macro_rules! clone_into { + ( + $(#[$($meta:meta)*])* + $kind:ident($ty:ty), + $into_ref:ident, + $into_mut:ident, + $borrow_ref:ident, + $borrow_mut:ident, + $as:ident, + ) => { + into_base! { + $(#[$($meta)*])* + $kind($ty), + $into_ref, + $into_mut, + $borrow_ref, + $borrow_mut, + } + + $(#[$($meta)*])* + /// + /// This clones the underlying value. + #[inline] + pub fn $as(&self) -> Result<$ty, RuntimeError> { + let value = match self.borrow_ref()? { + ValueBorrowRef::Mutable(value) => value, + actual => { + return Err(RuntimeError::expected::<$ty>(actual.type_info())); + } + }; + + match &*value { + Mutable::$kind(value) => Ok(value.clone()), + actual => { + Err(RuntimeError::expected::<$ty>(actual.type_info())) + } + } + } + } +} + +macro_rules! from { + ($($variant:ident => $ty:ty),* $(,)*) => { + $( + impl TryFrom<$ty> for Value { + type Error = alloc::Error; + + #[inline] + fn try_from(value: $ty) -> Result { + Value::try_from(Mutable::$variant(value)) + } + } + + impl IntoOutput for $ty { + type Output = $ty; + + #[inline] + fn into_output(self) -> VmResult { + VmResult::Ok(self) + } + } + + impl ToValue for $ty { + #[inline] + fn to_value(self) -> VmResult { + VmResult::Ok(vm_try!(Value::try_from(self))) + } + } + )* + }; +} + +macro_rules! inline_from { + ($($variant:ident => $ty:ty),* $(,)*) => { + $( + impl From<$ty> for Value { + #[inline] + fn from(value: $ty) -> Self { + Value::from(Inline::$variant(value)) + } + } + + impl IntoOutput for $ty { + type Output = $ty; + + #[inline] + fn into_output(self) -> VmResult { + VmResult::Ok(self) + } + } + + impl ToValue for $ty { + #[inline] + fn to_value(self) -> VmResult { + VmResult::Ok(Value::from(self)) + } + } + )* + }; +} + +macro_rules! from_container { + ($($variant:ident => $ty:ty),* $(,)?) => { + $( + impl TryFrom<$ty> for Value { + type Error = alloc::Error; + + #[inline] + fn try_from(value: $ty) -> Result { + Value::try_from(Mutable::$variant(value)) + } + } + + impl IntoOutput for $ty { + type Output = $ty; + + #[inline] + fn into_output(self) -> VmResult { + VmResult::Ok(self) + } + } + )* + }; +} diff --git a/crates/rune/src/runtime/value/rtti.rs b/crates/rune/src/runtime/value/rtti.rs new file mode 100644 index 000000000..09187c941 --- /dev/null +++ b/crates/rune/src/runtime/value/rtti.rs @@ -0,0 +1,80 @@ +use core::cmp::Ordering; +use core::hash; + +use serde::{Deserialize, Serialize}; + +use crate::{Hash, ItemBuf}; + +/// Runtime information on variant. +#[derive(Debug, Serialize, Deserialize)] +#[non_exhaustive] +pub struct VariantRtti { + /// The type hash of the enum. + pub enum_hash: Hash, + /// The type variant hash. + pub hash: Hash, + /// The name of the variant. + pub item: ItemBuf, +} + +impl PartialEq for VariantRtti { + fn eq(&self, other: &Self) -> bool { + self.hash == other.hash + } +} + +impl Eq for VariantRtti {} + +impl hash::Hash for VariantRtti { + fn hash(&self, state: &mut H) { + self.hash.hash(state) + } +} + +impl PartialOrd for VariantRtti { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for VariantRtti { + fn cmp(&self, other: &Self) -> Ordering { + self.hash.cmp(&other.hash) + } +} + +/// Runtime information on variant. +#[derive(Debug, Serialize, Deserialize)] +#[non_exhaustive] +pub struct Rtti { + /// The type hash of the type. + pub hash: Hash, + /// The item of the type. + pub item: ItemBuf, +} + +impl PartialEq for Rtti { + fn eq(&self, other: &Self) -> bool { + self.hash == other.hash + } +} + +impl Eq for Rtti {} + +impl hash::Hash for Rtti { + fn hash(&self, state: &mut H) { + self.hash.hash(state) + } +} + +impl PartialOrd for Rtti { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Rtti { + fn cmp(&self, other: &Self) -> Ordering { + self.hash.cmp(&other.hash) + } +} diff --git a/crates/rune/src/runtime/value/serde.rs b/crates/rune/src/runtime/value/serde.rs index eea1ab3b1..35730f28b 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::{Bytes, Object, ValueKind, Vec}; +use crate::runtime::{Bytes, Inline, Mutable, Object, ValueBorrowRef, Vec}; use serde::de::{self, Deserialize as _, Error as _}; use serde::ser::{self, Error as _, SerializeMap as _, SerializeSeq as _}; @@ -25,78 +25,88 @@ impl ser::Serialize for Value { where S: ser::Serializer, { - match &*self.borrow_kind_ref().map_err(S::Error::custom)? { - ValueKind::EmptyTuple => serializer.serialize_unit(), - ValueKind::Bool(b) => serializer.serialize_bool(*b), - ValueKind::Char(c) => serializer.serialize_char(*c), - ValueKind::Byte(c) => serializer.serialize_u8(*c), - ValueKind::Integer(integer) => serializer.serialize_i64(*integer), - ValueKind::Float(float) => serializer.serialize_f64(*float), - ValueKind::Type(..) => Err(ser::Error::custom("cannot serialize types")), - ValueKind::Ordering(..) => Err(ser::Error::custom("cannot serialize orderings")), - ValueKind::String(string) => serializer.serialize_str(string), - ValueKind::Bytes(bytes) => serializer.serialize_bytes(bytes), - ValueKind::Vec(vec) => { - let mut serializer = serializer.serialize_seq(Some(vec.len()))?; - - for value in vec { - serializer.serialize_element(value)?; + match self.borrow_ref().map_err(S::Error::custom)? { + ValueBorrowRef::Inline(value) => match value { + Inline::Unit => serializer.serialize_unit(), + Inline::Bool(b) => serializer.serialize_bool(*b), + Inline::Char(c) => serializer.serialize_char(*c), + Inline::Byte(c) => serializer.serialize_u8(*c), + Inline::Integer(integer) => serializer.serialize_i64(*integer), + Inline::Float(float) => serializer.serialize_f64(*float), + Inline::Type(..) => Err(ser::Error::custom("cannot serialize types")), + 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()))?; + + for value in vec { + serializer.serialize_element(value)?; + } + + serializer.end() } + Mutable::Tuple(tuple) => { + let mut serializer = serializer.serialize_seq(Some(tuple.len()))?; - serializer.end() - } - ValueKind::Tuple(tuple) => { - let mut serializer = serializer.serialize_seq(Some(tuple.len()))?; + for value in tuple.iter() { + serializer.serialize_element(value)?; + } - for value in tuple.iter() { - serializer.serialize_element(value)?; + serializer.end() } + Mutable::Object(object) => { + let mut serializer = serializer.serialize_map(Some(object.len()))?; - serializer.end() - } - ValueKind::Object(object) => { - let mut serializer = serializer.serialize_map(Some(object.len()))?; + for (key, value) in object { + serializer.serialize_entry(key, value)?; + } - for (key, value) in object { - serializer.serialize_entry(key, value)?; + serializer.end() } - - serializer.end() - } - ValueKind::Option(option) => >::serialize(option, serializer), - ValueKind::EmptyStruct(..) => Err(ser::Error::custom("cannot serialize empty structs")), - ValueKind::TupleStruct(..) => Err(ser::Error::custom("cannot serialize tuple structs")), - ValueKind::Struct(..) => Err(ser::Error::custom("cannot serialize objects structs")), - ValueKind::Variant(..) => Err(ser::Error::custom("cannot serialize variants")), - ValueKind::Result(..) => Err(ser::Error::custom("cannot serialize results")), - ValueKind::Future(..) => Err(ser::Error::custom("cannot serialize futures")), - ValueKind::Stream(..) => Err(ser::Error::custom("cannot serialize streams")), - ValueKind::Generator(..) => Err(ser::Error::custom("cannot serialize generators")), - ValueKind::GeneratorState(..) => { - Err(ser::Error::custom("cannot serialize generator states")) - } - ValueKind::Function(..) => { - Err(ser::Error::custom("cannot serialize function pointers")) - } - ValueKind::Format(..) => { - Err(ser::Error::custom("cannot serialize format specifications")) - } - ValueKind::RangeFrom(..) => { - Err(ser::Error::custom("cannot serialize `start..` ranges")) - } - ValueKind::RangeFull(..) => Err(ser::Error::custom("cannot serialize `..` ranges")), - ValueKind::RangeInclusive(..) => { - Err(ser::Error::custom("cannot serialize `start..=end` ranges")) - } - ValueKind::RangeToInclusive(..) => { - Err(ser::Error::custom("cannot serialize `..=end` ranges")) - } - ValueKind::RangeTo(..) => Err(ser::Error::custom("cannot serialize `..end` ranges")), - ValueKind::Range(..) => Err(ser::Error::custom("cannot serialize `start..end` ranges")), - ValueKind::ControlFlow(..) => { - Err(ser::Error::custom("cannot serialize `start..end` ranges")) - } - ValueKind::Any(..) => Err(ser::Error::custom("cannot serialize external objects")), + Mutable::Option(option) => >::serialize(option, serializer), + Mutable::EmptyStruct(..) => { + Err(ser::Error::custom("cannot serialize empty structs")) + } + Mutable::TupleStruct(..) => { + Err(ser::Error::custom("cannot serialize tuple structs")) + } + Mutable::Struct(..) => Err(ser::Error::custom("cannot serialize objects structs")), + Mutable::Variant(..) => Err(ser::Error::custom("cannot serialize variants")), + Mutable::Result(..) => Err(ser::Error::custom("cannot serialize results")), + Mutable::Future(..) => Err(ser::Error::custom("cannot serialize futures")), + Mutable::Stream(..) => Err(ser::Error::custom("cannot serialize streams")), + Mutable::Generator(..) => Err(ser::Error::custom("cannot serialize generators")), + Mutable::GeneratorState(..) => { + Err(ser::Error::custom("cannot serialize generator states")) + } + Mutable::Function(..) => { + Err(ser::Error::custom("cannot serialize function pointers")) + } + Mutable::Format(..) => { + Err(ser::Error::custom("cannot serialize format specifications")) + } + Mutable::RangeFrom(..) => { + Err(ser::Error::custom("cannot serialize `start..` ranges")) + } + Mutable::RangeFull(..) => Err(ser::Error::custom("cannot serialize `..` ranges")), + Mutable::RangeInclusive(..) => { + Err(ser::Error::custom("cannot serialize `start..=end` ranges")) + } + Mutable::RangeToInclusive(..) => { + Err(ser::Error::custom("cannot serialize `..=end` ranges")) + } + Mutable::RangeTo(..) => Err(ser::Error::custom("cannot serialize `..end` ranges")), + Mutable::Range(..) => { + Err(ser::Error::custom("cannot serialize `start..end` ranges")) + } + Mutable::ControlFlow(..) => { + Err(ser::Error::custom("cannot serialize `start..end` ranges")) + } + Mutable::Any(..) => Err(ser::Error::custom("cannot serialize external objects")), + }, } } } @@ -154,7 +164,7 @@ impl<'de> de::Visitor<'de> for VmVisitor { where E: de::Error, { - Value::try_from(v as i64).map_err(E::custom) + Ok(Value::from(v as i64)) } #[inline] @@ -162,7 +172,7 @@ impl<'de> de::Visitor<'de> for VmVisitor { where E: de::Error, { - Value::try_from(v as i64).map_err(E::custom) + Ok(Value::from(v as i64)) } #[inline] @@ -170,7 +180,7 @@ impl<'de> de::Visitor<'de> for VmVisitor { where E: de::Error, { - Value::try_from(v as i64).map_err(E::custom) + Ok(Value::from(v as i64)) } #[inline] @@ -178,7 +188,7 @@ impl<'de> de::Visitor<'de> for VmVisitor { where E: de::Error, { - Value::try_from(v).map_err(E::custom) + Ok(Value::from(v)) } #[inline] @@ -186,7 +196,7 @@ impl<'de> de::Visitor<'de> for VmVisitor { where E: de::Error, { - Value::try_from(v as i64).map_err(E::custom) + Ok(Value::from(v as i64)) } #[inline] @@ -194,7 +204,7 @@ impl<'de> de::Visitor<'de> for VmVisitor { where E: de::Error, { - Value::try_from(v as i64).map_err(E::custom) + Ok(Value::from(v as i64)) } #[inline] @@ -202,7 +212,7 @@ impl<'de> de::Visitor<'de> for VmVisitor { where E: de::Error, { - Value::try_from(v as i64).map_err(E::custom) + Ok(Value::from(v as i64)) } #[inline] @@ -210,7 +220,7 @@ impl<'de> de::Visitor<'de> for VmVisitor { where E: de::Error, { - Value::try_from(v as i64).map_err(E::custom) + Ok(Value::from(v as i64)) } #[inline] @@ -218,7 +228,7 @@ impl<'de> de::Visitor<'de> for VmVisitor { where E: de::Error, { - Value::try_from(v as i64).map_err(E::custom) + Ok(Value::from(v as i64)) } #[inline] @@ -226,7 +236,7 @@ impl<'de> de::Visitor<'de> for VmVisitor { where E: de::Error, { - Value::try_from(v as i64).map_err(E::custom) + Ok(Value::from(v as i64)) } #[inline] @@ -234,7 +244,7 @@ impl<'de> de::Visitor<'de> for VmVisitor { where E: de::Error, { - Value::try_from(v as f64).map_err(E::custom) + Ok(Value::from(v as f64)) } #[inline] @@ -242,7 +252,7 @@ impl<'de> de::Visitor<'de> for VmVisitor { where E: de::Error, { - Value::try_from(v).map_err(E::custom) + Ok(Value::from(v)) } #[inline] @@ -250,7 +260,7 @@ impl<'de> de::Visitor<'de> for VmVisitor { where E: de::Error, { - Value::try_from(v).map_err(E::custom) + Ok(Value::from(v)) } #[inline] @@ -275,7 +285,7 @@ impl<'de> de::Visitor<'de> for VmVisitor { where E: de::Error, { - Value::unit().map_err(E::custom) + Ok(Value::unit()) } #[inline] diff --git a/crates/rune/src/runtime/vec.rs b/crates/rune/src/runtime/vec.rs index d7c5942f2..929bd518d 100644 --- a/crates/rune/src/runtime/vec.rs +++ b/crates/rune/src/runtime/vec.rs @@ -13,8 +13,8 @@ use crate::runtime::slice::Iter; #[cfg(feature = "alloc")] use crate::runtime::Hasher; use crate::runtime::{ - Formatter, FromValue, ProtocolCaller, RawRef, Ref, ToValue, UnsafeToRef, Value, ValueKind, - VmErrorKind, VmResult, + Formatter, FromValue, Mutable, ProtocolCaller, RawRef, Ref, ToValue, UnsafeToRef, Value, + ValueBorrowRef, VmErrorKind, VmResult, }; use crate::Any; @@ -125,6 +125,16 @@ impl Vec { VmResult::Ok(()) } + /// Resizes the `Vec` in-place so that `len` is equal to `new_len`. + /// + /// If `new_len` is greater than `len`, the `Vec` is extended by the + /// difference, with each additional slot filled with `value`. If `new_len` + /// is less than `len`, the `Vec` is simply truncated. + pub fn resize(&mut self, new_len: usize, value: Value) -> VmResult<()> { + vm_try!(self.inner.try_resize(new_len, value)); + VmResult::Ok(()) + } + /// Appends an element to the back of a dynamic vector. pub fn push(&mut self, value: Value) -> alloc::Result<()> { self.inner.try_push(value) @@ -342,31 +352,33 @@ impl Vec { /// types, such as vectors and tuples. pub(crate) fn index_get(this: &[Value], index: Value) -> VmResult> { let slice: Option<&[Value]> = 'out: { - match &*vm_try!(index.borrow_kind_ref()) { - ValueKind::RangeFrom(range) => { - let start = vm_try!(range.start.as_usize()); - break 'out this.get(start..); - } - ValueKind::RangeFull(..) => break 'out this.get(..), - ValueKind::RangeInclusive(range) => { - let start = vm_try!(range.start.as_usize()); - let end = vm_try!(range.end.as_usize()); - break 'out this.get(start..=end); - } - ValueKind::RangeToInclusive(range) => { - let end = vm_try!(range.end.as_usize()); - break 'out this.get(..=end); - } - ValueKind::RangeTo(range) => { - let end = vm_try!(range.end.as_usize()); - break 'out this.get(..end); - } - ValueKind::Range(range) => { - let start = vm_try!(range.start.as_usize()); - let end = vm_try!(range.end.as_usize()); - break 'out this.get(start..end); + if let ValueBorrowRef::Mutable(value) = vm_try!(index.borrow_ref()) { + match &*value { + Mutable::RangeFrom(range) => { + let start = vm_try!(range.start.as_usize()); + break 'out this.get(start..); + } + Mutable::RangeFull(..) => break 'out this.get(..), + Mutable::RangeInclusive(range) => { + let start = vm_try!(range.start.as_usize()); + let end = vm_try!(range.end.as_usize()); + break 'out this.get(start..=end); + } + Mutable::RangeToInclusive(range) => { + let end = vm_try!(range.end.as_usize()); + break 'out this.get(..=end); + } + Mutable::RangeTo(range) => { + let end = vm_try!(range.end.as_usize()); + break 'out this.get(..end); + } + Mutable::Range(range) => { + let start = vm_try!(range.start.as_usize()); + let end = vm_try!(range.end.as_usize()); + break 'out this.get(start..end); + } + _ => {} } - _ => {} }; let index = vm_try!(usize::from_value(index)); diff --git a/crates/rune/src/runtime/vm.rs b/crates/rune/src/runtime/vm.rs index b67c61678..0f583ca8d 100644 --- a/crates/rune/src/runtime/vm.rs +++ b/crates/rune/src/runtime/vm.rs @@ -15,12 +15,12 @@ use crate::runtime::future::SelectFuture; use crate::runtime::unit::{UnitFn, UnitStorage}; use crate::runtime::{ self, Args, Awaited, BorrowMut, Bytes, Call, ControlFlow, DynArgs, DynGuardedArgs, EmptyStruct, - Format, FormatSpec, Formatter, FromValue, Function, Future, Generator, GuardedArgs, Inst, - InstAddress, InstAssignOp, InstOp, InstRange, InstTarget, InstValue, InstVariant, Object, - Output, OwnedTuple, Panic, Protocol, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, - RangeToInclusive, RuntimeContext, Select, Stack, Stream, Struct, Type, TypeCheck, TypeOf, Unit, - Value, ValueKind, Variant, VariantData, Vec, VmError, VmErrorKind, VmExecution, VmHalt, - VmIntegerRepr, VmResult, VmSendExecution, + Format, FormatSpec, Formatter, FromValue, Function, Future, Generator, GuardedArgs, Inline, + Inst, InstAddress, InstAssignOp, InstOp, InstRange, InstTarget, InstValue, InstVariant, + Mutable, Object, Output, OwnedTuple, Pair, Panic, Protocol, Range, RangeFrom, RangeFull, + RangeInclusive, RangeTo, RangeToInclusive, RuntimeContext, Select, Stack, Stream, Struct, Type, + TypeCheck, TypeOf, Unit, Value, ValueBorrowRef, ValueMut, ValueRef, Variant, VariantData, Vec, + VmError, VmErrorKind, VmExecution, VmHalt, VmIntegerRepr, VmResult, VmSendExecution, }; use crate::runtime::{budget, ProtocolCaller}; @@ -115,36 +115,43 @@ enum TargetFallback<'a> { enum TargetValue<'a, 'b> { /// Resolved internal target to mutable value. - Value(&'a Value, Value), + Same(&'a mut Value), + /// Resolved internal target to mutable value. + Pair(&'a mut Value, &'a Value), /// Fallback to a different kind of operation. Fallback(TargetFallback<'b>), } macro_rules! target_value { ($vm:ident, $target:expr, $guard:ident, $lhs:ident, $rhs:ident) => {{ - let rhs = vm_try!($vm.stack.at($rhs)).clone(); - match $target { - InstTarget::Address(addr) => TargetValue::Value(vm_try!($vm.stack.at(addr)), rhs), + InstTarget::Address(addr) => match vm_try!($vm.stack.pair(addr, $rhs)) { + Pair::Same(value) => TargetValue::Same(value), + Pair::Pair(lhs, rhs) => TargetValue::Pair(lhs, rhs), + }, InstTarget::TupleField(lhs, index) => { + let rhs = vm_try!($vm.stack.at($rhs)); + $lhs = vm_try!($vm.stack.at(lhs)).clone(); if let Some(value) = vm_try!(Vm::try_tuple_like_index_get_mut(&$lhs, index)) { $guard = value; - TargetValue::Value(&mut *$guard, rhs) + TargetValue::Pair(&mut *$guard, rhs) } else { - TargetValue::Fallback(TargetFallback::Index(&$lhs, index, rhs)) + TargetValue::Fallback(TargetFallback::Index(&$lhs, index, rhs.clone())) } } InstTarget::Field(lhs, field) => { + let rhs = vm_try!($vm.stack.at($rhs)); + let field = vm_try!($vm.unit.lookup_string(field)); $lhs = vm_try!($vm.stack.at(lhs)).clone(); if let Some(value) = vm_try!(Vm::try_object_like_index_get_mut(&$lhs, field)) { $guard = value; - TargetValue::Value(&mut *$guard, rhs) + TargetValue::Pair(&mut *$guard, rhs) } else { - TargetValue::Fallback(TargetFallback::Field(&$lhs, field.hash(), rhs)) + TargetValue::Fallback(TargetFallback::Field(&$lhs, field.hash(), rhs.clone())) } } } @@ -628,6 +635,7 @@ impl Vm { } /// Helper function to call an instance function. + #[inline] pub(crate) fn call_instance_fn( &mut self, isolated: Isolated, @@ -643,6 +651,7 @@ impl Vm { } /// Helper to call a field function. + #[inline] fn call_field_fn( &mut self, protocol: Protocol, @@ -657,6 +666,7 @@ impl Vm { } /// Helper to call an index function. + #[inline] fn call_index_fn( &mut self, protocol: Protocol, @@ -680,6 +690,7 @@ impl Vm { }) } + #[inline(never)] fn call_hash_with( &mut self, isolated: Isolated, @@ -689,6 +700,19 @@ impl Vm { count: usize, out: Output, ) -> VmResult> { + if let Some(handler) = self.context.function(hash) { + let addr = self.stack.addr(); + + vm_try!(self.called_function_hook(hash)); + vm_try!(self.stack.push(target)); + vm_try!(args.push_to_stack(&mut self.stack)); + + let result = handler(&mut self.stack, addr, count, out); + self.stack.truncate(addr); + vm_try!(result); + return VmResult::Ok(CallResult::Ok(())); + } + if let Some(UnitFn::Offset { offset, call, @@ -713,21 +737,10 @@ impl Vm { } } - if let Some(handler) = self.context.function(hash) { - let addr = self.stack.addr(); - vm_try!(self.called_function_hook(hash)); - vm_try!(self.stack.push(target)); - vm_try!(args.push_to_stack(&mut self.stack)); - - let result = handler(&mut self.stack, addr, count, out); - self.stack.truncate(addr); - vm_try!(result); - return VmResult::Ok(CallResult::Ok(())); - } - VmResult::Ok(CallResult::Unsupported(target)) } + #[cfg_attr(feature = "bench", inline(never))] fn internal_boolean_ops( &mut self, match_ordering: fn(Ordering) -> bool, @@ -735,15 +748,23 @@ impl Vm { rhs: InstAddress, out: Output, ) -> VmResult<()> { - let rhs = vm_try!(self.stack.at(rhs)).clone(); - let lhs = vm_try!(self.stack.at(lhs)).clone(); + let rhs = vm_try!(self.stack.at(rhs)); + let lhs = vm_try!(self.stack.at(lhs)); + + let ordering = match (lhs.as_inline_unchecked(), rhs.as_inline_unchecked()) { + (Some(lhs), Some(rhs)) => vm_try!(lhs.partial_cmp(rhs)), + _ => { + let lhs = lhs.clone(); + let rhs = rhs.clone(); + vm_try!(Value::partial_cmp_with(&lhs, &rhs, self)) + } + }; - let value = match vm_try!(Value::partial_cmp_with(&lhs, &rhs, self)) { + vm_try!(out.store(&mut self.stack, || match ordering { Some(ordering) => match_ordering(ordering), None => false, - }; + })); - vm_try!(out.store(&mut self.stack, || VmResult::Ok(value))); VmResult::Ok(()) } @@ -810,12 +831,14 @@ 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 target = vm_try!(target.borrow_kind_ref()); + let ValueBorrowRef::Mutable(target) = vm_try!(target.borrow_ref()) else { + return VmResult::Ok(None); + }; let value = match &*target { - ValueKind::Object(ref target) => target.get(field), - ValueKind::Struct(ref target) => target.get(field), - ValueKind::Variant(ref variant) => match variant.data() { + 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), }, @@ -836,42 +859,56 @@ impl Vm { fn try_tuple_like_index_get(target: &Value, index: usize) -> VmResult> { use crate::runtime::GeneratorState::*; - let target = vm_try!(target.borrow_kind_ref()); - - let value = match &*target { - ValueKind::EmptyTuple => None, - ValueKind::Tuple(tuple) => tuple.get(index), - ValueKind::Vec(vec) => vec.get(index), - ValueKind::Result(result) => match (index, result) { - (0, Ok(value)) => Some(value), - (0, Err(value)) => Some(value), - _ => None, - }, - ValueKind::Option(option) => match (index, option) { - (0, Some(value)) => Some(value), - _ => None, - }, - ValueKind::GeneratorState(state) => match (index, state) { - (0, Yielded(value)) => Some(value), - (0, Complete(value)) => Some(value), - _ => None, + let result = match vm_try!(target.borrow_ref()) { + ValueBorrowRef::Inline(target) => match target { + Inline::Unit => Err(target.type_info()), + _ => return VmResult::Ok(None), }, - ValueKind::TupleStruct(tuple_struct) => tuple_struct.data().get(index), - ValueKind::Variant(variant) => match variant.data() { - VariantData::Tuple(tuple) => tuple.get(index), + ValueBorrowRef::Mutable(target) => match &*target { + Mutable::Tuple(tuple) => match tuple.get(index) { + Some(value) => Ok(value.clone()), + None => Err(target.type_info()), + }, + Mutable::Vec(vec) => match vec.get(index) { + Some(value) => Ok(value.clone()), + None => Err(target.type_info()), + }, + Mutable::Result(result) => match (index, result) { + (0, Ok(value)) => Ok(value.clone()), + (0, Err(value)) => Ok(value.clone()), + _ => Err(target.type_info()), + }, + Mutable::Option(option) => match (index, option) { + (0, Some(value)) => Ok(value.clone()), + _ => Err(target.type_info()), + }, + Mutable::GeneratorState(state) => match (index, state) { + (0, Yielded(value)) => Ok(value.clone()), + (0, Complete(value)) => Ok(value.clone()), + _ => Err(target.type_info()), + }, + Mutable::TupleStruct(tuple_struct) => match tuple_struct.data().get(index) { + Some(value) => Ok(value.clone()), + None => Err(target.type_info()), + }, + Mutable::Variant(variant) => match variant.data() { + VariantData::Tuple(tuple) => match tuple.get(index) { + Some(value) => Ok(value.clone()), + None => Err(target.type_info()), + }, + _ => return VmResult::Ok(None), + }, _ => return VmResult::Ok(None), }, - _ => return VmResult::Ok(None), }; - let Some(value) = value else { - return err(VmErrorKind::MissingIndexInteger { - target: target.type_info(), + match result { + Ok(value) => VmResult::Ok(Some(value)), + Err(target) => err(VmErrorKind::MissingIndexInteger { + target, index: VmIntegerRepr::from(index), - }); - }; - - VmResult::Ok(Some(value.clone())) + }), + } } /// Implementation of getting a mutable value out of a tuple-like value. @@ -883,38 +920,40 @@ impl Vm { let mut unsupported = false; - let result = BorrowMut::try_map(vm_try!(target.borrow_kind_mut()), |kind| { - match kind { - ValueKind::Tuple(tuple) => return tuple.get_mut(index), - ValueKind::Vec(vec) => return vec.get_mut(index), - ValueKind::Result(result) => match (index, result) { - (0, Ok(value)) => return Some(value), - (0, Err(value)) => return Some(value), - _ => {} - }, - ValueKind::Option(option) => { - if let (0, Some(value)) = (index, option) { - return Some(value); + let result = match vm_try!(target.value_ref()) { + ValueRef::Mutable(value) => BorrowMut::try_map(vm_try!(value.borrow_mut()), |kind| { + match kind { + Mutable::Tuple(tuple) => return tuple.get_mut(index), + Mutable::Vec(vec) => return vec.get_mut(index), + Mutable::Result(result) => match (index, result) { + (0, Ok(value)) => return Some(value), + (0, Err(value)) => return Some(value), + _ => return None, + }, + Mutable::Option(option) => match (index, option) { + (0, Some(value)) => return Some(value), + _ => return None, + }, + Mutable::GeneratorState(state) => match (index, state) { + (0, Yielded(value)) => return Some(value), + (0, Complete(value)) => return Some(value), + _ => return None, + }, + Mutable::TupleStruct(tuple_struct) => return tuple_struct.get_mut(index), + Mutable::Variant(Variant { + data: VariantData::Tuple(tuple), + .. + }) => { + return tuple.get_mut(index); } - } - ValueKind::GeneratorState(state) => match (index, state) { - (0, Yielded(value)) => return Some(value), - (0, Complete(value)) => return Some(value), _ => {} - }, - ValueKind::TupleStruct(tuple_struct) => return tuple_struct.get_mut(index), - ValueKind::Variant(Variant { - data: VariantData::Tuple(tuple), - .. - }) => { - return tuple.get_mut(index); } - _ => {} - } - unsupported = true; - None - }); + unsupported = true; + None + }), + _ => return VmResult::Ok(None), + }; if unsupported { return VmResult::Ok(None); @@ -934,17 +973,27 @@ impl Vm { target: &'a Value, field: &str, ) -> VmResult>> { + let target = match vm_try!(target.value_ref()) { + ValueRef::Mutable(target) => vm_try!(target.borrow_mut()), + ValueRef::Inline(actual) => { + return err(VmErrorKind::MissingField { + target: actual.type_info(), + field: vm_try!(field.try_to_owned()), + }); + } + }; + let mut unsupported = false; - let result = BorrowMut::try_map(vm_try!(target.borrow_kind_mut()), |kind| { - match kind { - ValueKind::Object(target) => { + let result = BorrowMut::try_map(target, |value| { + match value { + Mutable::Object(target) => { return target.get_mut(field); } - ValueKind::Struct(target) => { + Mutable::Struct(target) => { return target.get_mut(field); } - ValueKind::Variant(Variant { + Mutable::Variant(Variant { data: VariantData::Struct(st), .. }) => { @@ -971,63 +1020,68 @@ impl Vm { } /// Implementation of getting a string index on an object-like type. - fn try_tuple_like_index_set(target: &Value, index: usize, value: Value) -> VmResult { - match &mut *vm_try!(target.borrow_kind_mut()) { - ValueKind::EmptyTuple => VmResult::Ok(false), - ValueKind::Tuple(tuple) => { - if let Some(target) = tuple.get_mut(index) { - *target = value; - return VmResult::Ok(true); - } + fn try_tuple_like_index_set(target: &mut Value, index: usize, value: Value) -> VmResult { + match vm_try!(target.value_ref()) { + ValueRef::Inline(target) => match target { + Inline::Unit => VmResult::Ok(false), + _ => VmResult::Ok(false), + }, + ValueRef::Mutable(target) => match &mut *vm_try!(target.borrow_mut()) { + Mutable::Tuple(tuple) => { + if let Some(target) = tuple.get_mut(index) { + *target = value; + return VmResult::Ok(true); + } - VmResult::Ok(false) - } - ValueKind::Vec(vec) => { - if let Some(target) = vec.get_mut(index) { - *target = value; - return VmResult::Ok(true); + VmResult::Ok(false) } + Mutable::Vec(vec) => { + if let Some(target) = vec.get_mut(index) { + *target = value; + return VmResult::Ok(true); + } - VmResult::Ok(false) - } - ValueKind::Result(result) => { - let target = match result { - Ok(ok) if index == 0 => ok, - Err(err) if index == 1 => err, - _ => return VmResult::Ok(false), - }; - - *target = value; - VmResult::Ok(true) - } - ValueKind::Option(option) => { - let target = match option { - Some(some) if index == 0 => some, - _ => return VmResult::Ok(false), - }; + VmResult::Ok(false) + } + Mutable::Result(result) => { + let target = match result { + Ok(ok) if index == 0 => ok, + Err(err) if index == 1 => err, + _ => return VmResult::Ok(false), + }; - *target = value; - VmResult::Ok(true) - } - ValueKind::TupleStruct(tuple_struct) => { - if let Some(target) = tuple_struct.get_mut(index) { *target = value; - return VmResult::Ok(true); + VmResult::Ok(true) } + Mutable::Option(option) => { + let target = match option { + Some(some) if index == 0 => some, + _ => return VmResult::Ok(false), + }; - VmResult::Ok(false) - } - ValueKind::Variant(variant) => { - if let VariantData::Tuple(data) = variant.data_mut() { - if let Some(target) = data.get_mut(index) { + *target = value; + VmResult::Ok(true) + } + Mutable::TupleStruct(tuple_struct) => { + if let Some(target) = tuple_struct.get_mut(index) { *target = value; return VmResult::Ok(true); } + + VmResult::Ok(false) } + Mutable::Variant(variant) => { + if let VariantData::Tuple(data) = variant.data_mut() { + if let Some(target) = data.get_mut(index) { + *target = value; + return VmResult::Ok(true); + } + } - VmResult::Ok(false) - } - _ => VmResult::Ok(false), + VmResult::Ok(false) + } + _ => VmResult::Ok(false), + }, } } @@ -1040,21 +1094,25 @@ impl Vm { ) -> VmResult> { let index = vm_try!(self.unit.lookup_string(slot)); - 'out: { - match &*vm_try!(target.borrow_kind_ref()) { - ValueKind::Object(object) => { + 'fallback: { + let ValueRef::Mutable(target) = vm_try!(target.value_ref()) else { + return VmResult::Ok(CallResult::Unsupported(target.clone())); + }; + + 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(())); } } - ValueKind::Struct(typed_object) => { + Mutable::Struct(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(())); } } - ValueKind::Variant(Variant { + Mutable::Variant(Variant { data: VariantData::Struct(data), .. }) => { @@ -1064,7 +1122,7 @@ impl Vm { } } _ => { - break 'out; + break 'fallback; } } @@ -1086,43 +1144,56 @@ impl Vm { ) -> VmResult> { let field = vm_try!(self.unit.lookup_string(string_slot)); - match &mut *vm_try!(target.borrow_kind_mut()) { - ValueKind::Object(object) => { - let key = vm_try!(field.as_str().try_to_owned()); - vm_try!(object.insert(key, value)); - return VmResult::Ok(CallResult::Ok(())); - } - ValueKind::Struct(typed_object) => { - if let Some(v) = typed_object.get_mut(field.as_str()) { - *v = value; + 'fallback: { + let ValueRef::Mutable(target) = vm_try!(target.value_ref()) else { + return VmResult::Ok(CallResult::Unsupported(target.clone())); + }; + + match &mut *vm_try!(target.borrow_mut()) { + Mutable::Object(object) => { + let key = vm_try!(field.as_str().try_to_owned()); + vm_try!(object.insert(key, value.clone())); return VmResult::Ok(CallResult::Ok(())); } - - return err(VmErrorKind::MissingField { - target: typed_object.type_info(), - field: vm_try!(field.as_str().try_to_owned()), - }); - } - ValueKind::Variant(variant) => { - if let VariantData::Struct(data) = variant.data_mut() { - if let Some(v) = data.get_mut(field.as_str()) { - *v = value; + Mutable::Struct(object) => { + if let Some(v) = object.get_mut(field.as_str()) { + *v = value.clone(); return VmResult::Ok(CallResult::Ok(())); } + + return err(VmErrorKind::MissingField { + target: object.type_info(), + field: vm_try!(field.as_str().try_to_owned()), + }); } + Mutable::Variant(variant) => { + if let VariantData::Struct(data) = variant.data_mut() { + if let Some(v) = data.get_mut(field.as_str()) { + *v = value.clone(); + return VmResult::Ok(CallResult::Ok(())); + } + } - return err(VmErrorKind::MissingField { - target: variant.type_info(), - field: vm_try!(field.as_str().try_to_owned()), - }); + return err(VmErrorKind::MissingField { + target: variant.type_info(), + field: vm_try!(field.as_str().try_to_owned()), + }); + } + _ => { + break 'fallback; + } } - _ => {} } + let target = target.clone(); + let value = value.clone(); + let hash = field.hash(); let mut args = DynGuardedArgs::new((value,)); + let result = vm_try!(self.call_field_fn(Protocol::SET, target, hash, &mut args, Output::discard())); + VmResult::Ok(result) } @@ -1132,37 +1203,42 @@ impl Vm { { use crate::runtime::GeneratorState::*; - VmResult::Ok(match (ty, &*vm_try!(value.borrow_kind_ref())) { - (TypeCheck::EmptyTuple, ValueKind::EmptyTuple) => Some(f(&[])), - (TypeCheck::Tuple, ValueKind::Tuple(tuple)) => Some(f(tuple)), - (TypeCheck::Vec, ValueKind::Vec(vec)) => Some(f(vec)), - (TypeCheck::Result(v), ValueKind::Result(result)) => Some(match (v, result) { - (0, Ok(ok)) => f(slice::from_ref(ok)), - (1, Err(err)) => f(slice::from_ref(err)), - _ => return VmResult::Ok(None), - }), - (TypeCheck::Option(v), ValueKind::Option(option)) => Some(match (v, option) { - (0, Some(some)) => f(slice::from_ref(some)), - (1, None) => f(&[]), - _ => return VmResult::Ok(None), - }), - (TypeCheck::GeneratorState(v), ValueKind::GeneratorState(state)) => { - Some(match (v, state) { - (0, Complete(complete)) => f(slice::from_ref(complete)), - (1, Yielded(yielded)) => f(slice::from_ref(yielded)), + VmResult::Ok(match vm_try!(value.borrow_ref()) { + ValueBorrowRef::Inline(value) => match (ty, value) { + (TypeCheck::Unit, Inline::Unit) => Some(f(&[])), + _ => None, + }, + ValueBorrowRef::Mutable(value) => match (ty, &*value) { + (TypeCheck::Tuple, Mutable::Tuple(tuple)) => Some(f(tuple)), + (TypeCheck::Vec, Mutable::Vec(vec)) => Some(f(vec)), + (TypeCheck::Result(v), Mutable::Result(result)) => Some(match (v, result) { + (0, Ok(ok)) => f(slice::from_ref(ok)), + (1, Err(err)) => f(slice::from_ref(err)), _ => return VmResult::Ok(None), - }) - } - _ => None, + }), + (TypeCheck::Option(v), Mutable::Option(option)) => Some(match (v, option) { + (0, Some(some)) => f(slice::from_ref(some)), + (1, None) => f(&[]), + _ => return VmResult::Ok(None), + }), + (TypeCheck::GeneratorState(v), Mutable::GeneratorState(state)) => { + Some(match (v, state) { + (0, Complete(complete)) => f(slice::from_ref(complete)), + (1, Yielded(yielded)) => f(slice::from_ref(yielded)), + _ => return VmResult::Ok(None), + }) + } + _ => None, + }, }) } /// Internal implementation of the instance check. fn as_op(&mut self, lhs: InstAddress, rhs: InstAddress) -> VmResult { - let b = vm_try!(self.stack.at(rhs)).clone(); - let a = vm_try!(self.stack.at(lhs)).clone(); + let b = vm_try!(self.stack.at(rhs)); + let a = vm_try!(self.stack.at(lhs)); - let ValueKind::Type(ty) = *vm_try!(b.borrow_kind_ref()) else { + let ValueRef::Inline(Inline::Type(ty)) = vm_try!(b.value_ref()) else { return err(VmErrorKind::UnsupportedIs { value: vm_try!(a.type_info()), test_type: vm_try!(b.type_info()), @@ -1170,15 +1246,11 @@ impl Vm { }; macro_rules! convert { - ($from:ty, $value:expr, $ty:expr) => { - match $ty.into_hash() { - runtime::static_type::FLOAT_HASH => { - vm_try!(Value::try_from($value as f64)) - } - runtime::static_type::BYTE_HASH => vm_try!(Value::try_from($value as u8)), - runtime::static_type::INTEGER_HASH => { - vm_try!(Value::try_from($value as i64)) - } + ($from:ty, $value:expr) => { + match ty.into_hash() { + runtime::static_type::FLOAT_HASH => Value::from($value as f64), + runtime::static_type::BYTE_HASH => Value::from($value as u8), + runtime::static_type::INTEGER_HASH => Value::from($value as i64), ty => { return err(VmErrorKind::UnsupportedAs { value: <$from as TypeOf>::type_info(), @@ -1189,13 +1261,13 @@ impl Vm { }; } - let value = match &*vm_try!(a.borrow_kind_ref()) { - ValueKind::Integer(a) => convert!(i64, *a, ty), - ValueKind::Float(a) => convert!(f64, *a, ty), - ValueKind::Byte(a) => convert!(u8, *a, ty), - kind => { + let value = match vm_try!(a.value_ref()) { + ValueRef::Inline(Inline::Integer(a)) => convert!(i64, *a), + ValueRef::Inline(Inline::Float(a)) => convert!(f64, *a), + ValueRef::Inline(Inline::Byte(a)) => convert!(u8, *a), + value => { return err(VmErrorKind::UnsupportedAs { - value: kind.type_info(), + value: vm_try!(value.type_info()), type_hash: ty.into_hash(), }); } @@ -1206,10 +1278,10 @@ impl Vm { /// Internal implementation of the instance check. fn test_is_instance(&mut self, lhs: InstAddress, rhs: InstAddress) -> VmResult { - let b = vm_try!(self.stack.at(rhs)).clone(); - let a = vm_try!(self.stack.at(lhs)).clone(); + let b = vm_try!(self.stack.at(rhs)); + let a = vm_try!(self.stack.at(lhs)); - let ValueKind::Type(ty) = *vm_try!(b.borrow_kind_ref()) else { + let Some(Inline::Type(ty)) = vm_try!(b.as_inline()) else { return err(VmErrorKind::UnsupportedIs { value: vm_try!(a.type_info()), test_type: vm_try!(b.type_info()), @@ -1230,18 +1302,24 @@ impl Vm { let rhs = vm_try!(self.stack.at(rhs)).clone(); let lhs = vm_try!(self.stack.at(lhs)).clone(); - match ( - &*vm_try!(lhs.borrow_kind_ref()), - &*vm_try!(rhs.borrow_kind_ref()), - ) { - (ValueKind::Bool(lhs), ValueKind::Bool(rhs)) => { - vm_try!(out.store(&mut self.stack, || VmResult::Ok(bool_op(*lhs, *rhs)))); - } + match (vm_try!(lhs.value_ref()), vm_try!(rhs.value_ref())) { + (ValueRef::Inline(lhs), ValueRef::Inline(rhs)) => match (lhs, rhs) { + (Inline::Bool(lhs), Inline::Bool(rhs)) => { + vm_try!(out.store(&mut self.stack, || bool_op(*lhs, *rhs))); + } + (lhs, rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + }, (lhs, rhs) => { return err(VmErrorKind::UnsupportedBinaryOperation { op, - lhs: lhs.type_info(), - rhs: rhs.type_info(), + lhs: vm_try!(lhs.type_info()), + rhs: vm_try!(rhs.type_info()), }); } }; @@ -1318,6 +1396,7 @@ impl Vm { } /// Helper function to call the function at the given offset. + #[cfg_attr(feature = "bench", inline(never))] fn call_offset_fn( &mut self, offset: usize, @@ -1349,6 +1428,7 @@ impl Vm { Ok(moved) } + #[cfg_attr(feature = "bench", inline(never))] fn internal_num_assign( &mut self, target: InstTarget, @@ -1362,25 +1442,62 @@ impl Vm { let mut guard; let fallback = match target_value!(self, target, guard, lhs, rhs) { - TargetValue::Value(lhs, rhs) => { - match ( - &mut *vm_try!(lhs.borrow_kind_mut()), - &*vm_try!(rhs.borrow_kind_ref()), - ) { - (ValueKind::Integer(lhs), ValueKind::Integer(rhs)) => { - let out = vm_try!(integer_op(*lhs, *rhs).ok_or_else(error)); - *lhs = out; + TargetValue::Same(value) => { + match vm_try!(value.value_mut()) { + ValueMut::Inline(Inline::Integer(value)) => { + let out = vm_try!(integer_op(*value, *value).ok_or_else(error)); + *value = out; return VmResult::Ok(()); } - (ValueKind::Float(lhs), ValueKind::Float(rhs)) => { - let out = float_op(*lhs, *rhs); - *lhs = out; + ValueMut::Inline(Inline::Float(value)) => { + let out = float_op(*value, *value); + *value = out; return VmResult::Ok(()); } + ValueMut::Inline(value) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: value.type_info(), + rhs: value.type_info(), + }); + } + _ => {} + } + + TargetFallback::Value(value.clone(), value.clone()) + } + TargetValue::Pair(lhs, rhs) => { + match (vm_try!(lhs.value_mut()), vm_try!(rhs.value_ref())) { + (ValueMut::Inline(lhs), ValueRef::Inline(rhs)) => match (lhs, rhs) { + (Inline::Integer(lhs), Inline::Integer(rhs)) => { + let out = vm_try!(integer_op(*lhs, *rhs).ok_or_else(error)); + *lhs = out; + return VmResult::Ok(()); + } + (Inline::Float(lhs), Inline::Float(rhs)) => { + let out = float_op(*lhs, *rhs); + *lhs = out; + return VmResult::Ok(()); + } + (lhs, rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + }, + (ValueMut::Inline(lhs), rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: lhs.type_info(), + rhs: vm_try!(rhs.type_info()), + }); + } _ => {} } - TargetFallback::Value(lhs.clone(), rhs) + TargetFallback::Value(lhs.clone(), rhs.clone()) } TargetValue::Fallback(fallback) => fallback, }; @@ -1389,6 +1506,7 @@ impl Vm { } /// Execute a fallback operation. + #[cfg_attr(feature = "bench", inline(never))] fn target_fallback_assign( &mut self, fallback: TargetFallback<'_>, @@ -1453,6 +1571,7 @@ impl Vm { } /// Internal impl of a numeric operation. + #[cfg_attr(feature = "bench", inline(never))] fn internal_num( &mut self, protocol: Protocol, @@ -1466,20 +1585,19 @@ impl Vm { let rhs = vm_try!(self.stack.at(rhs)).clone(); let lhs = vm_try!(self.stack.at(lhs)).clone(); - match ( - &*vm_try!(lhs.borrow_kind_ref()), - &*vm_try!(rhs.borrow_kind_ref()), - ) { - (ValueKind::Integer(lhs), ValueKind::Integer(rhs)) => { - let value = vm_try!(integer_op(*lhs, *rhs).ok_or_else(error)); - vm_try!(out.store(&mut self.stack, value)); - return VmResult::Ok(()); - } - (ValueKind::Float(lhs), ValueKind::Float(rhs)) => { - vm_try!(out.store(&mut self.stack, float_op(*lhs, *rhs))); - return VmResult::Ok(()); + if let (Some(lhs), Some(rhs)) = (vm_try!(lhs.as_inline()), vm_try!(rhs.as_inline())) { + match (lhs, rhs) { + (Inline::Integer(lhs), Inline::Integer(rhs)) => { + let value = vm_try!(integer_op(*lhs, *rhs).ok_or_else(error)); + vm_try!(out.store(&mut self.stack, value)); + return VmResult::Ok(()); + } + (Inline::Float(lhs), Inline::Float(rhs)) => { + vm_try!(out.store(&mut self.stack, float_op(*lhs, *rhs))); + return VmResult::Ok(()); + } + _ => {} } - _ => {} }; let lhs = lhs.clone(); @@ -1501,6 +1619,7 @@ impl Vm { } /// Internal impl of a numeric operation. + #[cfg_attr(feature = "bench", inline(never))] fn internal_infallible_bitwise_bool( &mut self, protocol: Protocol, @@ -1511,24 +1630,37 @@ impl Vm { rhs: InstAddress, out: Output, ) -> VmResult<()> { - let rhs = vm_try!(self.stack.at(rhs)).clone(); let lhs = vm_try!(self.stack.at(lhs)).clone(); + let rhs = vm_try!(self.stack.at(rhs)).clone(); - match ( - &*vm_try!(lhs.borrow_kind_ref()), - &*vm_try!(rhs.borrow_kind_ref()), - ) { - (ValueKind::Integer(lhs), ValueKind::Integer(rhs)) => { - vm_try!(out.store(&mut self.stack, integer_op(*lhs, *rhs))); - return VmResult::Ok(()); - } - (ValueKind::Byte(lhs), ValueKind::Byte(rhs)) => { - vm_try!(out.store(&mut self.stack, byte_op(*lhs, *rhs))); - return VmResult::Ok(()); - } - (ValueKind::Bool(lhs), ValueKind::Bool(rhs)) => { - vm_try!(out.store(&mut self.stack, bool_op(*lhs, *rhs))); - return VmResult::Ok(()); + match (vm_try!(lhs.value_ref()), vm_try!(rhs.value_ref())) { + (ValueRef::Inline(lhs), ValueRef::Inline(rhs)) => match (lhs, rhs) { + (Inline::Integer(lhs), Inline::Integer(rhs)) => { + vm_try!(out.store(&mut self.stack, integer_op(*lhs, *rhs))); + return VmResult::Ok(()); + } + (Inline::Byte(lhs), Inline::Byte(rhs)) => { + vm_try!(out.store(&mut self.stack, byte_op(*lhs, *rhs))); + return VmResult::Ok(()); + } + (Inline::Bool(lhs), Inline::Bool(rhs)) => { + vm_try!(out.store(&mut self.stack, bool_op(*lhs, *rhs))); + return VmResult::Ok(()); + } + (lhs, rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + }, + (ValueRef::Inline(lhs), rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: lhs.type_info(), + rhs: vm_try!(rhs.type_info()), + }); } _ => {} }; @@ -1561,27 +1693,69 @@ impl Vm { let mut guard; let fallback = match target_value!(self, target, guard, lhs, rhs) { - TargetValue::Value(lhs, rhs) => { - match ( - &mut *vm_try!(lhs.borrow_kind_mut()), - &*vm_try!(rhs.borrow_kind_ref()), - ) { - (ValueKind::Integer(lhs), ValueKind::Integer(rhs)) => { - integer_op(lhs, *rhs); + TargetValue::Same(value) => { + match vm_try!(value.value_mut()) { + ValueMut::Inline(Inline::Integer(value)) => { + let rhs = *value; + integer_op(value, rhs); return VmResult::Ok(()); } - (ValueKind::Byte(lhs), ValueKind::Byte(rhs)) => { - byte_op(lhs, *rhs); + ValueMut::Inline(Inline::Byte(value)) => { + let rhs = *value; + byte_op(value, rhs); return VmResult::Ok(()); } - (ValueKind::Bool(lhs), ValueKind::Bool(rhs)) => { - bool_op(lhs, *rhs); + ValueMut::Inline(Inline::Bool(value)) => { + let rhs = *value; + bool_op(value, rhs); return VmResult::Ok(()); } + ValueMut::Inline(value) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: value.type_info(), + rhs: value.type_info(), + }); + } _ => {} } - TargetFallback::Value(lhs.clone(), rhs) + TargetFallback::Value(value.clone(), value.clone()) + } + TargetValue::Pair(lhs, rhs) => { + match (vm_try!(lhs.value_mut()), vm_try!(rhs.value_ref())) { + (ValueMut::Inline(lhs), ValueRef::Inline(rhs)) => match (lhs, rhs) { + (Inline::Integer(lhs), Inline::Integer(rhs)) => { + integer_op(lhs, *rhs); + return VmResult::Ok(()); + } + (Inline::Byte(lhs), Inline::Byte(rhs)) => { + byte_op(lhs, *rhs); + return VmResult::Ok(()); + } + (Inline::Bool(lhs), Inline::Bool(rhs)) => { + bool_op(lhs, *rhs); + return VmResult::Ok(()); + } + (lhs, rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + }, + (ValueMut::Inline(lhs), rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: lhs.type_info(), + rhs: vm_try!(rhs.type_info()), + }); + } + _ => {} + } + + TargetFallback::Value(lhs.clone(), rhs.clone()) } TargetValue::Fallback(fallback) => fallback, }; @@ -1600,25 +1774,40 @@ impl Vm { out: Output, ) -> VmResult<()> { let rhs = vm_try!(self.stack.at(rhs)).clone(); - let lhs = vm_try!(self.stack.at(lhs)).clone(); + let lhs = vm_try!(self.stack.at_mut(lhs)); - match ( - &*vm_try!(lhs.borrow_kind_ref()), - &*vm_try!(rhs.borrow_kind_ref()), - ) { - (ValueKind::Integer(lhs), ValueKind::Integer(rhs)) => { - let integer = vm_try!(integer_op(*lhs, *rhs).ok_or_else(error)); - vm_try!(out.store(&mut self.stack, integer)); - return VmResult::Ok(()); - } - (ValueKind::Byte(lhs), ValueKind::Integer(rhs)) => { - let byte = vm_try!(byte_op(*lhs, *rhs).ok_or_else(error)); - vm_try!(out.store(&mut self.stack, byte)); - return VmResult::Ok(()); + match (vm_try!(lhs.value_mut()), vm_try!(rhs.value_ref())) { + (ValueMut::Inline(lhs), ValueRef::Inline(rhs)) => match (lhs, rhs) { + (Inline::Integer(lhs), Inline::Integer(rhs)) => { + let integer = vm_try!(integer_op(*lhs, *rhs).ok_or_else(error)); + vm_try!(out.store(&mut self.stack, integer)); + return VmResult::Ok(()); + } + (Inline::Byte(lhs), Inline::Integer(rhs)) => { + let byte = vm_try!(byte_op(*lhs, *rhs).ok_or_else(error)); + vm_try!(out.store(&mut self.stack, byte)); + return VmResult::Ok(()); + } + (lhs, rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + }, + (ValueMut::Inline(lhs), rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: lhs.type_info(), + rhs: vm_try!(rhs.type_info()), + }); } _ => {} } + let lhs = lhs.clone(); + let mut args = DynGuardedArgs::new((&rhs,)); if let CallResult::Unsupported(lhs) = @@ -1647,25 +1836,57 @@ impl Vm { let mut guard; let fallback = match target_value!(self, target, guard, lhs, rhs) { - TargetValue::Value(lhs, rhs) => { - match ( - &mut *vm_try!(lhs.borrow_kind_mut()), - &*vm_try!(rhs.borrow_kind_ref()), - ) { - (ValueKind::Integer(lhs), ValueKind::Integer(rhs)) => { - let out = vm_try!(integer_op(*lhs, *rhs).ok_or_else(error)); - *lhs = out; + TargetValue::Same(value) => { + match vm_try!(value.value_mut()) { + ValueMut::Inline(Inline::Integer(value)) => { + let out = vm_try!(integer_op(*value, *value).ok_or_else(error)); + *value = out; return VmResult::Ok(()); } - (ValueKind::Byte(lhs), ValueKind::Integer(rhs)) => { - let out = vm_try!(byte_op(*lhs, *rhs).ok_or_else(error)); - *lhs = out; - return VmResult::Ok(()); + ValueMut::Inline(value) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: value.type_info(), + rhs: value.type_info(), + }); } _ => {} } - TargetFallback::Value(lhs.clone(), rhs) + TargetFallback::Value(value.clone(), value.clone()) + } + TargetValue::Pair(lhs, rhs) => { + match (vm_try!(lhs.value_mut()), vm_try!(rhs.value_ref())) { + (ValueMut::Inline(lhs), ValueRef::Inline(rhs)) => match (lhs, rhs) { + (Inline::Integer(lhs), Inline::Integer(rhs)) => { + let out = vm_try!(integer_op(*lhs, *rhs).ok_or_else(error)); + *lhs = out; + return VmResult::Ok(()); + } + (Inline::Byte(lhs), Inline::Integer(rhs)) => { + let out = vm_try!(byte_op(*lhs, *rhs).ok_or_else(error)); + *lhs = out; + return VmResult::Ok(()); + } + (lhs, rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: lhs.type_info(), + rhs: rhs.type_info(), + }); + } + }, + (ValueMut::Inline(lhs), rhs) => { + return err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: lhs.type_info(), + rhs: vm_try!(rhs.type_info()), + }); + } + _ => {} + } + + TargetFallback::Value(lhs.clone(), rhs.clone()) } TargetValue::Fallback(fallback) => fallback, }; @@ -1705,8 +1926,8 @@ impl Vm { } #[cfg_attr(feature = "bench", inline(never))] - fn op_push(&mut self, value: InstValue, out: Output) -> VmResult<()> { - vm_try!(out.store(&mut self.stack, vm_try!(value.into_value()))); + fn op_store(&mut self, value: InstValue, out: Output) -> VmResult<()> { + vm_try!(out.store(&mut self.stack, value.into_value())); VmResult::Ok(()) } @@ -1774,7 +1995,7 @@ impl Vm { fn op_vec(&mut self, addr: InstAddress, count: usize, out: Output) -> VmResult<()> { let vec = vm_try!(self.stack.slice_at_mut(addr, count)); let vec = vm_try!(vec.iter_mut().map(take).try_collect::>()); - vm_try!(out.store(&mut self.stack, || ValueKind::Vec(Vec::from(vec)))); + vm_try!(out.store(&mut self.stack, || Mutable::Vec(Vec::from(vec)))); VmResult::Ok(()) } @@ -1789,7 +2010,7 @@ impl Vm { .try_collect::>()); vm_try!( - out.store(&mut self.stack, || VmResult::Ok(ValueKind::Tuple(vm_try!( + out.store(&mut self.stack, || VmResult::Ok(Mutable::Tuple(vm_try!( OwnedTuple::try_from(tuple) )))) ); @@ -1808,7 +2029,7 @@ impl Vm { } vm_try!( - out.store(&mut self.stack, || VmResult::Ok(ValueKind::Tuple(vm_try!( + out.store(&mut self.stack, || VmResult::Ok(Mutable::Tuple(vm_try!( OwnedTuple::try_from(tuple) )))) ); @@ -1850,12 +2071,18 @@ impl Vm { fn op_not(&mut self, operand: InstAddress, out: Output) -> VmResult<()> { let value = vm_try!(self.stack.at(operand)); - let value = match *vm_try!(value.borrow_kind_ref()) { - ValueKind::Bool(value) => vm_try!(Value::try_from(!value)), - ValueKind::Integer(value) => vm_try!(Value::try_from(!value)), - ValueKind::Byte(value) => vm_try!(Value::try_from(!value)), - ref other => { - let operand = other.type_info(); + let value = match vm_try!(value.borrow_ref()) { + ValueBorrowRef::Inline(value) => match value { + Inline::Bool(value) => Value::from(!value), + Inline::Integer(value) => Value::from(!value), + Inline::Byte(value) => Value::from(!value), + actual => { + let operand = actual.type_info(); + return err(VmErrorKind::UnsupportedUnaryOperation { op: "!", operand }); + } + }, + actual => { + let operand = actual.type_info(); return err(VmErrorKind::UnsupportedUnaryOperation { op: "!", operand }); } }; @@ -1868,11 +2095,17 @@ impl Vm { fn op_neg(&mut self, addr: InstAddress, out: Output) -> VmResult<()> { let value = vm_try!(self.stack.at(addr)); - let value = match *vm_try!(value.borrow_kind_ref()) { - ValueKind::Float(value) => vm_try!(Value::try_from(-value)), - ValueKind::Integer(value) => vm_try!(Value::try_from(-value)), - ref other => { - let operand = other.type_info(); + let value = match vm_try!(value.borrow_ref()) { + ValueBorrowRef::Inline(value) => match value { + Inline::Float(value) => Value::from(-value), + Inline::Integer(value) => Value::from(-value), + actual => { + let operand = actual.type_info(); + return err(VmErrorKind::UnsupportedUnaryOperation { op: "-", operand }); + } + }, + actual => { + let operand = actual.type_info(); return err(VmErrorKind::UnsupportedUnaryOperation { op: "-", operand }); } }; @@ -2190,45 +2423,63 @@ impl Vm { let index = vm_try!(self.stack.at(index)); let value = vm_try!(self.stack.at(value)); - 'out: { - let kind = vm_try!(index.borrow_kind_ref()); + 'fallback: { + let ValueBorrowRef::Mutable(field) = vm_try!(index.borrow_ref()) else { + break 'fallback; + }; - let field = match *kind { - ValueKind::String(ref string) => string.as_str(), - _ => break 'out, + let Mutable::String(field) = &*field else { + break 'fallback; }; - match &mut *vm_try!(target.borrow_kind_mut()) { - ValueKind::Object(object) => { + let mut target = match vm_try!(target.value_ref()) { + ValueRef::Mutable(target) => vm_try!(target.borrow_mut()), + ValueRef::Inline(target) => { + return err(VmErrorKind::UnsupportedIndexSet { + target: target.type_info(), + index: vm_try!(index.type_info()), + value: vm_try!(value.type_info()), + }); + } + }; + + match &mut *target { + Mutable::Object(object) => { vm_try!(object.insert(vm_try!(field.try_to_owned()), value.clone())); - return VmResult::Ok(()); } - ValueKind::Struct(typed_object) => { - if let Some(v) = typed_object.get_mut(field) { - *v = value.clone(); - return VmResult::Ok(()); - } + Mutable::Struct(typed_object) => { + let Some(v) = typed_object.get_mut(field) else { + return err(VmErrorKind::MissingField { + target: typed_object.type_info(), + field: vm_try!(field.try_to_owned()), + }); + }; - return err(VmErrorKind::MissingField { - target: typed_object.type_info(), - field: vm_try!(field.try_to_owned()), - }); + *v = value.clone(); } - ValueKind::Variant(variant) => { - if let VariantData::Struct(st) = variant.data_mut() { - if let Some(v) = st.get_mut(field) { - *v = value.clone(); - return VmResult::Ok(()); - } - } + Mutable::Variant(variant) => { + let VariantData::Struct(st) = variant.data_mut() else { + return err(VmErrorKind::MissingField { + target: variant.type_info(), + field: vm_try!(field.try_to_owned()), + }); + }; - return err(VmErrorKind::MissingField { - target: variant.type_info(), - field: vm_try!(field.try_to_owned()), - }); + let Some(v) = st.get_mut(field) else { + return err(VmErrorKind::MissingField { + target: variant.type_info(), + field: vm_try!(field.try_to_owned()), + }); + }; + + *v = value.clone(); + } + _ => { + break 'fallback; } - _ => {} } + + return VmResult::Ok(()); } let target = target.clone(); @@ -2356,7 +2607,7 @@ impl Vm { let instance = vm_try!(self.stack.at(addr)); let ty = vm_try!(instance.type_hash()); let hash = Hash::associated_function(ty, hash); - vm_try!(out.store(&mut self.stack, || ValueKind::Type(Type::new(hash)))); + vm_try!(out.store(&mut self.stack, || Type::new(hash))); VmResult::Ok(()) } @@ -2372,27 +2623,31 @@ impl Vm { let index = vm_try!(self.stack.at(index)); let target = vm_try!(self.stack.at(target)); - match &*vm_try!(index.borrow_kind_ref()) { - ValueKind::String(index) => { - if let Some(value) = - vm_try!(Self::try_object_like_index_get(target, index.as_str())) - { - break 'store value; + 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), + }); + }; + + if let Some(value) = vm_try!(Self::try_tuple_like_index_get(target, index)) + { + break 'store value; + } } } - ValueKind::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; + ValueBorrowRef::Mutable(value) => { + if let Mutable::String(index) = &*value { + if let Some(value) = + vm_try!(Self::try_object_like_index_get(target, index.as_str())) + { + break 'store value; + } } } - _ => (), } let target = target.clone(); @@ -2428,10 +2683,10 @@ impl Vm { index: usize, value: InstAddress, ) -> VmResult<()> { - let target = vm_try!(self.stack.at(target)).clone(); let value = vm_try!(self.stack.at(value)).clone(); + let target = vm_try!(self.stack.at_mut(target)); - if vm_try!(Self::try_tuple_like_index_set(&target, index, value)) { + if vm_try!(Self::try_tuple_like_index_set(target, index, value)) { return VmResult::Ok(()); } @@ -2528,7 +2783,7 @@ impl Vm { vm_try!(object.insert(key, take(value))); } - vm_try!(out.store(&mut self.stack, ValueKind::Object(object))); + vm_try!(out.store(&mut self.stack, Mutable::Object(object))); VmResult::Ok(()) } @@ -2576,7 +2831,7 @@ impl Vm { .ok_or(VmErrorKind::MissingRtti { hash })); vm_try!( - out.store(&mut self.stack, || ValueKind::EmptyStruct(EmptyStruct { + out.store(&mut self.stack, || Mutable::EmptyStruct(EmptyStruct { rtti: rtti.clone() })) ); @@ -2611,7 +2866,7 @@ impl Vm { vm_try!(data.insert(key, take(value))); } - vm_try!(out.store(&mut self.stack, || ValueKind::Struct(Struct { + vm_try!(out.store(&mut self.stack, || Mutable::Struct(Struct { rtti: rtti.clone(), data, }))); @@ -2647,7 +2902,7 @@ impl Vm { } vm_try!( - out.store(&mut self.stack, || ValueKind::Variant(Variant::struct_( + out.store(&mut self.stack, || Mutable::Variant(Variant::struct_( rtti.clone(), data ))) @@ -2687,7 +2942,7 @@ impl Vm { vm_try!(value.string_display_with(&mut f, &mut *self)); } - vm_try!(out.store(&mut self.stack, ValueKind::String(f.string))); + vm_try!(out.store(&mut self.stack, Mutable::String(f.string))); VmResult::Ok(()) } @@ -2695,17 +2950,14 @@ impl Vm { #[cfg_attr(feature = "bench", inline(never))] fn op_format(&mut self, addr: InstAddress, spec: FormatSpec, out: Output) -> VmResult<()> { let value = vm_try!(self.stack.at(addr)).clone(); - vm_try!(out.store(&mut self.stack, || ValueKind::Format(Format { - value, - spec - }))); + vm_try!(out.store(&mut self.stack, || Mutable::Format(Format { value, spec }))); VmResult::Ok(()) } #[cfg_attr(feature = "bench", inline(never))] fn op_is_unit(&mut self, addr: InstAddress, out: Output) -> VmResult<()> { let value = vm_try!(self.stack.at(addr)); - let is_unit = matches!(&*vm_try!(value.borrow_kind_ref()), ValueKind::EmptyTuple); + let is_unit = matches!(vm_try!(value.as_inline()), Some(Inline::Unit)); vm_try!(out.store(&mut self.stack, is_unit)); VmResult::Ok(()) } @@ -2717,10 +2969,12 @@ impl Vm { let value = { let value = vm_try!(self.stack.at(addr)); - match &*vm_try!(value.borrow_kind_ref()) { - ValueKind::Result(result) => break 'out vm_try!(result::result_try(result)), - ValueKind::Option(option) => break 'out vm_try!(option::option_try(option)), - _ => {} + if let ValueBorrowRef::Mutable(value) = vm_try!(value.borrow_ref()) { + match &*value { + Mutable::Result(result) => break 'out vm_try!(result::result_try(result)), + Mutable::Option(option) => break 'out vm_try!(option::option_try(option)), + _ => {} + } } value.clone() @@ -2749,8 +3003,8 @@ impl Vm { fn op_eq_byte(&mut self, addr: InstAddress, value: u8, out: Output) -> VmResult<()> { let v = vm_try!(self.stack.at(addr)); - let is_match = match *vm_try!(v.borrow_kind_ref()) { - ValueKind::Byte(actual) => actual == value, + let is_match = match vm_try!(v.as_inline()) { + Some(Inline::Byte(actual)) => *actual == value, _ => false, }; @@ -2762,8 +3016,8 @@ impl Vm { fn op_eq_character(&mut self, addr: InstAddress, value: char, out: Output) -> VmResult<()> { let v = vm_try!(self.stack.at(addr)); - let is_match = match *vm_try!(v.borrow_kind_ref()) { - ValueKind::Char(actual) => actual == value, + let is_match = match vm_try!(v.as_inline()) { + Some(Inline::Char(actual)) => *actual == value, _ => false, }; @@ -2775,8 +3029,8 @@ impl Vm { fn op_eq_integer(&mut self, addr: InstAddress, value: i64, out: Output) -> VmResult<()> { let v = vm_try!(self.stack.at(addr)); - let is_match = match *vm_try!(v.borrow_kind_ref()) { - ValueKind::Integer(actual) => actual == value, + let is_match = match vm_try!(v.as_inline()) { + Some(Inline::Integer(actual)) => *actual == value, _ => false, }; @@ -2788,8 +3042,8 @@ impl Vm { fn op_eq_bool(&mut self, addr: InstAddress, value: bool, out: Output) -> VmResult<()> { let v = vm_try!(self.stack.at(addr)); - let is_match = match *vm_try!(v.borrow_kind_ref()) { - ValueKind::Bool(actual) => actual == value, + let is_match = match vm_try!(v.as_inline()) { + Some(Inline::Bool(actual)) => *actual == value, _ => false, }; @@ -2803,12 +3057,17 @@ impl Vm { fn op_eq_string(&mut self, addr: InstAddress, slot: usize, out: Output) -> VmResult<()> { let v = vm_try!(self.stack.at(addr)); - let is_match = match *vm_try!(v.borrow_kind_ref()) { - ValueKind::String(ref actual) => { - let string = vm_try!(self.unit.lookup_string(slot)); - actual.as_str() == string.as_str() - } - _ => false, + let is_match = 'out: { + let ValueBorrowRef::Mutable(value) = vm_try!(v.borrow_ref()) else { + break 'out false; + }; + + let Mutable::String(actual) = &*value else { + break 'out false; + }; + + let string = vm_try!(self.unit.lookup_string(slot)); + actual.as_str() == string.as_str() }; vm_try!(out.store(&mut self.stack, is_match)); @@ -2821,12 +3080,17 @@ impl Vm { fn op_eq_bytes(&mut self, addr: InstAddress, slot: usize, out: Output) -> VmResult<()> { let v = vm_try!(self.stack.at(addr)); - let is_match = match *vm_try!(v.borrow_kind_ref()) { - ValueKind::Bytes(ref actual) => { - let bytes = vm_try!(self.unit.lookup_bytes(slot)); - *actual == *bytes - } - _ => false, + let is_match = 'out: { + let ValueBorrowRef::Mutable(value) = vm_try!(v.borrow_ref()) else { + break 'out false; + }; + + let Mutable::Bytes(actual) = &*value else { + break 'out false; + }; + + let bytes = vm_try!(self.unit.lookup_bytes(slot)); + *actual == *bytes }; vm_try!(out.store(&mut self.stack, is_match)); @@ -2876,15 +3140,20 @@ impl Vm { let value = vm_try!(self.stack.at(addr)); let is_match = 'out: { - match &*vm_try!(value.borrow_kind_ref()) { - ValueKind::Variant(variant) => { - break 'out variant.rtti().hash == variant_hash; - } - ValueKind::Any(any) => { - if any.type_hash() != enum_hash { + match vm_try!(value.borrow_ref()) { + ValueBorrowRef::Mutable(value) => match &*value { + Mutable::Variant(variant) => { + break 'out variant.rtti().hash == variant_hash; + } + Mutable::Any(any) => { + if any.type_hash() != enum_hash { + break 'out false; + } + } + _ => { break 'out false; } - } + }, _ => { break 'out false; } @@ -2917,26 +3186,33 @@ impl Vm { let value = vm_try!(self.stack.at(addr)); - let is_match = match (type_check, &*vm_try!(value.borrow_kind_ref())) { - (TypeCheck::EmptyTuple, ValueKind::EmptyTuple) => true, - (TypeCheck::Tuple, ValueKind::Tuple(..)) => true, - (TypeCheck::Vec, ValueKind::Vec(..)) => true, - (TypeCheck::Result(v), ValueKind::Result(result)) => match (v, result) { - (0, Ok(..)) => true, - (1, Err(..)) => true, + let is_match = match vm_try!(value.borrow_ref()) { + ValueBorrowRef::Inline(value) => match (type_check, value) { + (TypeCheck::Unit, Inline::Unit) => true, _ => false, }, - (TypeCheck::Option(v), ValueKind::Option(option)) => match (v, option) { - (0, Some(..)) => true, - (1, None) => true, - _ => false, - }, - (TypeCheck::GeneratorState(v), ValueKind::GeneratorState(state)) => match (v, state) { - (0, Complete(..)) => true, - (1, Yielded(..)) => true, + ValueBorrowRef::Mutable(value) => match (type_check, &*value) { + (TypeCheck::Tuple, Mutable::Tuple(..)) => true, + (TypeCheck::Vec, Mutable::Vec(..)) => true, + (TypeCheck::Result(v), Mutable::Result(result)) => match (v, result) { + (0, Ok(..)) => true, + (1, Err(..)) => true, + _ => false, + }, + (TypeCheck::Option(v), Mutable::Option(option)) => match (v, option) { + (0, Some(..)) => true, + (1, None) => true, + _ => false, + }, + (TypeCheck::GeneratorState(v), Mutable::GeneratorState(state)) => { + match (v, state) { + (0, Complete(..)) => true, + (1, Yielded(..)) => true, + _ => false, + } + } _ => false, }, - _ => false, }; vm_try!(out.store(&mut self.stack, is_match)); @@ -2971,15 +3247,18 @@ impl Vm { let value = vm_try!(self.stack.at(addr)); - let is_match = match &*vm_try!(value.borrow_kind_ref()) { - ValueKind::Object(object) => { - let keys = vm_try!(self - .unit - .lookup_object_keys(slot) - .ok_or(VmErrorKind::MissingStaticObjectKeys { slot })); + let is_match = match vm_try!(value.borrow_ref()) { + ValueBorrowRef::Mutable(value) => match &*value { + Mutable::Object(object) => { + let keys = vm_try!(self + .unit + .lookup_object_keys(slot) + .ok_or(VmErrorKind::MissingStaticObjectKeys { slot })); - test(object, keys, exact) - } + test(object, keys, exact) + } + _ => false, + }, _ => false, }; @@ -3015,7 +3294,7 @@ impl Vm { #[cfg_attr(feature = "bench", inline(never))] fn op_load_fn(&mut self, hash: Hash, out: Output) -> VmResult<()> { let function = vm_try!(self.lookup_function_by_hash(hash)); - vm_try!(out.store(&mut self.stack, || ValueKind::Function(function))); + vm_try!(out.store(&mut self.stack, || Mutable::Function(function))); VmResult::Ok(()) } @@ -3067,7 +3346,7 @@ impl Vm { hash, ); - vm_try!(out.store(&mut self.stack, || ValueKind::Function(function))); + vm_try!(out.store(&mut self.stack, || Mutable::Function(function))); VmResult::Ok(()) } @@ -3182,6 +3461,12 @@ impl Vm { let type_hash = vm_try!(instance.type_hash()); let hash = Hash::associated_function(type_hash, hash); + if let Some(handler) = self.context.function(hash) { + vm_try!(self.called_function_hook(hash)); + vm_try!(handler(&mut self.stack, addr, args, out)); + return VmResult::Ok(()); + } + if let Some(UnitFn::Offset { offset, call, @@ -3195,12 +3480,6 @@ impl Vm { return VmResult::Ok(()); } - if let Some(handler) = self.context.function(hash) { - vm_try!(self.called_function_hook(hash)); - vm_try!(handler(&mut self.stack, addr, args, out)); - return VmResult::Ok(()); - } - err(VmErrorKind::MissingInstanceFunction { instance: vm_try!(instance.type_info()), hash, @@ -3218,15 +3497,25 @@ impl Vm { ) -> VmResult> { let function = vm_try!(self.stack.at(function)).clone(); - let ty = match *vm_try!(function.borrow_kind_ref()) { - ValueKind::Type(ty) => ty, - ValueKind::Function(ref function) => { - return function.call_with_vm(self, addr, args, out); - } - ref actual => { - let actual = actual.type_info(); - return err(VmErrorKind::UnsupportedCallFn { actual }); - } + let ty = match vm_try!(function.borrow_ref()) { + ValueBorrowRef::Inline(value) => match value { + Inline::Type(ty) => *ty, + actual => { + return err(VmErrorKind::UnsupportedCallFn { + actual: actual.type_info(), + }); + } + }, + ValueBorrowRef::Mutable(value) => match &*value { + Mutable::Function(function) => { + return function.call_with_vm(self, addr, args, out); + } + actual => { + return err(VmErrorKind::UnsupportedCallFn { + actual: actual.type_info(), + }); + } + }, }; vm_try!(self.op_call(ty.into_hash(), addr, args, out)); @@ -3237,12 +3526,19 @@ impl Vm { fn op_iter_next(&mut self, addr: InstAddress, jump: usize, out: Output) -> VmResult<()> { let value = vm_try!(self.stack.at(addr)); - let some = match &*vm_try!(value.borrow_kind_ref()) { - ValueKind::Option(option) => match option { - Some(some) => some.clone(), - None => { - self.ip = vm_try!(self.unit.translate(jump)); - return VmResult::Ok(()); + let some = match vm_try!(value.borrow_ref()) { + ValueBorrowRef::Mutable(value) => match &*value { + Mutable::Option(option) => match option { + Some(some) => some.clone(), + None => { + self.ip = vm_try!(self.unit.translate(jump)); + return VmResult::Ok(()); + } + }, + actual => { + return err(VmErrorKind::UnsupportedIterNextOperand { + actual: actual.type_info(), + }); } }, actual => { @@ -3447,7 +3743,7 @@ impl Vm { vm_try!(self.op_load_fn(hash, out)); } Inst::Store { value, out } => { - vm_try!(self.op_push(value, out)); + vm_try!(self.op_store(value, out)); } Inst::Copy { addr, out } => { vm_try!(self.op_copy(addr, out)); diff --git a/crates/rune/src/runtime/vm_execution.rs b/crates/rune/src/runtime/vm_execution.rs index 0cd9e7e31..e2844c83f 100644 --- a/crates/rune/src/runtime/vm_execution.rs +++ b/crates/rune/src/runtime/vm_execution.rs @@ -262,7 +262,7 @@ where VmHalt::Yielded(addr, out) => { let value = match addr { Some(addr) => vm_try!(vm.stack().at(addr)).clone(), - None => vm_try!(Value::unit()), + None => Value::unit(), }; self.state = ExecutionState::Resumed(out); @@ -351,7 +351,7 @@ where VmHalt::Yielded(addr, out) => { let value = match addr { Some(addr) => vm_try!(vm.stack().at(addr)).clone(), - None => vm_try!(Value::unit()), + None => Value::unit(), }; self.state = ExecutionState::Resumed(out); @@ -448,7 +448,7 @@ where let value = match addr { Some(addr) => vm_try!(self.head.as_ref().stack().at(addr)).clone(), - None => vm_try!(Value::unit()), + None => Value::unit(), }; debug_assert!(self.states.is_empty(), "Execution states should be empty"); diff --git a/crates/rune/src/tests.rs b/crates/rune/src/tests.rs index 251f5b401..d2c4008ab 100644 --- a/crates/rune/src/tests.rs +++ b/crates/rune/src/tests.rs @@ -16,8 +16,8 @@ pub(crate) mod prelude { pub(crate) use crate::parse; pub(crate) use crate::runtime::{ self, AnyTypeInfo, Bytes, CoreTypeOf, Formatter, Function, InstAddress, MaybeTypeOf, Mut, - Object, Output, OwnedTuple, Protocol, RawRef, RawStr, Ref, Stack, Tuple, TypeInfo, TypeOf, - UnsafeToRef, ValueKind, VecTuple, VmErrorKind, VmResult, + Mutable, Object, Output, OwnedTuple, OwnedValue, Protocol, RawRef, RawStr, Ref, Stack, + Tuple, TypeInfo, TypeOf, UnsafeToRef, VecTuple, VmErrorKind, VmResult, }; pub(crate) use crate::support::Result; pub(crate) use crate::tests::{eval, run}; diff --git a/crates/rune/src/tests/external_ops.rs b/crates/rune/src/tests/external_ops.rs index 4d9bb6e2f..90da23ed4 100644 --- a/crates/rune/src/tests/external_ops.rs +++ b/crates/rune/src/tests/external_ops.rs @@ -74,7 +74,7 @@ fn assign_ops_struct() -> Result<()> { assert_eq!(foo.field, $expected, "{} != {} (field)", foo.field, $expected); assert_eq!(foo.derived, $expected, "{} != {} (derived)", foo.derived, $expected); assert_eq!(foo.custom, $expected, "{} != {} (custom)", foo.custom, $expected); - assert!(matches!(output.take_kind().unwrap(), ValueKind::EmptyTuple)); + output.into_unit().unwrap(); } }}; } @@ -156,7 +156,7 @@ fn assign_ops_tuple() -> Result<()> { assert_eq!(foo.1, $expected, "{} != {} (field .1)", foo.1, $expected); assert_eq!(foo.2, $expected, "{} != {} (derived .2)", foo.2, $expected); assert_eq!(foo.3, $expected, "{} != {} (custom .3)", foo.3, $expected); - assert!(matches!(output.take_kind().unwrap(), ValueKind::EmptyTuple)); + output.into_unit().unwrap(); } }}; } diff --git a/crates/rune/src/tests/getter_setter.rs b/crates/rune/src/tests/getter_setter.rs index b2311c21b..9d0123581 100644 --- a/crates/rune/src/tests/getter_setter.rs +++ b/crates/rune/src/tests/getter_setter.rs @@ -41,6 +41,6 @@ fn test_getter_setter() -> Result<()> { assert_eq!(foo.number, 43); assert_eq!(foo.string, "Hello World"); - assert!(matches!(output.take_kind().unwrap(), ValueKind::EmptyTuple)); + output.into_unit().unwrap(); Ok(()) } diff --git a/crates/rune/src/tests/unit_constants.rs b/crates/rune/src/tests/unit_constants.rs index 7e018d9c6..731c55894 100644 --- a/crates/rune/src/tests/unit_constants.rs +++ b/crates/rune/src/tests/unit_constants.rs @@ -15,7 +15,7 @@ fn test_get_const() -> Result<()> { assert_eq!( unit.constant(Hash::type_hash(["LEET"])) .context("missing constant")? - .as_value()? + .to_value()? .as_integer()?, 1337 ); @@ -41,7 +41,7 @@ fn test_get_const_re_export() -> Result<()> { assert_eq!( unit.constant(Hash::type_hash(["LEET"])) .context("missing constant")? - .as_value()? + .to_value()? .as_integer()?, 1337 ); @@ -65,7 +65,7 @@ fn test_get_const_nested() -> Result<()> { assert_eq!( unit.constant(Hash::type_hash(["inner", "LEET"])) .expect("successful lookup") - .as_value() + .to_value() .expect("could not allocate value") .as_integer() .expect("the inner value"), diff --git a/crates/rune/src/tests/vm_function.rs b/crates/rune/src/tests/vm_function.rs index 26a046536..d6f7bc3f7 100644 --- a/crates/rune/src/tests/vm_function.rs +++ b/crates/rune/src/tests/vm_function.rs @@ -34,7 +34,10 @@ fn test_function() { assert!(function.call::(()).into_result().is_err()); let value: Value = function.call((1i64,)).unwrap(); - assert!(matches!(value.take_kind().unwrap(), ValueKind::Variant(..))); + assert!(matches!( + value.take_value().unwrap(), + OwnedValue::Mutable(Mutable::Variant(..)) + )); // ptr to dynamic function. let function: Function = rune! { @@ -45,8 +48,8 @@ fn test_function() { assert!(function.call::(()).into_result().is_err()); let value: Value = function.call((1i64,)).unwrap(); assert!(matches!( - value.take_kind().unwrap(), - ValueKind::TupleStruct(..) + value.take_value().unwrap(), + OwnedValue::Mutable(Mutable::TupleStruct(..)) )); // non-capturing closure == free function @@ -56,7 +59,7 @@ fn test_function() { assert!(function.call::((1i64,)).into_result().is_err()); let value: Value = function.call((1i64, 2i64)).unwrap(); - assert!(matches!(value.take_kind().unwrap(), ValueKind::Integer(3))); + assert_eq!(value.as_integer().unwrap(), 3); // closure with captures let function: Function = run( @@ -69,5 +72,5 @@ fn test_function() { assert!(function.call::((1i64,)).into_result().is_err()); let value: Value = function.call(()).unwrap(); - assert!(matches!(value.take_kind().unwrap(), ValueKind::Integer(3))); + assert_eq!(value.as_integer().unwrap(), 3); } diff --git a/crates/rune/src/workspace/spanned_value.rs b/crates/rune/src/workspace/spanned_value.rs index 30b2a9642..a1a97d9e4 100644 --- a/crates/rune/src/workspace/spanned_value.rs +++ b/crates/rune/src/workspace/spanned_value.rs @@ -433,9 +433,9 @@ impl<'de> de::Deserialize<'de> for Value { where D: de::Deserializer<'de>, { - struct ValueKindVisitor; + struct MutableValueVisitor; - impl<'de> de::Visitor<'de> for ValueKindVisitor { + impl<'de> de::Visitor<'de> for MutableValueVisitor { type Value = Value; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -532,7 +532,7 @@ impl<'de> de::Deserialize<'de> for Value { } } - deserializer.deserialize_any(ValueKindVisitor) + deserializer.deserialize_any(MutableValueVisitor) } }