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)
+
+
+
+
+public(friend) native fun swap<T>(left: &mut T, right: &mut T);
+
+
+
+
+public(friend) fun replace<T>(ref: &mut T, new: T): T
+
+
+
+
+public(friend) fun replace<T>(ref: &mut T, new: T): T {
+ swap(ref, &mut new);
+ new
+}
+
+
+
+
+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)