Skip to content

Commit

Permalink
[move][stdlib] Implement mem::swap native move call
Browse files Browse the repository at this point in the history
  • Loading branch information
igor-aptos committed Sep 27, 2024
1 parent a2a930b commit 093a454
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 2 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions aptos-move/aptos-gas-schedule/src/gas_schedule/move_stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,8 @@ crate::gas_schedule::macros::define_gas_parameters!(

[cmp_compare_base: InternalGas, { RELEASE_V1_21.. => "cmp.base" }, 367],
[cmp_compare_per_abs_val_unit: InternalGasPerAbstractValueUnit, { RELEASE_V1_21.. => "cmp.per_abs_val_unit"}, 14],

[mem_swap_base: InternalGas, { RELEASE_V1_21.. => "mem.swap.base" }, 367],
[mem_swap_per_abs_val_unit: InternalGasPerAbstractValueUnit, { RELEASE_V1_21.. => "mem.swap.per_abs_val_unit"}, 14],
]
);
67 changes: 67 additions & 0 deletions aptos-move/framework/move-stdlib/doc/mem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@

<a id="0x1_mem"></a>

# Module `0x1::mem`



- [Function `swap`](#0x1_mem_swap)
- [Function `replace`](#0x1_mem_replace)


<pre><code></code></pre>



<a id="0x1_mem_swap"></a>

## Function `swap`

Swap contents of two passed mutable locations.


<pre><code><b>public</b> <b>fun</b> <a href="mem.md#0x1_mem_swap">swap</a>&lt;T&gt;(left: &<b>mut</b> T, right: &<b>mut</b> T)
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b> <b>native</b> <b>fun</b> <a href="mem.md#0x1_mem_swap">swap</a>&lt;T&gt;(left: &<b>mut</b> T, right: &<b>mut</b> T);
</code></pre>



</details>

<a id="0x1_mem_replace"></a>

## Function `replace`

Replace value reference points to with the given new value,
and return value it had before.


<pre><code><b>public</b> <b>fun</b> <a href="mem.md#0x1_mem_replace">replace</a>&lt;T&gt;(ref: &<b>mut</b> T, new: T): T
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="mem.md#0x1_mem_replace">replace</a>&lt;T&gt;(ref: &<b>mut</b> T, new: T): T {
<a href="mem.md#0x1_mem_swap">swap</a>(ref, &<b>mut</b> new);
new
}
</code></pre>



</details>


[move-book]: https://aptos.dev/move/book/SUMMARY
1 change: 1 addition & 0 deletions aptos-move/framework/move-stdlib/doc/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,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)
Expand Down
66 changes: 66 additions & 0 deletions aptos-move/framework/move-stdlib/sources/mem.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
module std::mem {
/// Swap contents of two passed mutable locations.
public native fun swap<T>(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<T>(ref: &mut T, new: T): T {
swap(ref, &mut new);
new
}

// tests

#[test_only]
use std::vector;

#[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_only]
struct SomeStruct has drop {
f: u64,
v: vector<u64>,
}

#[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);
}
}
75 changes: 75 additions & 0 deletions aptos-move/framework/move-stdlib/src/natives/mem.rs
Original file line number Diff line number Diff line change
@@ -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: base_cost + unit_cost * length_in_bytes
*
**************************************************************************************************/
fn native_swap(
context: &mut SafeNativeContext,
_ty_args: Vec<Type>,
mut args: VecDeque<Value>,
) -> SafeNativeResult<SmallVec<[Value; 1]>> {
debug_assert!(args.len() == 2);

if args.len() != 2 {
return Err(SafeNativeError::InvariantViolation(PartialVMError::new(
StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR,
)));

Check warning on line 43 in aptos-move/framework/move-stdlib/src/natives/mem.rs

View check run for this annotation

Codecov / codecov/patch

aptos-move/framework/move-stdlib/src/natives/mem.rs#L41-L43

Added lines #L41 - L43 were not covered by tests
}

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<Item = (String, NativeFunction)> + '_ {
let natives = [("swap", native_swap as RawSafeNative)];

builder.make_named_natives(natives)
}
2 changes: 2 additions & 0 deletions aptos-move/framework/move-stdlib/src/natives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -35,6 +36,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));
#[cfg(feature = "testing")]
Expand Down
75 changes: 75 additions & 0 deletions third_party/move/move-vm/types/src/values/values_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,81 @@ impl Reference {
}
}

/**************************************************************************************
*
* Swap reference (Move)
*
* Implementation of the Move operation to swap contents of a reference.
*
*************************************************************************************/

impl ContainerRef {
pub fn swap_ref<F>(self, swap_with: F) -> PartialVMResult<()>
where
F: FnOnce(Value) -> PartialVMResult<Value>,
{
// 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<F>(self, swap_with: F) -> PartialVMResult<()>
where
F: FnOnce(Value) -> PartialVMResult<Value>,
{
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()?,

Check warning on line 1148 in third_party/move/move-vm/types/src/values/values_impl.rs

View check run for this annotation

Codecov / codecov/patch

third_party/move/move-vm/types/src/values/values_impl.rs#L1148

Added line #L1148 was not covered by tests
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]),

Check warning on line 1153 in third_party/move/move-vm/types/src/values/values_impl.rs

View check run for this annotation

Codecov / codecov/patch

third_party/move/move-vm/types/src/values/values_impl.rs#L1151-L1153

Added lines #L1151 - L1153 were not covered by tests
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]),

Check warning on line 1158 in third_party/move/move-vm/types/src/values/values_impl.rs

View check run for this annotation

Codecov / codecov/patch

third_party/move/move-vm/types/src/values/values_impl.rs#L1155-L1158

Added lines #L1155 - L1158 were not covered by tests

Locals(r) => r.borrow()[self.idx].copy_value()?,
});

self.write_ref(swap_with(old_value)?)?;
Ok(())
}
}

impl ReferenceImpl {
pub fn swap_ref<F>(self, swap_with: F) -> PartialVMResult<()>
where
F: FnOnce(Value) -> PartialVMResult<Value>,
{
match self {
Self::ContainerRef(r) => r.swap_ref(swap_with),
Self::IndexedRef(r) => r.swap_ref(swap_with),
}
}
}

impl Reference {
pub fn swap_ref<F>(self, swap_with: F) -> PartialVMResult<()>
where
F: FnOnce(Value) -> PartialVMResult<Value>,
{
self.0.swap_ref(swap_with)
}
}

/***************************************************************************************
*
* Borrows (Move)
Expand Down

0 comments on commit 093a454

Please sign in to comment.