From 4d42c542503f9b464e8d0412ea02c13e22f67956 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Tue, 8 Jul 2025 12:46:59 +0300 Subject: [PATCH 01/11] managed dealloc - m buffer wip --- .../managed_type_container/handle_map.rs | 4 ++ .../managed_types/managed_type_api_impl.rs | 3 ++ .../src/types/managed/basic/managed_buffer.rs | 6 +-- .../types/managed/wrapped/managed_ref_mut.rs | 2 +- .../src/types/managed/wrapped/managed_vec.rs | 30 +++++++++++-- .../types/managed/wrapped/managed_vec_item.rs | 44 ++++++++++++++++++- .../src/api/impl_vh/debug_handle_vh.rs | 1 + framework/scenario/tests/managed_ref_test.rs | 29 ++++++++++-- 8 files changed, 107 insertions(+), 12 deletions(-) diff --git a/chain/vm/src/host/context/managed_type_container/handle_map.rs b/chain/vm/src/host/context/managed_type_container/handle_map.rs index f121389696..99648f1a14 100644 --- a/chain/vm/src/host/context/managed_type_container/handle_map.rs +++ b/chain/vm/src/host/context/managed_type_container/handle_map.rs @@ -49,6 +49,10 @@ impl HandleMap { } pub fn remove_handle(&mut self, handle: RawHandle) { + assert!( + self.map.contains_key(&handle), + "attepting to remove non-existing handle {handle}, this is a memory managedment issue" + ); let _ = self.map.remove(&handle); } } diff --git a/framework/base/src/api/managed_types/managed_type_api_impl.rs b/framework/base/src/api/managed_types/managed_type_api_impl.rs index 64fc31d81e..a61136639d 100644 --- a/framework/base/src/api/managed_types/managed_type_api_impl.rs +++ b/framework/base/src/api/managed_types/managed_type_api_impl.rs @@ -60,6 +60,9 @@ pub trait ManagedTypeApiImpl: token_identifier_util::get_token_ticker_len(token_id_len) } + fn requires_managed_type_drop(&self) -> bool { + false + } fn drop_managed_buffer(&self, _handle: Self::ManagedBufferHandle) {} fn drop_big_float(&self, _handle: Self::BigFloatHandle) {} fn drop_big_int(&self, _handle: Self::BigIntHandle) {} diff --git a/framework/base/src/types/managed/basic/managed_buffer.rs b/framework/base/src/types/managed/basic/managed_buffer.rs index 68b6a13192..5ba792ef8d 100644 --- a/framework/base/src/types/managed/basic/managed_buffer.rs +++ b/framework/base/src/types/managed/basic/managed_buffer.rs @@ -1,8 +1,7 @@ use crate::{ abi::{TypeAbi, TypeAbiFrom, TypeName}, api::{ - use_raw_handle, ErrorApiImpl, HandleConstraints, InvalidSliceError, ManagedBufferApiImpl, - ManagedTypeApi, RawHandle, StaticVarApiImpl, + use_raw_handle, ErrorApiImpl, HandleConstraints, InvalidSliceError, ManagedBufferApiImpl, ManagedTypeApi, ManagedTypeApiImpl, RawHandle, StaticVarApiImpl }, codec::{ DecodeErrorHandler, Empty, EncodeErrorHandler, NestedDecode, NestedDecodeInput, @@ -427,8 +426,7 @@ impl Clone for ManagedBuffer { impl Drop for ManagedBuffer { fn drop(&mut self) { - // TODO: enable, after fixing all ownership issues - // M::managed_type_impl().drop_managed_buffer(self.handle.clone()); + M::managed_type_impl().drop_managed_buffer(self.handle.clone()); } } diff --git a/framework/base/src/types/managed/wrapped/managed_ref_mut.rs b/framework/base/src/types/managed/wrapped/managed_ref_mut.rs index e017021d24..0abc304c6a 100644 --- a/framework/base/src/types/managed/wrapped/managed_ref_mut.rs +++ b/framework/base/src/types/managed/wrapped/managed_ref_mut.rs @@ -35,7 +35,7 @@ where /// Will completely disregard lifetimes, use with care. #[doc(hidden)] - pub(crate) unsafe fn wrap_handle(handle: T::OwnHandle) -> Self { + pub unsafe fn wrap_handle(handle: T::OwnHandle) -> Self { Self { _phantom_m: PhantomData, _phantom_t: PhantomData, diff --git a/framework/base/src/types/managed/wrapped/managed_vec.rs b/framework/base/src/types/managed/wrapped/managed_vec.rs index a7e8e97f92..3179005c01 100644 --- a/framework/base/src/types/managed/wrapped/managed_vec.rs +++ b/framework/base/src/types/managed/wrapped/managed_vec.rs @@ -1,7 +1,7 @@ use super::{EncodedManagedVecItem, ManagedVecItemPayload}; use crate::{ abi::{TypeAbi, TypeAbiFrom, TypeDescriptionContainer, TypeName}, - api::{ErrorApiImpl, InvalidSliceError, ManagedTypeApi}, + api::{ErrorApiImpl, InvalidSliceError, ManagedTypeApi, ManagedTypeApiImpl}, codec::{ DecodeErrorHandler, EncodeErrorHandler, IntoMultiValue, NestedDecode, NestedDecodeInput, NestedEncode, NestedEncodeOutput, TopDecode, TopDecodeInput, TopEncode, @@ -9,7 +9,8 @@ use crate::{ }, types::{ ManagedBuffer, ManagedBufferNestedDecodeInput, ManagedType, ManagedVecItem, - ManagedVecRefIterator, ManagedVecRefMut, MultiValueEncoded, MultiValueManagedVec, + ManagedVecPayloadIterator, ManagedVecRefIterator, ManagedVecRefMut, MultiValueEncoded, + MultiValueManagedVec, }, }; use alloc::{format, vec::Vec}; @@ -57,7 +58,11 @@ where } unsafe fn forget_into_handle(self) -> Self::OwnHandle { - self.buffer.forget_into_handle() + unsafe { + let handle = core::ptr::read(&self.buffer.handle); + core::mem::forget(self); + handle + } } fn transmute_from_handle_ref(handle_ref: &M::ManagedBufferHandle) -> &Self { @@ -624,6 +629,25 @@ where } } +impl Drop for ManagedVec +where + M: ManagedTypeApi, + T: ManagedVecItem, +{ + fn drop(&mut self) { + unsafe { + if T::requires_drop() { + let iter = ManagedVecPayloadIterator::::new(self.get_handle()); + for payload in iter { + let item = T::read_from_payload(&payload); + core::mem::drop(item); + } + } + M::managed_type_impl().drop_managed_buffer(self.get_handle()); + } + } +} + impl TopEncode for ManagedVec where M: ManagedTypeApi, diff --git a/framework/base/src/types/managed/wrapped/managed_vec_item.rs b/framework/base/src/types/managed/wrapped/managed_vec_item.rs index 31a248bade..0a2cc389e0 100644 --- a/framework/base/src/types/managed/wrapped/managed_vec_item.rs +++ b/framework/base/src/types/managed/wrapped/managed_vec_item.rs @@ -8,7 +8,7 @@ use multiversx_chain_core::types::{EsdtLocalRole, EsdtTokenType}; use multiversx_sc_codec::multi_types::{MultiValue2, MultiValue3}; use crate::{ - api::{use_raw_handle, HandleConstraints, ManagedTypeApi}, + api::{use_raw_handle, HandleConstraints, ManagedTypeApi, ManagedTypeApiImpl}, types::{ BigInt, BigUint, EllipticCurve, ManagedAddress, ManagedBuffer, ManagedByteArray, ManagedRef, ManagedType, ManagedVec, TokenIdentifier, @@ -50,6 +50,8 @@ pub trait ManagedVecItem: 'static { } /// Parses given bytes as a an owned object. + /// + /// TODO: unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self; /// Parses given bytes as a representation of the object, either owned, or a reference. @@ -65,6 +67,20 @@ pub trait ManagedVecItem: 'static { /// /// Note that a destructor should not be called at this moment, since the ManagedVec will take ownership of the item. fn save_to_payload(self, payload: &mut Self::PAYLOAD); + + /// Signals that vec should drop all items one by one when being itself dropped. + /// + /// If false, iterating over all items on drop makes no sense. + fn requires_drop() -> bool { + false + } + + /// Called when deallocating the item, based on payload. Will be called if `requires_drop` returns true. + /// + /// Especially important for managed types. + /// + /// Avoid calling directly, it should be called automatically. + unsafe fn item_drop(_payload: &mut Self::PAYLOAD) {} } /// Used by the ManagedVecItem derive. @@ -202,6 +218,10 @@ where t.save_to_payload(p2); } } + + fn requires_drop() -> bool { + T::requires_drop() + } } macro_rules! impl_managed_type { @@ -259,6 +279,19 @@ where let handle = unsafe { self.forget_into_handle() }; handle.get_raw_handle().save_to_payload(payload); } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } + + // unsafe fn item_drop(payload: &mut Self::PAYLOAD) { + // let handle = use_raw_handle(i32::read_from_payload(payload)); + // Self::dealloc_vec(handle) + // } + + // unsafe fn item_drop(&mut self) { + // M::managed_type_impl().drop_managed_buffer(self.get_handle()); + // } } impl ManagedVecItem for ManagedVec @@ -284,6 +317,15 @@ where let handle = unsafe { self.forget_into_handle() }; handle.get_raw_handle().save_to_payload(payload); } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } + + unsafe fn item_drop(payload: &mut Self::PAYLOAD) { + // let handle = use_raw_handle(i32::read_from_payload(payload)); + // Self::dealloc_vec(handle) + } } impl ManagedVecItem for EsdtTokenType { diff --git a/framework/scenario/src/api/impl_vh/debug_handle_vh.rs b/framework/scenario/src/api/impl_vh/debug_handle_vh.rs index 7f3da0d20f..17a5ce2dfa 100644 --- a/framework/scenario/src/api/impl_vh/debug_handle_vh.rs +++ b/framework/scenario/src/api/impl_vh/debug_handle_vh.rs @@ -44,6 +44,7 @@ impl core::fmt::Debug for DebugHandle { impl HandleConstraints for DebugHandle { fn new(handle: multiversx_sc::api::RawHandle) -> Self { + println!("new handle {handle}"); Self { context: ContractDebugStack::static_peek().tx_context_ref.into_ref(), raw_handle: handle, diff --git a/framework/scenario/tests/managed_ref_test.rs b/framework/scenario/tests/managed_ref_test.rs index dedba7904f..9945ec902a 100644 --- a/framework/scenario/tests/managed_ref_test.rs +++ b/framework/scenario/tests/managed_ref_test.rs @@ -1,9 +1,8 @@ use core::fmt::Debug; use multiversx_sc::{ - api::ManagedTypeApi, + api::{use_raw_handle, ManagedTypeApi}, types::{ - BigInt, BigUint, ManagedAddress, ManagedBuffer, ManagedByteArray, ManagedRef, ManagedType, - TokenIdentifier, + BigInt, BigUint, ManagedAddress, ManagedBuffer, ManagedByteArray, ManagedRef, ManagedRefMut, ManagedType, TokenIdentifier }, }; use multiversx_sc_scenario::api::StaticApi; @@ -56,3 +55,27 @@ fn test_managed_ref_eq() { BigUint::::from(2u32).as_ref() ); } + +#[test] +fn test_managed_ref_no_drop() { + const INVALID_HANDLE: i32 = 1000; + unsafe { + let _r = ManagedRef::<'static, StaticApi, ManagedBuffer>::wrap_handle( + use_raw_handle(INVALID_HANDLE), + ); + } + + unsafe { + let mut r = ManagedRefMut::<'static, StaticApi, ManagedBuffer>::wrap_handle( + use_raw_handle(INVALID_HANDLE), + ); + r.overwrite(b"abc"); + } + + unsafe { + let r = ManagedBuffer::::from_handle( + use_raw_handle(INVALID_HANDLE), + ); + assert_eq!(r.to_vec(), b"abc"); + } +} From 5c824fe1e577e51bf070e70317b16cdea402bef7 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Tue, 8 Jul 2025 12:48:20 +0300 Subject: [PATCH 02/11] managed dealloc - remove item drop --- .../src/types/managed/basic/managed_buffer.rs | 3 ++- .../types/managed/wrapped/managed_vec_item.rs | 21 ------------------- framework/scenario/tests/managed_ref_test.rs | 7 +++---- 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/framework/base/src/types/managed/basic/managed_buffer.rs b/framework/base/src/types/managed/basic/managed_buffer.rs index 5ba792ef8d..b7d38e1c41 100644 --- a/framework/base/src/types/managed/basic/managed_buffer.rs +++ b/framework/base/src/types/managed/basic/managed_buffer.rs @@ -1,7 +1,8 @@ use crate::{ abi::{TypeAbi, TypeAbiFrom, TypeName}, api::{ - use_raw_handle, ErrorApiImpl, HandleConstraints, InvalidSliceError, ManagedBufferApiImpl, ManagedTypeApi, ManagedTypeApiImpl, RawHandle, StaticVarApiImpl + use_raw_handle, ErrorApiImpl, HandleConstraints, InvalidSliceError, ManagedBufferApiImpl, + ManagedTypeApi, ManagedTypeApiImpl, RawHandle, StaticVarApiImpl, }, codec::{ DecodeErrorHandler, Empty, EncodeErrorHandler, NestedDecode, NestedDecodeInput, diff --git a/framework/base/src/types/managed/wrapped/managed_vec_item.rs b/framework/base/src/types/managed/wrapped/managed_vec_item.rs index 0a2cc389e0..0171ebf7ac 100644 --- a/framework/base/src/types/managed/wrapped/managed_vec_item.rs +++ b/framework/base/src/types/managed/wrapped/managed_vec_item.rs @@ -74,13 +74,6 @@ pub trait ManagedVecItem: 'static { fn requires_drop() -> bool { false } - - /// Called when deallocating the item, based on payload. Will be called if `requires_drop` returns true. - /// - /// Especially important for managed types. - /// - /// Avoid calling directly, it should be called automatically. - unsafe fn item_drop(_payload: &mut Self::PAYLOAD) {} } /// Used by the ManagedVecItem derive. @@ -283,15 +276,6 @@ where fn requires_drop() -> bool { M::managed_type_impl().requires_managed_type_drop() } - - // unsafe fn item_drop(payload: &mut Self::PAYLOAD) { - // let handle = use_raw_handle(i32::read_from_payload(payload)); - // Self::dealloc_vec(handle) - // } - - // unsafe fn item_drop(&mut self) { - // M::managed_type_impl().drop_managed_buffer(self.get_handle()); - // } } impl ManagedVecItem for ManagedVec @@ -321,11 +305,6 @@ where fn requires_drop() -> bool { M::managed_type_impl().requires_managed_type_drop() } - - unsafe fn item_drop(payload: &mut Self::PAYLOAD) { - // let handle = use_raw_handle(i32::read_from_payload(payload)); - // Self::dealloc_vec(handle) - } } impl ManagedVecItem for EsdtTokenType { diff --git a/framework/scenario/tests/managed_ref_test.rs b/framework/scenario/tests/managed_ref_test.rs index 9945ec902a..1ebaf2b4cb 100644 --- a/framework/scenario/tests/managed_ref_test.rs +++ b/framework/scenario/tests/managed_ref_test.rs @@ -2,7 +2,8 @@ use core::fmt::Debug; use multiversx_sc::{ api::{use_raw_handle, ManagedTypeApi}, types::{ - BigInt, BigUint, ManagedAddress, ManagedBuffer, ManagedByteArray, ManagedRef, ManagedRefMut, ManagedType, TokenIdentifier + BigInt, BigUint, ManagedAddress, ManagedBuffer, ManagedByteArray, ManagedRef, + ManagedRefMut, ManagedType, TokenIdentifier, }, }; use multiversx_sc_scenario::api::StaticApi; @@ -73,9 +74,7 @@ fn test_managed_ref_no_drop() { } unsafe { - let r = ManagedBuffer::::from_handle( - use_raw_handle(INVALID_HANDLE), - ); + let r = ManagedBuffer::::from_handle(use_raw_handle(INVALID_HANDLE)); assert_eq!(r.to_vec(), b"abc"); } } From c4833006398bece7cadb30abf0ee3da5b105d29d Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Tue, 8 Jul 2025 15:08:26 +0300 Subject: [PATCH 03/11] managed dealloc - unsafe read_from_payload --- Cargo.lock | 20 ++++----- .../tx_managed_buffer.rs | 1 + .../egld_or_esdt_token_payment_multi_value.rs | 2 +- .../esdt_token_payment_multi_value.rs | 2 +- .../multi_value_managed_vec_counted.rs | 2 +- .../wrapped/egld_or_esdt_token_payment.rs | 2 +- .../wrapped/encoded_managed_vec_item.rs | 4 +- .../managed/wrapped/esdt_token_payment.rs | 2 +- .../types/managed/wrapped/managed_decimal.rs | 4 +- .../managed_decimal/managed_decimal_signed.rs | 4 +- .../types/managed/wrapped/managed_option.rs | 2 +- .../src/types/managed/wrapped/managed_vec.rs | 15 +++++-- .../types/managed/wrapped/managed_vec_item.rs | 43 +++++++++++++------ .../managed/wrapped/managed_vec_iter_owned.rs | 6 ++- .../derive/src/managed_vec_item_derive.rs | 4 +- .../derive_managed_vec_item_biguint_test.rs | 11 ++--- .../derive_managed_vec_item_decimal_test.rs | 5 ++- .../tests/derive_managed_vec_item_enum_1.rs | 35 ++++++++------- .../derive_managed_vec_item_enum_simple.rs | 10 +++-- ...anaged_vec_item_esdt_token_payment_test.rs | 12 +++--- .../derive_managed_vec_item_struct_1_test.rs | 9 ++-- .../derive_managed_vec_item_struct_2_test.rs | 10 +++-- 22 files changed, 125 insertions(+), 80 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1211df8e5..45b0daf2c4 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -538,9 +538,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.27" +version = "1.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" dependencies = [ "shlex", ] @@ -1234,18 +1234,18 @@ dependencies = [ [[package]] name = "enumset" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a6b7c3d347de0a9f7bfd2f853be43fe32fa6fac30c70f6d6d67a1e936b87ee" +checksum = "d6ee17054f550fd7400e1906e2f9356c7672643ed34008a9e8abe147ccd2d821" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6da3ea9e1d1a3b1593e15781f930120e72aa7501610b2f82e5b6739c72e8eac5" +checksum = "76d07902c93376f1e96c34abc4d507c0911df3816cef50b01f5a2ff3ad8c370d" dependencies = [ "darling", "proc-macro2", @@ -2261,9 +2261,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" dependencies = [ "base64", "bytes", @@ -5270,9 +5270,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.46.0" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1140bb80481756a8cbe10541f37433b459c5aa1e727b4c020fbfebdc25bf3ec4" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" dependencies = [ "backtrace", "bytes", diff --git a/chain/vm/src/host/context/managed_type_container/tx_managed_buffer.rs b/chain/vm/src/host/context/managed_type_container/tx_managed_buffer.rs index 0fff43c1c4..cbab957225 100644 --- a/chain/vm/src/host/context/managed_type_container/tx_managed_buffer.rs +++ b/chain/vm/src/host/context/managed_type_container/tx_managed_buffer.rs @@ -185,6 +185,7 @@ impl ManagedTypeContainer { } pub fn mb_remove(&mut self, handle: RawHandle) { + println!("removing MB: {handle}"); self.managed_buffer_map.remove_handle(handle); } } diff --git a/framework/base/src/types/managed/multi_value/egld_or_esdt_token_payment_multi_value.rs b/framework/base/src/types/managed/multi_value/egld_or_esdt_token_payment_multi_value.rs index ad10ac76d0..ae1a99e250 100644 --- a/framework/base/src/types/managed/multi_value/egld_or_esdt_token_payment_multi_value.rs +++ b/framework/base/src/types/managed/multi_value/egld_or_esdt_token_payment_multi_value.rs @@ -39,7 +39,7 @@ impl ManagedVecItem for EgldOrEsdtTokenPaymentMultiValue { const SKIPS_RESERIALIZATION: bool = EgldOrEsdtTokenPayment::::SKIPS_RESERIALIZATION; type Ref<'a> = ManagedVecRef<'a, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { EgldOrEsdtTokenPayment::read_from_payload(payload).into() } diff --git a/framework/base/src/types/managed/multi_value/esdt_token_payment_multi_value.rs b/framework/base/src/types/managed/multi_value/esdt_token_payment_multi_value.rs index 957dd497fc..bcc2b8b863 100644 --- a/framework/base/src/types/managed/multi_value/esdt_token_payment_multi_value.rs +++ b/framework/base/src/types/managed/multi_value/esdt_token_payment_multi_value.rs @@ -45,7 +45,7 @@ impl ManagedVecItem for EsdtTokenPaymentMultiValue { const SKIPS_RESERIALIZATION: bool = EsdtTokenPayment::::SKIPS_RESERIALIZATION; type Ref<'a> = ManagedVecRef<'a, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { EsdtTokenPayment::read_from_payload(payload).into() } diff --git a/framework/base/src/types/managed/multi_value/multi_value_managed_vec_counted.rs b/framework/base/src/types/managed/multi_value/multi_value_managed_vec_counted.rs index 4f51989a9b..c17b86d5f4 100644 --- a/framework/base/src/types/managed/multi_value/multi_value_managed_vec_counted.rs +++ b/framework/base/src/types/managed/multi_value/multi_value_managed_vec_counted.rs @@ -100,7 +100,7 @@ where const SKIPS_RESERIALIZATION: bool = false; type Ref<'a> = Self; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { Self::from(ManagedVec::::read_from_payload(payload)) } diff --git a/framework/base/src/types/managed/wrapped/egld_or_esdt_token_payment.rs b/framework/base/src/types/managed/wrapped/egld_or_esdt_token_payment.rs index a370d4219d..91f081b6dd 100644 --- a/framework/base/src/types/managed/wrapped/egld_or_esdt_token_payment.rs +++ b/framework/base/src/types/managed/wrapped/egld_or_esdt_token_payment.rs @@ -205,7 +205,7 @@ impl ManagedVecItem for EgldOrEsdtTokenPayment { const SKIPS_RESERIALIZATION: bool = false; type Ref<'a> = ManagedVecRef<'a, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { let mut index = 0; unsafe { EgldOrEsdtTokenPayment { diff --git a/framework/base/src/types/managed/wrapped/encoded_managed_vec_item.rs b/framework/base/src/types/managed/wrapped/encoded_managed_vec_item.rs index 205a219a2c..9fad5f9eca 100644 --- a/framework/base/src/types/managed/wrapped/encoded_managed_vec_item.rs +++ b/framework/base/src/types/managed/wrapped/encoded_managed_vec_item.rs @@ -14,7 +14,9 @@ where T: ManagedVecItem, { pub(crate) fn decode(&self) -> T { - T::read_from_payload(&self.encoded) + // TODO: not safe!! + // must revisit + unsafe { T::read_from_payload(&self.encoded) } } } diff --git a/framework/base/src/types/managed/wrapped/esdt_token_payment.rs b/framework/base/src/types/managed/wrapped/esdt_token_payment.rs index 4bfe4fd8e5..3369cf748a 100644 --- a/framework/base/src/types/managed/wrapped/esdt_token_payment.rs +++ b/framework/base/src/types/managed/wrapped/esdt_token_payment.rs @@ -192,7 +192,7 @@ impl ManagedVecItem for EsdtTokenPayment { const SKIPS_RESERIALIZATION: bool = false; type Ref<'a> = ManagedVecRef<'a, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { let mut index = 0; unsafe { EsdtTokenPayment { diff --git a/framework/base/src/types/managed/wrapped/managed_decimal.rs b/framework/base/src/types/managed/wrapped/managed_decimal.rs index b3fa2de093..7ebf2aaf8b 100644 --- a/framework/base/src/types/managed/wrapped/managed_decimal.rs +++ b/framework/base/src/types/managed/wrapped/managed_decimal.rs @@ -147,7 +147,7 @@ impl ManagedVecItem for ManagedDecimal { type Ref<'a> = ManagedVecRef<'a, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { let mut index = 0; unsafe { Self { @@ -179,7 +179,7 @@ impl ManagedVecItem type Ref<'a> = ManagedVecRef<'a, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { Self::const_decimals_from_raw(BigUint::read_from_payload(payload)) } diff --git a/framework/base/src/types/managed/wrapped/managed_decimal/managed_decimal_signed.rs b/framework/base/src/types/managed/wrapped/managed_decimal/managed_decimal_signed.rs index f50c230c2a..ab170bcd7c 100644 --- a/framework/base/src/types/managed/wrapped/managed_decimal/managed_decimal_signed.rs +++ b/framework/base/src/types/managed/wrapped/managed_decimal/managed_decimal_signed.rs @@ -196,7 +196,7 @@ impl ManagedVecItem for ManagedDecimalSigned type Ref<'a> = ManagedVecRef<'a, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { let mut index = 0; unsafe { Self { @@ -228,7 +228,7 @@ impl ManagedVecItem type Ref<'a> = ManagedVecRef<'a, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { Self::const_decimals_from_raw(BigInt::read_from_payload(payload)) } diff --git a/framework/base/src/types/managed/wrapped/managed_option.rs b/framework/base/src/types/managed/wrapped/managed_option.rs index ba47fa897f..16c313f6d3 100644 --- a/framework/base/src/types/managed/wrapped/managed_option.rs +++ b/framework/base/src/types/managed/wrapped/managed_option.rs @@ -206,7 +206,7 @@ where const SKIPS_RESERIALIZATION: bool = false; type Ref<'a> = Self; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { let handle = use_raw_handle(i32::read_from_payload(payload)); Self::new_with_handle(handle) } diff --git a/framework/base/src/types/managed/wrapped/managed_vec.rs b/framework/base/src/types/managed/wrapped/managed_vec.rs index 3179005c01..079871f6ff 100644 --- a/framework/base/src/types/managed/wrapped/managed_vec.rs +++ b/framework/base/src/types/managed/wrapped/managed_vec.rs @@ -586,11 +586,18 @@ where let _ = other .buffer .load_slice(byte_index, other_payload.payload_slice_mut()); - let self_item = T::read_from_payload(&self_payload); - let other_item = T::read_from_payload(&other_payload); - if self_item != other_item { - return false; + unsafe { + // ok because of the forget below + let self_item = T::read_from_payload(&self_payload); + let other_item = T::read_from_payload(&other_payload); + + if self_item != other_item { + return false; + } + core::mem::forget(self_item); + core::mem::forget(other_item); } + byte_index += T::payload_size(); } true diff --git a/framework/base/src/types/managed/wrapped/managed_vec_item.rs b/framework/base/src/types/managed/wrapped/managed_vec_item.rs index 0171ebf7ac..a8927618e5 100644 --- a/framework/base/src/types/managed/wrapped/managed_vec_item.rs +++ b/framework/base/src/types/managed/wrapped/managed_vec_item.rs @@ -25,7 +25,7 @@ use super::{ /// in the underlying managed buffer. /// Not all data needs to be stored as payload, for instance for most managed types /// the payload is just the handle, whereas the mai ndata is kept by the VM. -pub trait ManagedVecItem: 'static { +pub trait ManagedVecItem: Sized + 'static { /// Type managing the underlying binary representation in a ManagedVec.. type PAYLOAD: ManagedVecItemPayload; @@ -51,8 +51,11 @@ pub trait ManagedVecItem: 'static { /// Parses given bytes as a an owned object. /// - /// TODO: unsafe - fn read_from_payload(payload: &Self::PAYLOAD) -> Self; + /// # Safety + /// + /// It creates a new object from a payload, which will drop. + /// This can lead to a double drop, in case the payload is also handled by another object. + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self; /// Parses given bytes as a representation of the object, either owned, or a reference. /// @@ -74,6 +77,18 @@ pub trait ManagedVecItem: 'static { fn requires_drop() -> bool { false } + + fn temp_decode(payload: &Self::PAYLOAD, f: F) -> R + where + F: FnOnce(&Self) -> R, + { + unsafe { + let item = Self::read_from_payload(payload); + let result = f(&item); + core::mem::forget(item); + result + } + } } /// Used by the ManagedVecItem derive. @@ -115,7 +130,7 @@ macro_rules! impl_int { const SKIPS_RESERIALIZATION: bool = true; type Ref<'a> = Self; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { $ty::from_be_bytes(payload.buffer.into_array()) } @@ -141,7 +156,7 @@ impl ManagedVecItem for usize { const SKIPS_RESERIALIZATION: bool = true; type Ref<'a> = Self; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { u32::read_from_payload(payload) as usize } @@ -159,7 +174,7 @@ impl ManagedVecItem for bool { const SKIPS_RESERIALIZATION: bool = true; type Ref<'a> = Self; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { u8::read_from_payload(payload) > 0 } @@ -184,7 +199,7 @@ where const SKIPS_RESERIALIZATION: bool = false; type Ref<'a> = ManagedVecRef<'a, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { let (p1, p2) = as ManagedVecItemPayloadAdd< T::PAYLOAD, >>::split_from_add(payload); @@ -224,7 +239,7 @@ macro_rules! impl_managed_type { const SKIPS_RESERIALIZATION: bool = false; type Ref<'a> = ManagedRef<'a, M, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { let handle = use_raw_handle(i32::read_from_payload(payload)); unsafe { Self::from_handle(handle) } } @@ -258,7 +273,7 @@ where const SKIPS_RESERIALIZATION: bool = false; type Ref<'a> = ManagedRef<'a, M, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { let handle = use_raw_handle(i32::read_from_payload(payload)); unsafe { Self::from_handle(handle) } } @@ -287,7 +302,7 @@ where const SKIPS_RESERIALIZATION: bool = false; type Ref<'a> = ManagedRef<'a, M, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { let handle = use_raw_handle(i32::read_from_payload(payload)); unsafe { Self::from_handle(handle) } } @@ -312,7 +327,7 @@ impl ManagedVecItem for EsdtTokenType { const SKIPS_RESERIALIZATION: bool = true; type Ref<'a> = Self; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { u8::read_from_payload(payload).into() } @@ -330,7 +345,7 @@ impl ManagedVecItem for EsdtLocalRole { const SKIPS_RESERIALIZATION: bool = false; // TODO: might be ok to be true, but needs testing type Ref<'a> = Self; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { u16::read_from_payload(payload).into() } @@ -353,7 +368,7 @@ where const SKIPS_RESERIALIZATION: bool = T1::SKIPS_RESERIALIZATION && T2::SKIPS_RESERIALIZATION; type Ref<'a> = ManagedVecRef<'a, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { let mut index = 0; unsafe { ( @@ -390,7 +405,7 @@ where const SKIPS_RESERIALIZATION: bool = T1::SKIPS_RESERIALIZATION && T2::SKIPS_RESERIALIZATION; type Ref<'a> = ManagedVecRef<'a, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { let mut index = 0; unsafe { ( diff --git a/framework/base/src/types/managed/wrapped/managed_vec_iter_owned.rs b/framework/base/src/types/managed/wrapped/managed_vec_iter_owned.rs index f4712e7464..13c2aa6e08 100644 --- a/framework/base/src/types/managed/wrapped/managed_vec_iter_owned.rs +++ b/framework/base/src/types/managed/wrapped/managed_vec_iter_owned.rs @@ -54,7 +54,8 @@ where fn next(&mut self) -> Option { let payload = self.payload_iter.next()?; - Some(T::read_from_payload(&payload)) + // ok, because the iterator has ownership over the payload and no drop + unsafe { Some(T::read_from_payload(&payload)) } } fn size_hint(&self) -> (usize, Option) { @@ -76,7 +77,8 @@ where { fn next_back(&mut self) -> Option { let payload = self.payload_iter.next_back()?; - Some(T::read_from_payload(&payload)) + // ok, because the iterator has ownership over the payload and no drop + unsafe { Some(T::read_from_payload(&payload)) } } } diff --git a/framework/derive/src/managed_vec_item_derive.rs b/framework/derive/src/managed_vec_item_derive.rs index 057b0ba643..0c327eb59f 100644 --- a/framework/derive/src/managed_vec_item_derive.rs +++ b/framework/derive/src/managed_vec_item_derive.rs @@ -158,7 +158,7 @@ fn enum_derive(data_enum: &syn::DataEnum, ast: &syn::DeriveInput) -> TokenStream const SKIPS_RESERIALIZATION: bool = #skips_reserialization; type Ref<'a> = multiversx_sc::types::ManagedVecRef<'a, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { let mut index = 0; unsafe { @@ -223,7 +223,7 @@ fn struct_derive(data_struct: &syn::DataStruct, ast: &syn::DeriveInput) -> Token const SKIPS_RESERIALIZATION: bool = #(#skips_reserialization_snippets)&&*; type Ref<'a> = multiversx_sc::types::ManagedVecRef<'a, Self>; - fn read_from_payload(payload: &Self::PAYLOAD) -> Self { + unsafe fn read_from_payload(payload: &Self::PAYLOAD) -> Self { let mut index = 0; unsafe { diff --git a/framework/scenario/tests/derive_managed_vec_item_biguint_test.rs b/framework/scenario/tests/derive_managed_vec_item_biguint_test.rs index a60a688c93..ce64e9fe75 100644 --- a/framework/scenario/tests/derive_managed_vec_item_biguint_test.rs +++ b/framework/scenario/tests/derive_managed_vec_item_biguint_test.rs @@ -61,9 +61,10 @@ fn managed_struct_from_bytes_reader() { let handle_bytes = s.big_uint.get_handle().to_be_bytes(); let arr: [u8; 8] = [0xff, 0xff, 0xff, handle_bytes[3], 0x00, 0x01, 0x23, 0x45]; - let struct_from_bytes = - as multiversx_sc::types::ManagedVecItem>::read_from_payload( - &arr.into() - ); - assert_eq!(s, struct_from_bytes); + as multiversx_sc::types::ManagedVecItem>::temp_decode( + &arr.into(), + |struct_from_bytes| { + assert_eq!(&s, struct_from_bytes); + }, + ); } diff --git a/framework/scenario/tests/derive_managed_vec_item_decimal_test.rs b/framework/scenario/tests/derive_managed_vec_item_decimal_test.rs index 554942bd36..4c2016b41a 100644 --- a/framework/scenario/tests/derive_managed_vec_item_decimal_test.rs +++ b/framework/scenario/tests/derive_managed_vec_item_decimal_test.rs @@ -58,10 +58,11 @@ fn struct_with_decimal_read_write() { s.clone(), &mut payload, ); - let struct_from_bytes = + let struct_from_bytes = unsafe { as multiversx_sc::types::ManagedVecItem>::read_from_payload( &payload - ); + ) + }; assert_eq!(struct_from_bytes, s); // check payload diff --git a/framework/scenario/tests/derive_managed_vec_item_enum_1.rs b/framework/scenario/tests/derive_managed_vec_item_enum_1.rs index f2f0805cb6..b2eb71bdaa 100644 --- a/framework/scenario/tests/derive_managed_vec_item_enum_1.rs +++ b/framework/scenario/tests/derive_managed_vec_item_enum_1.rs @@ -65,29 +65,34 @@ fn enum_to_bytes_writer_variant_3() { #[test] fn enum_from_bytes_reader_variant_1() { let payload = [0, 0, 0, 0, 0, 0, 0, 0, 0]; - let enum_from_bytes = - ::read_from_payload( - &payload.into(), - ); - assert_eq!(enum_from_bytes, EnumWithFields::Variant1(0)); + + ::temp_decode( + &payload.into(), + |enum_from_bytes| { + assert_eq!(enum_from_bytes, &EnumWithFields::Variant1(0)); + }, + ); } #[test] fn enum_from_bytes_reader_variant_2() { let payload = [1, 0, 0, 0, 0, 0, 0, 0, 0]; - let enum_from_bytes = - ::read_from_payload( - &payload.into(), - ); - assert_eq!(enum_from_bytes, EnumWithFields::Variant2); + + ::temp_decode( + &payload.into(), + |enum_from_bytes| { + assert_eq!(enum_from_bytes, &EnumWithFields::Variant2); + }, + ); } #[test] fn enum_from_bytes_reader_variant_3() { let payload = [2, 0, 0, 0, 0, 0, 0, 0, 4]; - let enum_from_bytes = - ::read_from_payload( - &payload.into(), - ); - assert_eq!(enum_from_bytes, EnumWithFields::Variant3(4)); + ::temp_decode( + &payload.into(), + |enum_from_bytes| { + assert_eq!(enum_from_bytes, &EnumWithFields::Variant3(4)); + }, + ); } diff --git a/framework/scenario/tests/derive_managed_vec_item_enum_simple.rs b/framework/scenario/tests/derive_managed_vec_item_enum_simple.rs index 2dfdd76c63..f736ed015d 100644 --- a/framework/scenario/tests/derive_managed_vec_item_enum_simple.rs +++ b/framework/scenario/tests/derive_managed_vec_item_enum_simple.rs @@ -41,7 +41,11 @@ fn enum_to_bytes_writer() { #[test] fn enum_from_bytes_reader() { - let enum_from_bytes = - ::read_from_payload(&[1u8].into()); - assert_eq!(enum_from_bytes, SimpleEnum::Variant2); + + ::temp_decode( + &[1u8].into(), + |enum_from_bytes| { + assert_eq!(enum_from_bytes, &SimpleEnum::Variant2); + }, + ); } diff --git a/framework/scenario/tests/derive_managed_vec_item_esdt_token_payment_test.rs b/framework/scenario/tests/derive_managed_vec_item_esdt_token_payment_test.rs index 9e7c8e2277..3c1c4b781e 100644 --- a/framework/scenario/tests/derive_managed_vec_item_esdt_token_payment_test.rs +++ b/framework/scenario/tests/derive_managed_vec_item_esdt_token_payment_test.rs @@ -89,10 +89,10 @@ fn struct_from_bytes_reader() { handle3[1], handle3[2], handle3[3], handle4[0], handle4[1], handle4[2], handle4[3], ]; - let struct_from_bytes = - as multiversx_sc::types::ManagedVecItem>::read_from_payload( - &arr.into() - ); - - assert_eq!(s, struct_from_bytes); + as multiversx_sc::types::ManagedVecItem>::temp_decode( + &arr.into(), + |struct_from_bytes| { + assert_eq!(&s, struct_from_bytes); + }, + ); } diff --git a/framework/scenario/tests/derive_managed_vec_item_struct_1_test.rs b/framework/scenario/tests/derive_managed_vec_item_struct_1_test.rs index 39642b0774..88204e1dde 100644 --- a/framework/scenario/tests/derive_managed_vec_item_struct_1_test.rs +++ b/framework/scenario/tests/derive_managed_vec_item_struct_1_test.rs @@ -89,7 +89,10 @@ fn struct_1_from_bytes_reader() { 0x00, ]; - let struct_from_bytes = - ::read_from_payload(&arr.into()); - assert_eq!(s, struct_from_bytes); + ::temp_decode( + &arr.into(), + |struct_from_bytes| { + assert_eq!(&s, struct_from_bytes); + }, + ); } diff --git a/framework/scenario/tests/derive_managed_vec_item_struct_2_test.rs b/framework/scenario/tests/derive_managed_vec_item_struct_2_test.rs index 5d0542a24d..2f4111e2bf 100644 --- a/framework/scenario/tests/derive_managed_vec_item_struct_2_test.rs +++ b/framework/scenario/tests/derive_managed_vec_item_struct_2_test.rs @@ -79,7 +79,11 @@ fn struct_2_from_bytes_reader() { /* arr */ 0x61, 0x11, 0x62, 0x22, ]; - let struct_from_bytes = - ::read_from_payload(&payload.into()); - assert_eq!(expected_struct, struct_from_bytes); + + ::temp_decode( + &payload.into(), + |struct_from_bytes| { + assert_eq!(&expected_struct, struct_from_bytes); + }, + ); } From a93e9062dcb6ea0f6e0f6883d2263d437b5dc388 Mon Sep 17 00:00:00 2001 From: Mihai Calin Luca Date: Fri, 18 Jul 2025 16:40:19 +0300 Subject: [PATCH 04/11] mVec does not force mBuffer drop, vh is dropped explicitly --- framework/base/src/types/managed/wrapped/managed_vec.rs | 3 +-- framework/scenario/src/api/impl_vh/static_api.rs | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/framework/base/src/types/managed/wrapped/managed_vec.rs b/framework/base/src/types/managed/wrapped/managed_vec.rs index 079871f6ff..71ca4f8fa6 100644 --- a/framework/base/src/types/managed/wrapped/managed_vec.rs +++ b/framework/base/src/types/managed/wrapped/managed_vec.rs @@ -1,7 +1,7 @@ use super::{EncodedManagedVecItem, ManagedVecItemPayload}; use crate::{ abi::{TypeAbi, TypeAbiFrom, TypeDescriptionContainer, TypeName}, - api::{ErrorApiImpl, InvalidSliceError, ManagedTypeApi, ManagedTypeApiImpl}, + api::{ErrorApiImpl, InvalidSliceError, ManagedTypeApi}, codec::{ DecodeErrorHandler, EncodeErrorHandler, IntoMultiValue, NestedDecode, NestedDecodeInput, NestedEncode, NestedEncodeOutput, TopDecode, TopDecodeInput, TopEncode, @@ -650,7 +650,6 @@ where core::mem::drop(item); } } - M::managed_type_impl().drop_managed_buffer(self.get_handle()); } } } diff --git a/framework/scenario/src/api/impl_vh/static_api.rs b/framework/scenario/src/api/impl_vh/static_api.rs index 1e5e61b140..fe41d03104 100644 --- a/framework/scenario/src/api/impl_vh/static_api.rs +++ b/framework/scenario/src/api/impl_vh/static_api.rs @@ -29,7 +29,9 @@ impl VMHooksApiBackend for StaticApiBackend { { STATIC_API_VH_CELL.with(|vh_mutex| { let mut vh = vh_mutex.lock().unwrap(); - f(&mut *vh).unwrap_or_else(|err| ContractDebugInstanceState::early_exit_panic(err)) + let result = f(&mut *vh); + std::mem::drop(vh); + result.unwrap_or_else(|err| ContractDebugInstanceState::early_exit_panic(err)) }) } From f9cfb2bff34846521f160a88fb22ef56cbef92c6 Mon Sep 17 00:00:00 2001 From: Mihai Calin Luca Date: Mon, 21 Jul 2025 18:55:58 +0300 Subject: [PATCH 05/11] protect dispatcher lock all apis --- framework/scenario/src/api/impl_vh/debug_api.rs | 8 ++++++-- framework/scenario/src/api/impl_vh/single_tx_api.rs | 5 +++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/framework/scenario/src/api/impl_vh/debug_api.rs b/framework/scenario/src/api/impl_vh/debug_api.rs index c7af196f3b..2c3bacdeb7 100644 --- a/framework/scenario/src/api/impl_vh/debug_api.rs +++ b/framework/scenario/src/api/impl_vh/debug_api.rs @@ -26,7 +26,9 @@ impl VMHooksApiBackend for DebugApiBackend { let tx_context_ref = instance.tx_context_ref.clone(); let vh_context = TxVMHooksContext::new(tx_context_ref, ContractDebugInstanceState); let mut dispatcher = VMHooksDispatcher::new(vh_context); - f(&mut dispatcher).unwrap_or_else(|err| ContractDebugInstanceState::early_exit_panic(err)) + let result = f(&mut dispatcher); + std::mem::drop(dispatcher); + result.unwrap_or_else(|err| ContractDebugInstanceState::early_exit_panic(err)) } fn with_vm_hooks_ctx_1(handle: Self::HandleType, f: F) -> R @@ -36,7 +38,9 @@ impl VMHooksApiBackend for DebugApiBackend { let tx_context_ref = TxContextRef(handle.context.clone()); let vh_context = TxVMHooksContext::new(tx_context_ref, ContractDebugInstanceState); let mut dispatcher = VMHooksDispatcher::new(vh_context); - f(&mut dispatcher).unwrap_or_else(|err| ContractDebugInstanceState::early_exit_panic(err)) + let result = f(&mut dispatcher); + std::mem::drop(dispatcher); + result.unwrap_or_else(|err| ContractDebugInstanceState::early_exit_panic(err)) } fn with_vm_hooks_ctx_2(handle1: Self::HandleType, handle2: Self::HandleType, f: F) -> R diff --git a/framework/scenario/src/api/impl_vh/single_tx_api.rs b/framework/scenario/src/api/impl_vh/single_tx_api.rs index 9357f5b094..29789717b0 100644 --- a/framework/scenario/src/api/impl_vh/single_tx_api.rs +++ b/framework/scenario/src/api/impl_vh/single_tx_api.rs @@ -29,8 +29,9 @@ impl VMHooksApiBackend for SingleTxApiBackend { SINGLE_TX_API_VH_CELL.with(|cell| { let vh_context = cell.lock().unwrap().clone(); let mut dispatcher = VMHooksDispatcher::new(vh_context); - f(&mut dispatcher) - .unwrap_or_else(|err| ContractDebugInstanceState::early_exit_panic(err)) + let result = f(&mut dispatcher); + std::mem::drop(dispatcher); + result.unwrap_or_else(|err| ContractDebugInstanceState::early_exit_panic(err)) }) } From d1b302a7639a946b4e309bf3dade1ef1851e5e0d Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Mon, 21 Jul 2025 19:57:50 +0300 Subject: [PATCH 06/11] MultiValueEncoded to_arg_buffer fix --- .../forwarder-barnard/src/forwarder_barnard.rs | 2 +- .../composability/forwarder-legacy/src/fwd_nft_legacy.rs | 2 +- .../composability/forwarder-queue/src/forwarder_queue.rs | 4 ++-- .../forwarder-raw/src/forwarder_raw_alt_init.rs | 4 ++-- .../forwarder-raw/src/forwarder_raw_async.rs | 2 +- .../forwarder-raw/src/forwarder_raw_deploy_upgrade.rs | 8 ++++---- .../composability/forwarder-raw/src/forwarder_raw_sync.rs | 2 +- .../forwarder/src/fwd_call_promise_direct.rs | 2 +- .../feature-tests/composability/forwarder/src/fwd_nft.rs | 2 +- .../base/src/types/interaction/tx_data/function_call.rs | 2 +- .../src/types/managed/multi_value/multi_value_encoded.rs | 8 ++++++-- 11 files changed, 21 insertions(+), 17 deletions(-) diff --git a/contracts/feature-tests/composability/forwarder-barnard/src/forwarder_barnard.rs b/contracts/feature-tests/composability/forwarder-barnard/src/forwarder_barnard.rs index f6e01b4085..3d24a0b08c 100644 --- a/contracts/feature-tests/composability/forwarder-barnard/src/forwarder_barnard.rs +++ b/contracts/feature-tests/composability/forwarder-barnard/src/forwarder_barnard.rs @@ -23,7 +23,7 @@ pub trait ForwarderBarnard { .to(&to) .gas(half_gas) .raw_call(endpoint_name) - .arguments_raw(args.to_arg_buffer()) + .arguments_raw(args.into_arg_buffer()) .returns(ReturnsHandledOrError::new().returns(ReturnsRawResult)) .sync_call_fallible(); diff --git a/contracts/feature-tests/composability/forwarder-legacy/src/fwd_nft_legacy.rs b/contracts/feature-tests/composability/forwarder-legacy/src/fwd_nft_legacy.rs index fa38e582be..5e674cf829 100644 --- a/contracts/feature-tests/composability/forwarder-legacy/src/fwd_nft_legacy.rs +++ b/contracts/feature-tests/composability/forwarder-legacy/src/fwd_nft_legacy.rs @@ -238,7 +238,7 @@ pub trait ForwarderNftModule: fwd_storage_legacy::ForwarderStorageModule { &amount, self.blockchain().get_gas_left(), &function, - &arguments.to_arg_buffer(), + &arguments.into_arg_buffer(), ); } diff --git a/contracts/feature-tests/composability/forwarder-queue/src/forwarder_queue.rs b/contracts/feature-tests/composability/forwarder-queue/src/forwarder_queue.rs index 1d97b48fc3..1627462cf6 100644 --- a/contracts/feature-tests/composability/forwarder-queue/src/forwarder_queue.rs +++ b/contracts/feature-tests/composability/forwarder-queue/src/forwarder_queue.rs @@ -96,7 +96,7 @@ pub trait ForwarderQueue { to, gas_limit, endpoint_name, - args: args.to_arg_buffer(), + args: args.into_arg_buffer(), payments, }); } @@ -144,7 +144,7 @@ pub trait ForwarderQueue { to, gas_limit, endpoint_name, - args: args.to_arg_buffer(), + args: args.into_arg_buffer(), payments, }); } diff --git a/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_alt_init.rs b/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_alt_init.rs index de16dfcff3..1ed99aa70d 100644 --- a/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_alt_init.rs +++ b/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_alt_init.rs @@ -20,7 +20,7 @@ pub trait ForwarderRawAlternativeInit: super::forwarder_raw_common::ForwarderRaw self.tx() .to(&to) .raw_call(endpoint_name) - .arguments_raw(args.to_arg_buffer()) + .arguments_raw(args.into_arg_buffer()) .async_call_and_exit(); } @@ -61,7 +61,7 @@ pub trait ForwarderRawAlternativeInit: super::forwarder_raw_common::ForwarderRaw .gas(half_gas) .egld(payment) .raw_call(endpoint_name) - .arguments_raw(args.to_arg_buffer()) + .arguments_raw(args.into_arg_buffer()) .returns(ReturnsRawResult) .sync_call(); diff --git a/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_async.rs b/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_async.rs index 2e1f7568a0..2e655f1d1e 100644 --- a/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_async.rs +++ b/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_async.rs @@ -48,7 +48,7 @@ pub trait ForwarderRawAsync: super::forwarder_raw_common::ForwarderRawCommon { self.tx() .to(to) .raw_call(endpoint_name) - .arguments_raw(args.to_arg_buffer()) + .arguments_raw(args.into_arg_buffer()) .payment(EgldOrEsdtTokenPayment::new( payment_token, 0, diff --git a/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_deploy_upgrade.rs b/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_deploy_upgrade.rs index 803119c140..94de70ccc6 100644 --- a/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_deploy_upgrade.rs +++ b/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_deploy_upgrade.rs @@ -13,7 +13,7 @@ pub trait ForwarderRawDeployUpgrade { .raw_deploy() .code(code) .code_metadata(code_metadata) - .arguments_raw(args.to_arg_buffer()) + .arguments_raw(args.into_arg_buffer()) .gas(self.blockchain().get_gas_left()) .returns(ReturnsNewManagedAddress) .returns(ReturnsRawResult) @@ -32,7 +32,7 @@ pub trait ForwarderRawDeployUpgrade { .raw_deploy() .from_source(source_contract_address) .code_metadata(code_metadata) - .arguments_raw(args.to_arg_buffer()) + .arguments_raw(args.into_arg_buffer()) .gas(self.blockchain().get_gas_left()) .returns(ReturnsNewManagedAddress) .sync_call() @@ -51,7 +51,7 @@ pub trait ForwarderRawDeployUpgrade { .raw_upgrade() .code(new_code) .code_metadata(code_metadata) - .arguments_raw(args.to_arg_buffer()) + .arguments_raw(args.into_arg_buffer()) .upgrade_async_call_and_exit(); } @@ -68,7 +68,7 @@ pub trait ForwarderRawDeployUpgrade { .raw_upgrade() .from_source(source_contract_address) .code_metadata(code_metadata) - .arguments_raw(args.to_arg_buffer()) + .arguments_raw(args.into_arg_buffer()) .gas(self.blockchain().get_gas_left()) .upgrade_async_call_and_exit(); } diff --git a/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_sync.rs b/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_sync.rs index bd1188c1bb..d2d3f62bf1 100644 --- a/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_sync.rs +++ b/contracts/feature-tests/composability/forwarder-raw/src/forwarder_raw_sync.rs @@ -36,7 +36,7 @@ pub trait ForwarderRawSync: super::forwarder_raw_common::ForwarderRawCommon { let payment = self.call_value().egld(); let one_third_gas = self.blockchain().get_gas_left() / 3; let half_payment = &*payment / 2u32; - let arg_buffer = args.to_arg_buffer(); + let arg_buffer = args.into_arg_buffer(); let result = self .tx() diff --git a/contracts/feature-tests/composability/forwarder/src/fwd_call_promise_direct.rs b/contracts/feature-tests/composability/forwarder/src/fwd_call_promise_direct.rs index e7b2696c08..f760161e65 100644 --- a/contracts/feature-tests/composability/forwarder/src/fwd_call_promise_direct.rs +++ b/contracts/feature-tests/composability/forwarder/src/fwd_call_promise_direct.rs @@ -35,7 +35,7 @@ pub trait CallPromisesDirectModule { .to(&to) .raw_call(endpoint_name) .payment(payment) - .arguments_raw(args.to_arg_buffer()) + .arguments_raw(args.into_arg_buffer()) .gas(gas_limit) .callback(self.callbacks().the_one_callback(1001, 1002u32.into())) .gas_for_callback(extra_gas_for_callback) diff --git a/contracts/feature-tests/composability/forwarder/src/fwd_nft.rs b/contracts/feature-tests/composability/forwarder/src/fwd_nft.rs index 883851f178..cd18da41a0 100644 --- a/contracts/feature-tests/composability/forwarder/src/fwd_nft.rs +++ b/contracts/feature-tests/composability/forwarder/src/fwd_nft.rs @@ -244,7 +244,7 @@ pub trait ForwarderNftModule: fwd_storage::ForwarderStorageModule { .to(&to) .gas(gas_left) .raw_call(function) - .arguments_raw(arguments.to_arg_buffer()) + .arguments_raw(arguments.into_arg_buffer()) .single_esdt(&token_identifier, nonce, &amount) .transfer_execute(); } diff --git a/framework/base/src/types/interaction/tx_data/function_call.rs b/framework/base/src/types/interaction/tx_data/function_call.rs index 2d359a04e6..84c1fd5b10 100644 --- a/framework/base/src/types/interaction/tx_data/function_call.rs +++ b/framework/base/src/types/interaction/tx_data/function_call.rs @@ -131,7 +131,7 @@ where MultiValueEncoded::>::multi_decode_or_handle_err(input, h)?; Ok(FunctionCall { function_name, - arg_buffer: args.to_arg_buffer(), + arg_buffer: args.into_arg_buffer(), }) } } diff --git a/framework/base/src/types/managed/multi_value/multi_value_encoded.rs b/framework/base/src/types/managed/multi_value/multi_value_encoded.rs index 719999b3c8..71911a8e2c 100644 --- a/framework/base/src/types/managed/multi_value/multi_value_encoded.rs +++ b/framework/base/src/types/managed/multi_value/multi_value_encoded.rs @@ -17,7 +17,7 @@ use crate::{ }, contract_base::{ExitCodecErrorHandler, ManagedSerializer}, err_msg, - types::{ManagedArgBuffer, ManagedBuffer, ManagedType, ManagedVec, ManagedVecItem}, + types::{ManagedArgBuffer, ManagedBuffer, ManagedVec, ManagedVecItem}, }; use core::{iter::FromIterator, marker::PhantomData}; @@ -107,7 +107,11 @@ where M: ManagedTypeApi, { pub fn to_arg_buffer(&self) -> ManagedArgBuffer { - unsafe { ManagedArgBuffer::from_handle(self.raw_buffers.get_handle()) } + ManagedArgBuffer::from(self.raw_buffers.clone()) + } + + pub fn into_arg_buffer(self) -> ManagedArgBuffer { + ManagedArgBuffer::from(self.raw_buffers) } } From 3e2a1a3742825bdad16e1449a793cb54ec406e5b Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Mon, 21 Jul 2025 19:57:57 +0300 Subject: [PATCH 07/11] cargo fmt --- framework/scenario/tests/derive_managed_vec_item_enum_simple.rs | 1 - .../scenario/tests/derive_managed_vec_item_struct_2_test.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/framework/scenario/tests/derive_managed_vec_item_enum_simple.rs b/framework/scenario/tests/derive_managed_vec_item_enum_simple.rs index f736ed015d..f5083ce29c 100644 --- a/framework/scenario/tests/derive_managed_vec_item_enum_simple.rs +++ b/framework/scenario/tests/derive_managed_vec_item_enum_simple.rs @@ -41,7 +41,6 @@ fn enum_to_bytes_writer() { #[test] fn enum_from_bytes_reader() { - ::temp_decode( &[1u8].into(), |enum_from_bytes| { diff --git a/framework/scenario/tests/derive_managed_vec_item_struct_2_test.rs b/framework/scenario/tests/derive_managed_vec_item_struct_2_test.rs index 2f4111e2bf..f6728c7874 100644 --- a/framework/scenario/tests/derive_managed_vec_item_struct_2_test.rs +++ b/framework/scenario/tests/derive_managed_vec_item_struct_2_test.rs @@ -79,7 +79,6 @@ fn struct_2_from_bytes_reader() { /* arr */ 0x61, 0x11, 0x62, 0x22, ]; - ::temp_decode( &payload.into(), |struct_from_bytes| { From c62ef32a0907c8a14bf9f5fea247e6238193cd82 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Mon, 21 Jul 2025 20:31:35 +0300 Subject: [PATCH 08/11] managed dealloc - removed some leaking from_handle calls --- .../src/contract_base/wrappers/blockchain_wrapper.rs | 9 ++++----- .../src/contract_base/wrappers/storage_raw_wrapper.rs | 8 ++------ framework/base/src/storage/storage_set.rs | 4 ++-- framework/base/src/types/managed/basic/managed_buffer.rs | 7 +++---- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/framework/base/src/contract_base/wrappers/blockchain_wrapper.rs b/framework/base/src/contract_base/wrappers/blockchain_wrapper.rs index 62847b6aa6..6ee12b812e 100644 --- a/framework/base/src/contract_base/wrappers/blockchain_wrapper.rs +++ b/framework/base/src/contract_base/wrappers/blockchain_wrapper.rs @@ -12,8 +12,8 @@ use crate::{ types::{ BackTransfers, BackTransfersLegacy, BigUint, CodeMetadata, EgldOrEsdtTokenIdentifier, EgldOrEsdtTokenPayment, EsdtLocalRoleFlags, EsdtTokenData, EsdtTokenType, ManagedAddress, - ManagedBuffer, ManagedByteArray, ManagedRefMut, ManagedType, ManagedVec, SystemSCAddress, - TokenIdentifier, + ManagedBuffer, ManagedByteArray, ManagedRef, ManagedRefMut, ManagedType, ManagedVec, + SystemSCAddress, TokenIdentifier, }, }; @@ -386,8 +386,7 @@ where big_int_temp_handle.clone(), ); - let bu = BigUint::::from_handle(big_int_temp_handle); - // TODO: forget bu + let bu = ManagedRef::>::wrap_handle(big_int_temp_handle); // BigUint::::from_handle(big_int_temp_handle); bu.to_u64().unwrap_or(255) } } @@ -619,7 +618,7 @@ where result_handle.clone(), ); - let result = unsafe { ManagedBuffer::::from_handle(result_handle) }; + let result = unsafe { ManagedRef::>::wrap_handle(result_handle) }; // Decoding the response needs more research // Empty response means no address has transferRole for the token diff --git a/framework/base/src/contract_base/wrappers/storage_raw_wrapper.rs b/framework/base/src/contract_base/wrappers/storage_raw_wrapper.rs index 9fd49e115b..d61032b062 100644 --- a/framework/base/src/contract_base/wrappers/storage_raw_wrapper.rs +++ b/framework/base/src/contract_base/wrappers/storage_raw_wrapper.rs @@ -6,10 +6,7 @@ use crate::api::HandleConstraints; use crate::codec::{TopDecode, TopEncode}; use crate::{ - api::{ - const_handles::MBUF_TEMPORARY_1, use_raw_handle, ErrorApi, ManagedTypeApi, StorageReadApi, - StorageReadApiImpl, StorageWriteApi, - }, + api::{ErrorApi, ManagedTypeApi, StorageReadApi, StorageReadApiImpl, StorageWriteApi}, storage::StorageKey, storage_get, storage_get::StorageGetErrorHandler, @@ -59,8 +56,7 @@ where V: TopDecode, { let key: StorageKey = storage_key.into(); - let result_buffer = - unsafe { ManagedBuffer::::from_handle(use_raw_handle(MBUF_TEMPORARY_1)) }; + let result_buffer = unsafe { ManagedBuffer::::new_uninit() }; A::storage_read_api_impl().storage_load_from_address( address.get_handle(), key.get_handle(), diff --git a/framework/base/src/storage/storage_set.rs b/framework/base/src/storage/storage_set.rs index aaa6a7ad53..5b248d44e1 100644 --- a/framework/base/src/storage/storage_set.rs +++ b/framework/base/src/storage/storage_set.rs @@ -53,7 +53,7 @@ where let handle: A::ManagedBufferHandle = use_raw_handle(const_handles::MBUF_TEMPORARY_1); A::managed_type_impl().mb_from_small_int_unsigned(handle.clone(), value as i64); - let managed_buffer = unsafe { ManagedBuffer::from_handle(handle) }; + let managed_buffer = unsafe { ManagedRef::wrap_handle(handle) }; self.set_managed_buffer(&managed_buffer); } @@ -63,7 +63,7 @@ where let handle: A::ManagedBufferHandle = use_raw_handle(const_handles::MBUF_TEMPORARY_1); A::managed_type_impl().mb_from_small_int_signed(handle.clone(), value); - let managed_buffer = unsafe { ManagedBuffer::from_handle(handle) }; + let managed_buffer = unsafe { ManagedRef::wrap_handle(handle) }; self.set_managed_buffer(&managed_buffer); } diff --git a/framework/base/src/types/managed/basic/managed_buffer.rs b/framework/base/src/types/managed/basic/managed_buffer.rs index b7d38e1c41..12992d4e4d 100644 --- a/framework/base/src/types/managed/basic/managed_buffer.rs +++ b/framework/base/src/types/managed/basic/managed_buffer.rs @@ -418,10 +418,9 @@ impl ManagedBuffer { impl Clone for ManagedBuffer { fn clone(&self) -> Self { - let api = M::managed_type_impl(); - let clone_handle = api.mb_new_empty(); - api.mb_append(clone_handle.clone(), self.handle.clone()); - unsafe { ManagedBuffer::from_handle(clone_handle) } + let cloned = ManagedBuffer::new(); + M::managed_type_impl().mb_append(cloned.get_handle(), self.handle.clone()); + cloned } } From fa1d233279b36f200e79a2b4bfd64f079d756b1e Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Thu, 24 Jul 2025 06:03:28 +0300 Subject: [PATCH 09/11] managed dealloc - ManagedVecRef soft drop via save_to_payload --- .../base/src/types/managed/wrapped/managed_vec_ref.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/framework/base/src/types/managed/wrapped/managed_vec_ref.rs b/framework/base/src/types/managed/wrapped/managed_vec_ref.rs index a10c5c234b..27b8d888c0 100644 --- a/framework/base/src/types/managed/wrapped/managed_vec_ref.rs +++ b/framework/base/src/types/managed/wrapped/managed_vec_ref.rs @@ -1,3 +1,4 @@ +use super::ManagedVecItemPayload; use crate::types::ManagedVecItem; use core::{borrow::Borrow, fmt::Debug, marker::PhantomData, mem::ManuallyDrop, ops::Deref}; @@ -31,9 +32,15 @@ where T: ManagedVecItem, { fn drop(&mut self) { - // TODO: improve + // the payload is a dummy + // it is used because save_to_payload does a great job doing the soft deallocation needed here + // + // TODO: the saving as payload is not necessary, figure out if it is worth optimizing + // by adding a special soft drop method to ManagedVecItem + let mut dummy_payload = T::PAYLOAD::new_buffer(); unsafe { - ManuallyDrop::drop(&mut self.item); + let inner = ManuallyDrop::take(&mut self.item); + inner.save_to_payload(&mut dummy_payload); } } } From 534d39cf90d9f167e0a687ffb9abd1f86e43d46a Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Thu, 24 Jul 2025 06:05:25 +0300 Subject: [PATCH 10/11] managed dealloc - ManagedVecRefMut cleanup & comment --- .../src/types/managed/wrapped/managed_vec_ref_mut.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/framework/base/src/types/managed/wrapped/managed_vec_ref_mut.rs b/framework/base/src/types/managed/wrapped/managed_vec_ref_mut.rs index 37a642648f..960c38f19c 100644 --- a/framework/base/src/types/managed/wrapped/managed_vec_ref_mut.rs +++ b/framework/base/src/types/managed/wrapped/managed_vec_ref_mut.rs @@ -53,13 +53,16 @@ where T: ManagedVecItem, { fn drop(&mut self) { + // This drop saves the item back into the p-arent ManagedVec. + // + // The `set` method also handles soft deallocation + // (freeing of the handle, without deallocating the underlying resource). let item = unsafe { ManuallyDrop::take(&mut self.item) }; unsafe { - let _ = - ManagedRefMut::>::wrap_handle(self.managed_vec_handle.clone()) - .set(self.item_index, item); + let mut parent_ref = + ManagedRefMut::>::wrap_handle(self.managed_vec_handle.clone()); + let _ = parent_ref.set(self.item_index, item); } - // core::mem::forget(item); } } From 20843a8752859717d08f7badee497d880b0241cb Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Thu, 24 Jul 2025 06:16:16 +0300 Subject: [PATCH 11/11] typo --- chain/vm/src/host/context/managed_type_container/handle_map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/vm/src/host/context/managed_type_container/handle_map.rs b/chain/vm/src/host/context/managed_type_container/handle_map.rs index 99648f1a14..8390a425f2 100644 --- a/chain/vm/src/host/context/managed_type_container/handle_map.rs +++ b/chain/vm/src/host/context/managed_type_container/handle_map.rs @@ -51,7 +51,7 @@ impl HandleMap { pub fn remove_handle(&mut self, handle: RawHandle) { assert!( self.map.contains_key(&handle), - "attepting to remove non-existing handle {handle}, this is a memory managedment issue" + "attempting to remove non-existing handle {handle}, this is a memory managedment issue" ); let _ = self.map.remove(&handle); }