diff --git a/Cargo.lock b/Cargo.lock
index d362783ff0d38..0b50492bfa560 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5151,9 +5151,9 @@ checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799"
[[package]]
name = "async-trait"
-version = "0.1.82"
+version = "0.1.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1"
+checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
dependencies = [
"proc-macro2",
"quote",
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 c25afc6e515ba..c5063257d9ff1 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
@@ -3,8 +3,11 @@
//! This module defines the gas parameters for Move Stdlib.
-use crate::{gas_feature_versions::RELEASE_V1_18, gas_schedule::NativeGasParameters};
-use aptos_gas_algebra::{InternalGas, InternalGasPerByte};
+use crate::{
+ gas_feature_versions::{RELEASE_V1_18, RELEASE_V1_22},
+ gas_schedule::NativeGasParameters,
+};
+use aptos_gas_algebra::{InternalGas, InternalGasPerAbstractValueUnit, InternalGasPerByte};
crate::gas_schedule::macros::define_gas_parameters!(
MoveStdlibGasParameters,
@@ -36,5 +39,8 @@ crate::gas_schedule::macros::define_gas_parameters!(
[bcs_serialized_size_base: InternalGas, { RELEASE_V1_18.. => "bcs.serialized_size.base" }, 735],
[bcs_serialized_size_per_byte_serialized: InternalGasPerByte, { RELEASE_V1_18.. => "bcs.serialized_size.per_byte_serialized" }, 36],
[bcs_serialized_size_failure: InternalGas, { RELEASE_V1_18.. => "bcs.serialized_size.failure" }, 3676],
+
+ [mem_swap_base: InternalGas, { RELEASE_V1_22.. => "mem.swap.base" }, 367],
+ [mem_swap_per_abs_val_unit: InternalGasPerAbstractValueUnit, { RELEASE_V1_22.. => "mem.swap.per_abs_val_unit"}, 14],
]
);
diff --git a/aptos-move/aptos-gas-schedule/src/ver.rs b/aptos-move/aptos-gas-schedule/src/ver.rs
index f798c42b40145..d958276339a46 100644
--- a/aptos-move/aptos-gas-schedule/src/ver.rs
+++ b/aptos-move/aptos-gas-schedule/src/ver.rs
@@ -69,7 +69,7 @@
/// global operations.
/// - V1
/// - TBA
-pub const LATEST_GAS_FEATURE_VERSION: u64 = gas_feature_versions::RELEASE_V1_21;
+pub const LATEST_GAS_FEATURE_VERSION: u64 = gas_feature_versions::RELEASE_V1_22;
pub mod gas_feature_versions {
pub const RELEASE_V1_8: u64 = 11;
@@ -86,4 +86,5 @@ pub mod gas_feature_versions {
pub const RELEASE_V1_19: u64 = 23;
pub const RELEASE_V1_20: u64 = 24;
pub const RELEASE_V1_21: u64 = 25;
+ pub const RELEASE_V1_22: u64 = 26;
}
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..cce483a926275
--- /dev/null
+++ b/aptos-move/framework/move-stdlib/doc/mem.md
@@ -0,0 +1,113 @@
+
+
+
+# Module `0x1::mem`
+
+Module with methods for safe memory manipulation.
+I.e. swapping/replacing non-copyable/non-droppable types.
+
+
+- [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.
+
+
+public fun swap<T>(left: &mut T, right: &mut T)
+
+
+
+
+
+Implementation
+
+
+public native fun swap<T>(left: &mut T, right: &mut T);
+
+
+
+
+
+
+
+
+## Function `replace`
+
+Replace value reference points to with the given new value,
+and return value it had before.
+
+
+public fun replace<T>(ref: &mut T, new: T): T
+
+
+
+
+
+Implementation
+
+
+public fun replace<T>(ref: &mut T, new: T): T {
+ swap(ref, &mut new);
+ new
+}
+
+
+
+
+
+
+
+
+## Specification
+
+
+
+
+### Function `swap`
+
+
+public 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 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/sources/mem.move b/aptos-move/framework/move-stdlib/sources/mem.move
new file mode 100644
index 0000000000000..98758c43eba38
--- /dev/null
+++ b/aptos-move/framework/move-stdlib/sources/mem.move
@@ -0,0 +1,27 @@
+/// Module with methods for safe memory manipulation.
+/// I.e. swapping/replacing non-copyable/non-droppable types.
+module std::mem {
+ /// Swap contents of two passed mutable references.
+ public native fun swap(left: &mut T, right: &mut T);
+
+ /// Replace value reference points to with the given new value,
+ /// and return value it had before.
+ public 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/src/natives/mem.rs b/aptos-move/framework/move-stdlib/src/natives/mem.rs
new file mode 100644
index 0000000000000..e13dccfd20ab3
--- /dev/null
+++ b/aptos-move/framework/move-stdlib/src/natives/mem.rs
@@ -0,0 +1,75 @@
+// Copyright © Aptos Foundation
+// SPDX-License-Identifier: Apache-2.0
+
+// Copyright (c) The Diem Core Contributors
+// Copyright (c) The Move Contributors
+// SPDX-License-Identifier: Apache-2.0
+
+//! Implementation of native functions for utf8 strings.
+
+use aptos_gas_schedule::gas_params::natives::move_stdlib::{
+ MEM_SWAP_BASE, MEM_SWAP_PER_ABS_VAL_UNIT,
+};
+use aptos_native_interface::{
+ safely_pop_arg, RawSafeNative, SafeNativeBuilder, SafeNativeContext, SafeNativeError,
+ SafeNativeResult,
+};
+use move_core_types::vm_status::StatusCode;
+use move_vm_runtime::native_functions::NativeFunction;
+use move_vm_types::{
+ loaded_data::runtime_types::Type,
+ natives::function::PartialVMError,
+ values::{Reference, Value},
+};
+use smallvec::{smallvec, SmallVec};
+use std::collections::VecDeque;
+
+/***************************************************************************************************
+ * native fun native_swap
+ *
+ * gas cost: MEM_SWAP_BASE + MEM_SWAP_PER_ABS_VAL_UNIT * abstract_size_of_arguments
+ *
+ **************************************************************************************************/
+fn native_swap(
+ context: &mut SafeNativeContext,
+ _ty_args: Vec,
+ mut args: VecDeque,
+) -> SafeNativeResult> {
+ debug_assert!(args.len() == 2);
+
+ if args.len() != 2 {
+ return Err(SafeNativeError::InvariantViolation(PartialVMError::new(
+ StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR,
+ )));
+ }
+
+ let cost = MEM_SWAP_BASE
+ + MEM_SWAP_PER_ABS_VAL_UNIT
+ * (context.abs_val_size(&args[0]) + context.abs_val_size(&args[1]));
+ context.charge(cost)?;
+
+ let ref1 = safely_pop_arg!(args, Reference);
+ let ref0 = safely_pop_arg!(args, Reference);
+
+ ref0.swap_ref(|value0| {
+ let mut value1_opt = Option::None;
+ ref1.swap_ref(|value1| {
+ value1_opt = Option::Some(value1);
+ Ok(value0)
+ })?;
+ Ok(value1_opt.unwrap())
+ })?;
+
+ 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 56b37bd332960..24c995f7e9cec 100644
--- a/aptos-move/framework/move-stdlib/src/natives/mod.rs
+++ b/aptos-move/framework/move-stdlib/src/natives/mod.rs
@@ -7,6 +7,7 @@
pub mod bcs;
pub mod hash;
+pub mod mem;
pub mod signer;
pub mod string;
#[cfg(feature = "testing")]
@@ -33,6 +34,7 @@ pub fn all_natives(
builder.with_incremental_gas_charging(false, |builder| {
add_natives!("bcs", bcs::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));
#[cfg(feature = "testing")]
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..14144ee4b3513
--- /dev/null
+++ b/aptos-move/framework/move-stdlib/tests/mem_tests.move
@@ -0,0 +1,65 @@
+#[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 {
+ f: u64,
+ v: vector,
+ }
+
+ #[test]
+ fun test_swap_struct() {
+ let a = 1;
+ 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);
+ }
+}
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 006c16d6b9dd1..9db5b3a678ef7 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
@@ -903,6 +903,81 @@ impl Reference {
}
}
+/**************************************************************************************
+ *
+ * Swap reference (Move)
+ *
+ * Implementation of the Move operation to swap contents of a reference.
+ *
+ *************************************************************************************/
+
+impl ContainerRef {
+ pub fn swap_ref(self, swap_with: F) -> PartialVMResult<()>
+ where
+ F: FnOnce(Value) -> PartialVMResult,
+ {
+ // same as read_ref, but without consuming self.
+ // But safe because even though we copy here, and temporarily leave a duplicate inside,
+ // we replace it in write_ref below.
+ let old_value = Value(ValueImpl::Container(self.container().copy_value()?));
+ self.write_ref(swap_with(old_value)?)?;
+
+ Ok(())
+ }
+}
+
+impl IndexedRef {
+ pub fn swap_ref(self, swap_with: F) -> PartialVMResult<()>
+ where
+ F: FnOnce(Value) -> PartialVMResult,
+ {
+ use Container::*;
+
+ // same as read_ref, but without consuming self.
+ // But safe because even though we copy here, and temporarily leave a duplicate inside,
+ // we replace it in write_ref below.
+ let old_value = Value(match self.container_ref.container() {
+ Vec(r) => r.borrow()[self.idx].copy_value()?,
+ Struct(r) => r.borrow()[self.idx].copy_value()?,
+
+ VecU8(r) => ValueImpl::U8(r.borrow()[self.idx]),
+ VecU16(r) => ValueImpl::U16(r.borrow()[self.idx]),
+ VecU32(r) => ValueImpl::U32(r.borrow()[self.idx]),
+ VecU64(r) => ValueImpl::U64(r.borrow()[self.idx]),
+ VecU128(r) => ValueImpl::U128(r.borrow()[self.idx]),
+ VecU256(r) => ValueImpl::U256(r.borrow()[self.idx]),
+ VecBool(r) => ValueImpl::Bool(r.borrow()[self.idx]),
+ VecAddress(r) => ValueImpl::Address(r.borrow()[self.idx]),
+
+ Locals(r) => r.borrow()[self.idx].copy_value()?,
+ });
+
+ self.write_ref(swap_with(old_value)?)?;
+ Ok(())
+ }
+}
+
+impl ReferenceImpl {
+ pub fn swap_ref(self, swap_with: F) -> PartialVMResult<()>
+ where
+ F: FnOnce(Value) -> PartialVMResult,
+ {
+ match self {
+ Self::ContainerRef(r) => r.swap_ref(swap_with),
+ Self::IndexedRef(r) => r.swap_ref(swap_with),
+ }
+ }
+}
+
+impl Reference {
+ pub fn swap_ref(self, swap_with: F) -> PartialVMResult<()>
+ where
+ F: FnOnce(Value) -> PartialVMResult,
+ {
+ self.0.swap_ref(swap_with)
+ }
+}
+
/***************************************************************************************
*
* Borrows (Move)