diff --git a/aptos-move/aptos-gas-schedule/src/gas_schedule/move_stdlib.rs b/aptos-move/aptos-gas-schedule/src/gas_schedule/move_stdlib.rs index dd730ffc58c94..b0a6266c2462d 100644 --- a/aptos-move/aptos-gas-schedule/src/gas_schedule/move_stdlib.rs +++ b/aptos-move/aptos-gas-schedule/src/gas_schedule/move_stdlib.rs @@ -50,5 +50,7 @@ crate::gas_schedule::macros::define_gas_parameters!( [vector_move_range_base: InternalGas, { RELEASE_V1_24.. => "vector.move_range.base" }, 4000], [vector_move_range_per_index_moved: InternalGasPerArg, { RELEASE_V1_24.. => "vector.move_range.per_index_moved" }, 10], + + [mem_swap_base: InternalGas, { RELEASE_V1_24.. => "mem.swap.base" }, 1500], ] ); diff --git a/aptos-move/e2e-move-tests/src/tests/code_publishing.data/pack_stdlib/sources/mem.move b/aptos-move/e2e-move-tests/src/tests/code_publishing.data/pack_stdlib/sources/mem.move new file mode 120000 index 0000000000000..13c1b4e1d1a8f --- /dev/null +++ b/aptos-move/e2e-move-tests/src/tests/code_publishing.data/pack_stdlib/sources/mem.move @@ -0,0 +1 @@ +../../../../../../framework/move-stdlib/sources/mem.move \ No newline at end of file diff --git a/aptos-move/e2e-move-tests/src/tests/code_publishing.data/pack_stdlib_incompat/sources/mem.move b/aptos-move/e2e-move-tests/src/tests/code_publishing.data/pack_stdlib_incompat/sources/mem.move new file mode 120000 index 0000000000000..13c1b4e1d1a8f --- /dev/null +++ b/aptos-move/e2e-move-tests/src/tests/code_publishing.data/pack_stdlib_incompat/sources/mem.move @@ -0,0 +1 @@ +../../../../../../framework/move-stdlib/sources/mem.move \ No newline at end of file diff --git a/aptos-move/framework/move-stdlib/doc/mem.md b/aptos-move/framework/move-stdlib/doc/mem.md new file mode 100644 index 0000000000000..b4ac9b3f608be --- /dev/null +++ b/aptos-move/framework/move-stdlib/doc/mem.md @@ -0,0 +1,115 @@ + + + +# Module `0x1::mem` + +Module with methods for safe memory manipulation. + + +- [Function `swap`](#0x1_mem_swap) +- [Function `replace`](#0x1_mem_replace) +- [Specification](#@Specification_0) + - [Function `swap`](#@Specification_0_swap) + - [Function `replace`](#@Specification_0_replace) + + +
+ + + + + +## Function `swap` + +Swap contents of two passed mutable references. + +Move prevents from having two mutable references to the same value, +so left and right references are always distinct. + + +
public(friend) fun swap<T>(left: &mut T, right: &mut T)
+
+ + + +
+Implementation + + +
public(friend) native fun swap<T>(left: &mut T, right: &mut T);
+
+ + + +
+ + + +## Function `replace` + +Replace the value reference points to with the given new value, +and return the value it had before. + + +
public(friend) fun replace<T>(ref: &mut T, new: T): T
+
+ + + +
+Implementation + + +
public(friend) fun replace<T>(ref: &mut T, new: T): T {
+    swap(ref, &mut new);
+    new
+}
+
+ + + +
+ + + +## Specification + + + + +### Function `swap` + + +
public(friend) fun swap<T>(left: &mut T, right: &mut T)
+
+ + + + +
pragma opaque;
+aborts_if false;
+ensures right == old(left);
+ensures left == old(right);
+
+ + + + + +### Function `replace` + + +
public(friend) fun replace<T>(ref: &mut T, new: T): T
+
+ + + + +
pragma opaque;
+aborts_if false;
+ensures result == old(ref);
+ensures ref == new;
+
+ + +[move-book]: https://aptos.dev/move/book/SUMMARY diff --git a/aptos-move/framework/move-stdlib/doc/overview.md b/aptos-move/framework/move-stdlib/doc/overview.md index 8eb0c67f05113..649873e8ab2f5 100644 --- a/aptos-move/framework/move-stdlib/doc/overview.md +++ b/aptos-move/framework/move-stdlib/doc/overview.md @@ -20,6 +20,7 @@ For on overview of the Move language, see the [Move Book][move-book]. - [`0x1::features`](features.md#0x1_features) - [`0x1::fixed_point32`](fixed_point32.md#0x1_fixed_point32) - [`0x1::hash`](hash.md#0x1_hash) +- [`0x1::mem`](mem.md#0x1_mem) - [`0x1::option`](option.md#0x1_option) - [`0x1::signer`](signer.md#0x1_signer) - [`0x1::string`](string.md#0x1_string) diff --git a/aptos-move/framework/move-stdlib/doc/vector.md b/aptos-move/framework/move-stdlib/doc/vector.md index 6507ce116f6ec..b56160dbaf27e 100644 --- a/aptos-move/framework/move-stdlib/doc/vector.md +++ b/aptos-move/framework/move-stdlib/doc/vector.md @@ -351,9 +351,11 @@ Moves range of elements [removal_position, removal_position + length)to, inserting them starting at the insert_position. In the from vector, elements after the selected range are moved left to fill the hole (i.e. range is removed, while the order of the rest of the elements is kept) -In the to vector, elements after the insert_position are moved to the right to make space for new elements -(i.e. range is inserted, while the order of the rest of the elements is kept). -Move prevents from having two mutable references to the same value, so from and to vectors are always distinct. +In the to vector, elements after the insert_position are moved to the right to make +space for new elements (i.e. range is inserted, while the order of the rest of the +elements is kept). +Move prevents from having two mutable references to the same value, so from and to +vectors are always distinct.
public(friend) fun move_range<T>(from: &mut vector<T>, removal_position: u64, length: u64, to: &mut vector<T>, insert_position: u64)
@@ -365,7 +367,13 @@ Move prevents from having two mutable references to the same value, so fro
 Implementation
 
 
-
native public(friend) fun move_range<T>(from: &mut vector<T>, removal_position: u64, length: u64, to: &mut vector<T>, insert_position: u64);
+
native public(friend) fun move_range<T>(
+    from: &mut vector<T>,
+    removal_position: u64,
+    length: u64,
+    to: &mut vector<T>,
+    insert_position: u64
+);
 
diff --git a/aptos-move/framework/move-stdlib/sources/mem.move b/aptos-move/framework/move-stdlib/sources/mem.move new file mode 100644 index 0000000000000..424766f4a2124 --- /dev/null +++ b/aptos-move/framework/move-stdlib/sources/mem.move @@ -0,0 +1,34 @@ +/// Module with methods for safe memory manipulation. +module std::mem { + // TODO - functions here are `public(friend)` here for one release, + // and to be changed to `public` one release later. + #[test_only] + friend std::mem_tests; + + /// Swap contents of two passed mutable references. + /// + /// Move prevents from having two mutable references to the same value, + /// so `left` and `right` references are always distinct. + public(friend) native fun swap(left: &mut T, right: &mut T); + + /// Replace the value reference points to with the given new value, + /// and return the value it had before. + public(friend) fun replace(ref: &mut T, new: T): T { + swap(ref, &mut new); + new + } + + spec swap(left: &mut T, right: &mut T) { + pragma opaque; + aborts_if false; + ensures right == old(left); + ensures left == old(right); + } + + spec replace(ref: &mut T, new: T): T { + pragma opaque; + aborts_if false; + ensures result == old(ref); + ensures ref == new; + } +} diff --git a/aptos-move/framework/move-stdlib/sources/vector.move b/aptos-move/framework/move-stdlib/sources/vector.move index f4a443d3fffe9..2d67042ef4fef 100644 --- a/aptos-move/framework/move-stdlib/sources/vector.move +++ b/aptos-move/framework/move-stdlib/sources/vector.move @@ -61,7 +61,7 @@ module std::vector { /// Aborts if `i` or `j` is out of bounds. native public fun swap(self: &mut vector, i: u64, j: u64); - // TODO - functions here are `public(friend)` here for one release, + // TODO - function `move_range` here is `public(friend)` for one release, // and to be changed to `public` one release later. #[test_only] friend std::vector_tests; @@ -70,10 +70,18 @@ module std::vector { /// to vector `to`, inserting them starting at the `insert_position`. /// In the `from` vector, elements after the selected range are moved left to fill the hole /// (i.e. range is removed, while the order of the rest of the elements is kept) - /// In the `to` vector, elements after the `insert_position` are moved to the right to make space for new elements - /// (i.e. range is inserted, while the order of the rest of the elements is kept). - /// Move prevents from having two mutable references to the same value, so `from` and `to` vectors are always distinct. - native public(friend) fun move_range(from: &mut vector, removal_position: u64, length: u64, to: &mut vector, insert_position: u64); + /// In the `to` vector, elements after the `insert_position` are moved to the right to make + /// space for new elements (i.e. range is inserted, while the order of the rest of the + /// elements is kept). + /// Move prevents from having two mutable references to the same value, so `from` and `to` + /// vectors are always distinct. + native public(friend) fun move_range( + from: &mut vector, + removal_position: u64, + length: u64, + to: &mut vector, + insert_position: u64 + ); /// Return an vector of size one containing element `e`. public fun singleton(e: Element): vector { diff --git a/aptos-move/framework/move-stdlib/src/natives/mem.rs b/aptos-move/framework/move-stdlib/src/natives/mem.rs new file mode 100644 index 0000000000000..578d4dc02d685 --- /dev/null +++ b/aptos-move/framework/move-stdlib/src/natives/mem.rs @@ -0,0 +1,64 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! Implementation of native functions for memory manipulation. + +use aptos_gas_schedule::gas_params::natives::move_stdlib::MEM_SWAP_BASE; +use aptos_native_interface::{ + safely_pop_arg, RawSafeNative, SafeNativeBuilder, SafeNativeContext, SafeNativeError, + SafeNativeResult, +}; +use aptos_types::error; +use move_vm_runtime::native_functions::NativeFunction; +use move_vm_types::{ + loaded_data::runtime_types::Type, + values::{Reference, Value}, +}; +use smallvec::{smallvec, SmallVec}; +use std::collections::VecDeque; + +/// The feature is not enabled. +pub const EFEATURE_NOT_ENABLED: u64 = 1; + +/*************************************************************************************************** + * native fun native_swap + * + * gas cost: MEM_SWAP_BASE + * + **************************************************************************************************/ +fn native_swap( + context: &mut SafeNativeContext, + _ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + if !context + .get_feature_flags() + .is_native_memory_operations_enabled() + { + return Err(SafeNativeError::Abort { + abort_code: error::unavailable(EFEATURE_NOT_ENABLED), + }); + } + + debug_assert!(args.len() == 2); + + context.charge(MEM_SWAP_BASE)?; + + let left = safely_pop_arg!(args, Reference); + let right = safely_pop_arg!(args, Reference); + + left.swap_values(right)?; + + Ok(smallvec![]) +} + +/*************************************************************************************************** + * module + **************************************************************************************************/ +pub fn make_all( + builder: &SafeNativeBuilder, +) -> impl Iterator + '_ { + let natives = [("swap", native_swap as RawSafeNative)]; + + builder.make_named_natives(natives) +} diff --git a/aptos-move/framework/move-stdlib/src/natives/mod.rs b/aptos-move/framework/move-stdlib/src/natives/mod.rs index 38e0f78119405..1ea4a32ebc93d 100644 --- a/aptos-move/framework/move-stdlib/src/natives/mod.rs +++ b/aptos-move/framework/move-stdlib/src/natives/mod.rs @@ -8,6 +8,7 @@ pub mod bcs; pub mod cmp; pub mod hash; +pub mod mem; pub mod signer; pub mod string; #[cfg(feature = "testing")] @@ -36,6 +37,7 @@ pub fn all_natives( add_natives!("bcs", bcs::make_all(builder)); add_natives!("cmp", cmp::make_all(builder)); add_natives!("hash", hash::make_all(builder)); + add_natives!("mem", mem::make_all(builder)); add_natives!("signer", signer::make_all(builder)); add_natives!("string", string::make_all(builder)); add_natives!("vector", vector::make_all(builder)); diff --git a/aptos-move/framework/move-stdlib/tests/mem_tests.move b/aptos-move/framework/move-stdlib/tests/mem_tests.move new file mode 100644 index 0000000000000..89d00b4430348 --- /dev/null +++ b/aptos-move/framework/move-stdlib/tests/mem_tests.move @@ -0,0 +1,97 @@ +#[test_only] +module std::mem_tests { + use std::vector; + use std::mem::{swap, replace}; + + #[test] + fun test_swap_ints() { + let a = 1; + let b = 2; + let v = vector[3, 4, 5, 6]; + + swap(&mut a, &mut b); + assert!(a == 2, 0); + assert!(b == 1, 1); + + swap(&mut a, vector::borrow_mut(&mut v, 0)); + assert!(a == 3, 0); + assert!(vector::borrow(&v, 0) == &2, 1); + + swap(vector::borrow_mut(&mut v, 2), &mut a); + assert!(a == 5, 0); + assert!(vector::borrow(&v, 2) == &3, 1); + } + + #[test] + fun test_replace_ints() { + let a = 1; + let b = 2; + + assert!(replace(&mut a, b) == 1, 0); + assert!(a == 2, 1); + } + + #[test_only] + struct SomeStruct has drop, key { + f: u64, + v: vector, + } + + #[test] + fun test_swap_struct() { + let a = 1; + let v = vector[20, 21]; + let s1 = SomeStruct { f: 2, v: vector[3, 4]}; + let s2 = SomeStruct { f: 5, v: vector[6, 7]}; + let vs = vector[SomeStruct { f: 8, v: vector[9, 10]}, SomeStruct { f: 11, v: vector[12, 13]}]; + + swap(&mut s1, &mut s2); + assert!(&s1 == &SomeStruct { f: 5, v: vector[6, 7]}, 0); + assert!(&s2 == &SomeStruct { f: 2, v: vector[3, 4]}, 1); + + swap(&mut s1.f, &mut a); + assert!(s1.f == 1, 2); + assert!(a == 5, 3); + + swap(&mut s1.f, vector::borrow_mut(&mut s1.v, 0)); + assert!(s1.f == 6, 4); + assert!(vector::borrow(&s1.v, 0) == &1, 5); + + swap(&mut s2, vector::borrow_mut(&mut vs, 0)); + assert!(&s2 == &SomeStruct { f: 8, v: vector[9, 10]}, 6); + assert!(vector::borrow(&vs, 0) == &SomeStruct { f: 2, v: vector[3, 4]}, 7); + + swap(&mut s1.f, vector::borrow_mut(&mut v, 0)); + assert!(&s1.f == &20, 8); + assert!(vector::borrow(&v, 0) == &6, 9); + } + + #[test(creator = @0xcafe)] + fun test_swap_resource(creator: &signer) acquires SomeStruct { + use std::signer; + { + move_to(creator, SomeStruct { f: 5, v: vector[6, 7]}); + }; + + { + let value = borrow_global_mut(signer::address_of(creator)); + let s1 = SomeStruct { f: 2, v: vector[3, 4]}; + let vs = vector[SomeStruct { f: 8, v: vector[9, 10]}, SomeStruct { f: 11, v: vector[12, 13]}]; + + swap(&mut s1, value); + assert!(&s1 == &SomeStruct { f: 5, v: vector[6, 7]}, 0); + assert!(value == &SomeStruct { f: 2, v: vector[3, 4]}, 1); + + swap(value, vector::borrow_mut(&mut vs, 0)); + assert!(value == &SomeStruct { f: 8, v: vector[9, 10]}, 2); + assert!(vector::borrow(&vs, 0) == &SomeStruct { f: 2, v: vector[3, 4]}, 3); + + let v_ref = &mut value.v; + let other_v = vector[11,12]; + swap(v_ref, &mut other_v); + + assert!(v_ref == &vector[11, 12], 4); + assert!(&other_v == &vector[9, 10], 5); + } + } +} diff --git a/execution/executor/tests/internal_indexer_test.rs b/execution/executor/tests/internal_indexer_test.rs index dbc58d60d64d6..13d03f8092693 100644 --- a/execution/executor/tests/internal_indexer_test.rs +++ b/execution/executor/tests/internal_indexer_test.rs @@ -205,6 +205,7 @@ fn test_db_indexer_data() { ident_str!("any"), ident_str!("bcs"), ident_str!("dkg"), + ident_str!("mem"), ident_str!("code"), ident_str!("coin"), ident_str!("guid"), diff --git a/third_party/move/move-vm/types/src/values/value_tests.rs b/third_party/move/move-vm/types/src/values/value_tests.rs index 4e939be5c7a23..c5d7574e774c4 100644 --- a/third_party/move/move-vm/types/src/values/value_tests.rs +++ b/third_party/move/move-vm/types/src/values/value_tests.rs @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{loaded_data::runtime_types::TypeBuilder, values::*, views::*}; +use claims::{assert_err, assert_ok}; use move_binary_format::errors::*; use move_core_types::{account_address::AccountAddress, u256::U256}; @@ -229,6 +230,86 @@ fn test_vm_value_vector_u64_casting() { ); } +#[test] +fn test_mem_swap() -> PartialVMResult<()> { + let mut locals = Locals::new(20); + // IndexedRef(Locals) + locals.store_loc(0, Value::u64(0), false)?; + locals.store_loc(1, Value::u64(1), false)?; + locals.store_loc(2, Value::address(AccountAddress::ZERO), false)?; + locals.store_loc(3, Value::address(AccountAddress::ONE), false)?; + + // ContainerRef + + // - Specialized + locals.store_loc(4, Value::vector_u64(vec![1, 2]), false)?; + locals.store_loc(5, Value::vector_u64(vec![3, 4, 5]), false)?; + locals.store_loc(6, Value::vector_address(vec![AccountAddress::ZERO]), false)?; + locals.store_loc(7, Value::vector_address(vec![AccountAddress::ONE]), false)?; + + // - Generic + // -- Container of container + locals.store_loc(8, Value::struct_(Struct::pack(vec![Value::u16(4)])), false)?; + locals.store_loc(9, Value::struct_(Struct::pack(vec![Value::u16(5)])), false)?; + locals.store_loc(10, Value::signer(AccountAddress::ZERO), false)?; + locals.store_loc(11, Value::signer(AccountAddress::ONE), false)?; + + // -- Container of vector + locals.store_loc( + 12, + Value::vector_for_testing_only(vec![Value::u64(1u64), Value::u64(2u64)]), + false, + )?; + locals.store_loc( + 13, + Value::vector_for_testing_only(vec![Value::u64(3u64), Value::u64(4u64)]), + false, + )?; + locals.store_loc( + 14, + Value::vector_for_testing_only(vec![Value::signer(AccountAddress::ZERO)]), + false, + )?; + locals.store_loc( + 15, + Value::vector_for_testing_only(vec![Value::signer(AccountAddress::ONE)]), + false, + )?; + + let mut locals2 = Locals::new(2); + locals2.store_loc(0, Value::u64(0), false)?; + + let get_local = + |ls: &Locals, idx: usize| ls.borrow_loc(idx).unwrap().value_as::().unwrap(); + + for i in (0..16).step_by(2) { + assert_ok!(get_local(&locals, i).swap_values(get_local(&locals, i + 1))); + } + + assert_ok!(get_local(&locals, 0).swap_values(get_local(&locals2, 0))); + + for i in (0..16).step_by(2) { + for j in ((i + 2)..16).step_by(2) { + let result = get_local(&locals, i).swap_values(get_local(&locals, j)); + + // These would all fail in `call_native` typing checks. + // But here some do pass: + if j < 4 // locals are not checked between each other + || (8 <= i && j < 12) // ContainerRef of containers is not checked between each other + || (12 <= i && j < 16) + // ContainerRef of vector is not checked between each other + // || i >= 8 // containers are also interchangeable + { + assert_ok!(result, "{} and {}", i, j); + } else { + assert_err!(result, "{} and {}", i, j); + } + } + } + + Ok(()) +} + #[cfg(test)] mod native_values { use super::*; diff --git a/third_party/move/move-vm/types/src/values/values_impl.rs b/third_party/move/move-vm/types/src/values/values_impl.rs index 6626d9bbc2cde..42fbe5d9278c3 100644 --- a/third_party/move/move-vm/types/src/values/values_impl.rs +++ b/third_party/move/move-vm/types/src/values/values_impl.rs @@ -25,7 +25,7 @@ use std::{ cell::RefCell, cmp::Ordering, fmt::{self, Debug, Display, Formatter}, - iter, + iter, mem, rc::Rc, }; @@ -1116,6 +1116,241 @@ impl Reference { } } +/************************************************************************************** + * + * Helpers: from primitive + * + *************************************************************************************/ +trait VMValueFromPrimitive { + fn from_primitive(val: T) -> Self; +} + +macro_rules! impl_vm_value_from_primitive { + ($ty:ty, $tc:ident) => { + impl VMValueFromPrimitive<$ty> for ValueImpl { + fn from_primitive(val: $ty) -> Self { + Self::$tc(val) + } + } + }; +} + +impl_vm_value_from_primitive!(u8, U8); +impl_vm_value_from_primitive!(u16, U16); +impl_vm_value_from_primitive!(u32, U32); +impl_vm_value_from_primitive!(u64, U64); +impl_vm_value_from_primitive!(u128, U128); +impl_vm_value_from_primitive!(u256::U256, U256); +impl_vm_value_from_primitive!(bool, Bool); +impl_vm_value_from_primitive!(AccountAddress, Address); + +/************************************************************************************** + * + * Swap reference (Move) + * + * Implementation of the Move operation to swap contents of a reference. + * + *************************************************************************************/ +impl Container { + /// Swaps contents of two mutable references. + /// + /// Precondition for this funciton is that `self` and `other` are required to be + /// distinct references. + /// Move will guarantee that invariant, because it prevents from having two + /// mutable references to the same value. + fn swap_contents(&self, other: &Self) -> PartialVMResult<()> { + use Container::*; + + match (self, other) { + (Vec(l), Vec(r)) => mem::swap(&mut *l.borrow_mut(), &mut *r.borrow_mut()), + (Struct(l), Struct(r)) => mem::swap(&mut *l.borrow_mut(), &mut *r.borrow_mut()), + + (VecBool(l), VecBool(r)) => mem::swap(&mut *l.borrow_mut(), &mut *r.borrow_mut()), + (VecAddress(l), VecAddress(r)) => mem::swap(&mut *l.borrow_mut(), &mut *r.borrow_mut()), + + (VecU8(l), VecU8(r)) => mem::swap(&mut *l.borrow_mut(), &mut *r.borrow_mut()), + (VecU16(l), VecU16(r)) => mem::swap(&mut *l.borrow_mut(), &mut *r.borrow_mut()), + (VecU32(l), VecU32(r)) => mem::swap(&mut *l.borrow_mut(), &mut *r.borrow_mut()), + (VecU64(l), VecU64(r)) => mem::swap(&mut *l.borrow_mut(), &mut *r.borrow_mut()), + (VecU128(l), VecU128(r)) => mem::swap(&mut *l.borrow_mut(), &mut *r.borrow_mut()), + (VecU256(l), VecU256(r)) => mem::swap(&mut *l.borrow_mut(), &mut *r.borrow_mut()), + + ( + Locals(_) | Vec(_) | Struct(_) | VecBool(_) | VecAddress(_) | VecU8(_) | VecU16(_) + | VecU32(_) | VecU64(_) | VecU128(_) | VecU256(_), + _, + ) => { + return Err( + PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR).with_message(format!( + "cannot swap container values: {:?}, {:?}", + self, other + )), + ) + }, + } + + Ok(()) + } +} + +impl ContainerRef { + fn swap_values(self, other: Self) -> PartialVMResult<()> { + self.container().swap_contents(other.container())?; + + self.mark_dirty(); + other.mark_dirty(); + + Ok(()) + } +} + +impl IndexedRef { + fn swap_values(self, other: Self) -> PartialVMResult<()> { + use Container::*; + + macro_rules! swap { + ($r1:ident, $r2:ident) => {{ + if Rc::ptr_eq($r1, $r2) { + if self.idx == other.idx { + return Err(PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR) + .with_message(format!( + "cannot swap references to the same item {:?}", + self + ))); + } + + $r1.borrow_mut().swap(self.idx, other.idx); + } else { + mem::swap( + &mut $r1.borrow_mut()[self.idx], + &mut $r2.borrow_mut()[other.idx], + ) + } + }}; + } + + macro_rules! swap_general_with_specialized { + ($r1:ident, $r2:ident) => {{ + let mut r1 = $r1.borrow_mut(); + let mut r2 = $r2.borrow_mut(); + + let v1 = *r1[self.idx].as_value_ref()?; + r1[self.idx] = ValueImpl::from_primitive(r2[other.idx]); + r2[other.idx] = v1; + }}; + } + + macro_rules! swap_specialized_with_general { + ($r1:ident, $r2:ident) => {{ + let mut r1 = $r1.borrow_mut(); + let mut r2 = $r2.borrow_mut(); + + let v2 = *r2[other.idx].as_value_ref()?; + r2[other.idx] = ValueImpl::from_primitive(r1[self.idx]); + r1[self.idx] = v2; + }}; + } + + match ( + self.container_ref.container(), + other.container_ref.container(), + ) { + // Case 1: (generic, generic) + (Vec(r1), Vec(r2)) + | (Vec(r1), Struct(r2)) + | (Vec(r1), Locals(r2)) + | (Struct(r1), Vec(r2)) + | (Struct(r1), Struct(r2)) + | (Struct(r1), Locals(r2)) + | (Locals(r1), Vec(r2)) + | (Locals(r1), Struct(r2)) + | (Locals(r1), Locals(r2)) => swap!(r1, r2), + + // Case 2: (specialized, specialized) + (VecU8(r1), VecU8(r2)) => swap!(r1, r2), + (VecU16(r1), VecU16(r2)) => swap!(r1, r2), + (VecU32(r1), VecU32(r2)) => swap!(r1, r2), + (VecU64(r1), VecU64(r2)) => swap!(r1, r2), + (VecU128(r1), VecU128(r2)) => swap!(r1, r2), + (VecU256(r1), VecU256(r2)) => swap!(r1, r2), + (VecBool(r1), VecBool(r2)) => swap!(r1, r2), + (VecAddress(r1), VecAddress(r2)) => swap!(r1, r2), + + // Case 3: (generic, specialized) or (specialized, generic) + (Locals(r1) | Struct(r1), VecU8(r2)) => swap_general_with_specialized!(r1, r2), + (VecU8(r1), Locals(r2) | Struct(r2)) => swap_specialized_with_general!(r1, r2), + + (Locals(r1) | Struct(r1), VecU16(r2)) => swap_general_with_specialized!(r1, r2), + (VecU16(r1), Locals(r2) | Struct(r2)) => swap_specialized_with_general!(r1, r2), + + (Locals(r1) | Struct(r1), VecU32(r2)) => swap_general_with_specialized!(r1, r2), + (VecU32(r1), Locals(r2) | Struct(r2)) => swap_specialized_with_general!(r1, r2), + + (Locals(r1) | Struct(r1), VecU64(r2)) => swap_general_with_specialized!(r1, r2), + (VecU64(r1), Locals(r2) | Struct(r2)) => swap_specialized_with_general!(r1, r2), + + (Locals(r1) | Struct(r1), VecU128(r2)) => swap_general_with_specialized!(r1, r2), + (VecU128(r1), Locals(r2) | Struct(r2)) => swap_specialized_with_general!(r1, r2), + + (Locals(r1) | Struct(r1), VecU256(r2)) => swap_general_with_specialized!(r1, r2), + (VecU256(r1), Locals(r2) | Struct(r2)) => swap_specialized_with_general!(r1, r2), + + (Locals(r1) | Struct(r1), VecBool(r2)) => swap_general_with_specialized!(r1, r2), + (VecBool(r1), Locals(r2) | Struct(r2)) => swap_specialized_with_general!(r1, r2), + + (Locals(r1) | Struct(r1), VecAddress(r2)) => swap_general_with_specialized!(r1, r2), + (VecAddress(r1), Locals(r2) | Struct(r2)) => swap_specialized_with_general!(r1, r2), + + // All other combinations are illegal. + (Vec(_), _) + | (VecU8(_), _) + | (VecU16(_), _) + | (VecU32(_), _) + | (VecU64(_), _) + | (VecU128(_), _) + | (VecU256(_), _) + | (VecBool(_), _) + | (VecAddress(_), _) => { + return Err(PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR) + .with_message(format!("cannot swap references {:?}, {:?}", self, other))) + }, + } + + self.container_ref.mark_dirty(); + other.container_ref.mark_dirty(); + + Ok(()) + } +} + +impl ReferenceImpl { + /// Swap contents of two passed mutable references. + /// + /// Precondition for this function is that `self` and `other` references are required to + /// be distinct. + /// Move will guaranteee that invariant, because it prevents from having two mutable + /// references to the same value. + fn swap_values(self, other: Self) -> PartialVMResult<()> { + use ReferenceImpl::*; + + match (self, other) { + (ContainerRef(r1), ContainerRef(r2)) => r1.swap_values(r2), + (IndexedRef(r1), IndexedRef(r2)) => r1.swap_values(r2), + + (ContainerRef(_), IndexedRef(_)) | (IndexedRef(_), ContainerRef(_)) => { + Err(PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR) + .with_message("cannot swap references: reference type mismatch".to_string())) + }, + } + } +} + +impl Reference { + pub fn swap_values(self, other: Self) -> PartialVMResult<()> { + self.0.swap_values(other.0) + } +} + /*************************************************************************************** * * Borrows (Move)