Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a25bb62
managed dealloc - api
andrei-marinica Jun 10, 2024
9dd2bff
drop for managed buffer
alyn509 Jun 18, 2024
951a7d8
leave just managed buffer destructor
alyn509 Jun 18, 2024
db4ea70
Merge branch 'master' into mdrop
alyn509 Aug 14, 2024
8c93071
Merge branch 'rc/v0.54' into mdrop
andrei-marinica Sep 18, 2024
38d100e
Merge branch 'rc/v0.54' into mdrop
andrei-marinica Oct 25, 2024
94e6981
managed dealloc - implementation
andrei-marinica Oct 27, 2024
96f3944
Merge branch 'rc/v0.54' into mdrop
andrei-marinica Oct 28, 2024
74e38a7
Merge branch 'managed-ref-mut' into mdrop-merge
andrei-marinica Nov 11, 2024
b54f8d5
Merge branch 'man-opt-refactor' into mdrop-merge
andrei-marinica Nov 20, 2024
4b87e5b
Merge branch 'master' into mdrop3
andrei-marinica Jul 7, 2025
a81e3fd
Merge pull request #2112 from multiversx/mdrop3
andrei-marinica Jul 8, 2025
ca9cc06
DebugHandle Weak pointer to TxContext
andrei-marinica Sep 18, 2025
2cb3343
vm - signal error utf-8 lossy
andrei-marinica Dec 29, 2025
57e7c43
ManagedBuffer Display utf8 lossy
andrei-marinica Dec 29, 2025
5162d8d
Merge branch 'master' into weak-handle-context
andrei-marinica Dec 29, 2025
7db86dd
DebugHandle Weak pointer to TxContext LLDB fix
andrei-marinica Dec 29, 2025
292413c
DebugHandle ptr eq optimization
andrei-marinica Dec 29, 2025
acdd326
docs
andrei-marinica Dec 29, 2025
691c929
DebugHandle LLVM dropped TxContext error & tests
andrei-marinica Dec 29, 2025
56954bf
lldb py ignore warning
andrei-marinica Dec 29, 2025
f7cda84
test fix
andrei-marinica Dec 30, 2025
356e455
python formatting
andrei-marinica Dec 30, 2025
147d153
Merge pull request #2260 from multiversx/utf8-lossy
andrei-marinica Dec 30, 2025
fe875bc
Merge pull request #2160 from multiversx/weak-handle-context
andrei-marinica Jan 8, 2026
5b0c3be
reqwest upgrade, using rusttls
andrei-marinica Jan 12, 2026
b2fd0a6
Merge pull request #2266 from multiversx/rusttls
andrei-marinica Jan 13, 2026
7c29692
Merge branch 'master' into rc/v0.65
andrei-marinica Jan 15, 2026
3a5f7c7
Merge pull request #2268 from multiversx/merge-master-rc-65
andrei-marinica Jan 16, 2026
a001f6a
Merge branch 'rc/v0.65' into feat/mdrop
andrei-marinica Jan 16, 2026
b7069f4
Merge pull request #2269 from multiversx/feat-mdrop-merge
andrei-marinica Jan 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
394 changes: 232 additions & 162 deletions Cargo.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,8 @@ impl<V> HandleMap<V> {
pub fn insert(&mut self, handle: RawHandle, value: V) {
let _ = self.map.insert(handle, value);
}

pub fn remove_handle(&mut self, handle: RawHandle) {
let _ = self.map.remove(&handle);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ impl ManagedTypeContainer {
pub fn bf_overwrite(&mut self, handle: RawHandle, value: f64) {
self.big_float_map.insert(handle, value);
}

pub fn bf_remove(&mut self, handle: RawHandle) {
self.big_float_map.remove_handle(handle);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ impl ManagedTypeContainer {
self.big_int_map.insert_new_handle_raw(value)
}

pub fn bi_remove(&mut self, handle: RawHandle) {
self.big_int_map.remove_handle(handle);
}

pub fn bi_overwrite(&mut self, destination: RawHandle, value: num_bigint::BigInt) {
self.big_int_map.insert(destination, value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ impl ManagedTypeContainer {

num_bytes_copied
}

pub fn mb_remove(&mut self, handle: RawHandle) {
self.managed_buffer_map.remove_handle(handle);
}
}

pub fn handle_to_be_bytes(handle: RawHandle) -> [u8; 4] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@ impl ManagedTypeContainer {
let mmap = self.managed_map_map.get_mut(map_handle);
mmap.remove(key).unwrap_or_default()
}

pub fn mm_remove(&mut self, handle: RawHandle) {
self.managed_map_map.remove_handle(handle);
}
}
28 changes: 27 additions & 1 deletion chain/vm/src/host/context/tx_context_ref.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::{ops::Deref, sync::Arc};
use std::{
ops::Deref,
sync::{Arc, Weak},
};

use crate::host::context::{TxContext, TxResult};

Expand Down Expand Up @@ -62,7 +65,30 @@ impl TxContextRef {
Arc::ptr_eq(&this.0, &other.0)
}

/// Returns a raw pointer to the underlying `TxContext`.
///
/// This is useful for pointer comparisons, particularly when comparing
/// with weak references to determine if they point to the same context.
pub fn as_ptr(&self) -> *const TxContext {
Arc::as_ptr(&self.0)
}

pub fn into_ref(self) -> Arc<TxContext> {
self.0
}

/// Creates a new [`Weak`] pointer to the [`TxContext`].
///
/// This is the preferred way to obtain a non‑owning reference to the underlying
/// `TxContext` when you need to store a handle that should not keep the transaction
/// alive on its own. In particular, this method underpins the weak‑pointer pattern
/// used by [`DebugHandle`], which holds a `Weak<TxContext>` so that debug tooling
/// can observe a transaction while it exists, without extending its lifetime.
///
/// Callers that use the returned [`Weak`] must call [`Weak::upgrade`] before
/// accessing the `TxContext` and be prepared to handle the case where upgrading
/// fails because the transaction context has already been dropped.
pub fn downgrade(&self) -> Weak<TxContext> {
Arc::downgrade(&self.0)
}
}
2 changes: 1 addition & 1 deletion chain/vm/src/host/vm_hooks/vh_dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub(super) const RESULT_ERROR: i32 = 1;
/// Dispatches messages coming via VMHooks to the underlying implementation (the VMHooksHandler).
#[derive(Debug)]
pub struct VMHooksDispatcher<C: VMHooksContext> {
pub(crate) handler: VMHooksHandler<C>,
pub handler: VMHooksHandler<C>,
}

impl<C: VMHooksContext> VMHooksDispatcher<C> {
Expand Down
17 changes: 5 additions & 12 deletions chain/vm/src/host/vm_hooks/vh_handler/vh_error.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
use multiversx_chain_core::types::ReturnCode;
use multiversx_chain_vm_executor::VMHooksEarlyExit;

use crate::{
host::vm_hooks::{VMHooksContext, vh_early_exit::early_exit_vm_error},
types::RawHandle,
};
use crate::{host::vm_hooks::VMHooksContext, types::RawHandle};

use super::VMHooksHandler;

impl<C: VMHooksContext> VMHooksHandler<C> {
pub fn signal_error(&mut self, message: &[u8]) -> Result<(), VMHooksEarlyExit> {
match String::from_utf8(message.to_owned()) {
Ok(message_string) => {
self.context.log_error_trace(&message_string);
Err(VMHooksEarlyExit::new(ReturnCode::UserError.as_u64())
.with_message(message_string))
}
Err(_) => Err(early_exit_vm_error("error message utf-8 error")),
}
let message_string = String::from_utf8_lossy(message);
self.context.log_error_trace(&message_string);
Err(VMHooksEarlyExit::new(ReturnCode::UserError.as_u64())
.with_message(message_string.to_string()))
}

pub fn signal_error_from_buffer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,4 +285,8 @@ impl<C: VMHooksContext> VMHooksHandler<C> {

Ok(())
}

pub fn bf_drop(&self, map_handle: RawHandle) {
self.context.m_types_lock().bf_remove(map_handle);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -360,4 +360,8 @@ impl<C: VMHooksContext> VMHooksHandler<C> {

Ok(())
}

pub fn bi_drop(&self, map_handle: RawHandle) {
self.context.m_types_lock().bi_remove(map_handle);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,8 @@ impl<C: VMHooksContext> VMHooksHandler<C> {
.mb_set(dest_handle, encoded.into_bytes());
Ok(())
}

pub fn mb_drop(&self, handle: RawHandle) {
self.context.m_types_lock().mb_remove(handle);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,8 @@ impl<C: VMHooksContext> VMHooksHandler<C> {
.m_types_lock()
.mm_contains(map_handle, key.as_slice())
}

pub fn mm_drop(&self, map_handle: RawHandle) {
self.context.m_types_lock().mm_remove(map_handle);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,10 @@ fn test_managed_address_zero() {
let result = bf.managed_address_zero();
assert_eq!(ManagedAddress::zero(), result);
}

#[test]
fn test_managed_buffer_destructor() {
let my_buffer = ManagedBuffer::<StaticApi>::from(b"my buffer");
assert_eq!(my_buffer, managed_buffer!(b"my buffer"));
drop(my_buffer);
}
6 changes: 6 additions & 0 deletions framework/base/src/api/managed_types/managed_type_api_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,10 @@ pub trait ManagedTypeApiImpl:
fn get_token_ticker_len(&self, token_id_len: usize) -> usize {
token_identifier_util::get_token_ticker_len(token_id_len)
}

fn drop_managed_buffer(&self, _handle: Self::ManagedBufferHandle) {}
fn drop_big_float(&self, _handle: Self::BigFloatHandle) {}
fn drop_big_int(&self, _handle: Self::BigIntHandle) {}
fn drop_elliptic_curve(&self, _handle: Self::EllipticCurveHandle) {}
fn drop_managed_map(&self, _handle: Self::ManagedMapHandle) {}
}
14 changes: 9 additions & 5 deletions framework/base/src/types/managed/basic/managed_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,13 @@ impl<M: ManagedTypeApi> Clone for ManagedBuffer<M> {
}
}

impl<M: ManagedTypeApi> Drop for ManagedBuffer<M> {
fn drop(&mut self) {
// TODO: enable, after fixing all ownership issues
// M::managed_type_impl().drop_managed_buffer(self.handle.clone());
}
}

impl<M: ManagedTypeApi> PartialEq for ManagedBuffer<M> {
#[inline]
fn eq(&self, other: &Self) -> bool {
Expand Down Expand Up @@ -583,11 +590,8 @@ impl<M: ManagedTypeApi> core::fmt::Debug for ManagedBuffer<M> {

impl<M: ManagedTypeApi> core::fmt::Display for ManagedBuffer<M> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use crate::contract_base::ErrorHelper;

let s = alloc::string::String::from_utf8(self.to_boxed_bytes().into_vec())
.unwrap_or_else(|err| ErrorHelper::<M>::signal_error_with_message(err.as_bytes()));

let bytes = self.to_boxed_bytes();
let s = alloc::string::String::from_utf8_lossy(bytes.as_slice());
s.fmt(f)
}
}
2 changes: 1 addition & 1 deletion framework/meta/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ convert_case = "0.10"
semver = "1.0.20"
ruplacer = { version = "0.10", default-features = false }
regex = "1.11"
reqwest = { version = "0.12", features = ["blocking", "json"] }
reqwest = { version = "0.13", features = ["rustls", "blocking", "json"] }
zip = { version = "6.0", features = ["deflate"], default-features = false }
copy_dir = "0.1.2"
pathdiff = "0.2.1"
Expand Down
14 changes: 7 additions & 7 deletions framework/scenario/src/api/impl_vh/debug_api.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use multiversx_chain_vm::{
executor::{VMHooks, VMHooksEarlyExit},
host::context::TxContextRef,
executor::VMHooksEarlyExit,
host::vm_hooks::{TxVMHooksContext, VMHooksDispatcher},
};
use multiversx_sc::{chain_core::types::ReturnCode, err_msg};

use crate::executor::debug::{
ContractDebugInstance, ContractDebugInstanceState, ContractDebugStack, StaticVarData,
VMHooksDebugger,
};

use super::{DebugHandle, VMHooksApi, VMHooksApiBackend};
Expand All @@ -19,7 +19,7 @@ impl VMHooksApiBackend for DebugApiBackend {

fn with_vm_hooks<R, F>(f: F) -> R
where
F: FnOnce(&mut dyn VMHooks) -> Result<R, VMHooksEarlyExit>,
F: FnOnce(&mut dyn VMHooksDebugger) -> Result<R, VMHooksEarlyExit>,
{
let instance = ContractDebugStack::static_peek();
let tx_context_ref = instance.tx_context_ref.clone();
Expand All @@ -30,17 +30,17 @@ impl VMHooksApiBackend for DebugApiBackend {

fn with_vm_hooks_ctx_1<R, F>(handle: Self::HandleType, f: F) -> R
where
F: FnOnce(&mut dyn VMHooks) -> Result<R, VMHooksEarlyExit>,
F: FnOnce(&mut dyn VMHooksDebugger) -> Result<R, VMHooksEarlyExit>,
{
let tx_context_ref = TxContextRef(handle.context.clone());
let tx_context_ref = handle.to_tx_context_ref();
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))
}

fn with_vm_hooks_ctx_2<R, F>(handle1: Self::HandleType, handle2: Self::HandleType, f: F) -> R
where
F: FnOnce(&mut dyn VMHooks) -> Result<R, VMHooksEarlyExit>,
F: FnOnce(&mut dyn VMHooksDebugger) -> Result<R, VMHooksEarlyExit>,
{
assert_handles_on_same_context(&handle1, &handle2);
Self::with_vm_hooks_ctx_1(handle1, f)
Expand All @@ -53,7 +53,7 @@ impl VMHooksApiBackend for DebugApiBackend {
f: F,
) -> R
where
F: FnOnce(&mut dyn VMHooks) -> Result<R, VMHooksEarlyExit>,
F: FnOnce(&mut dyn VMHooksDebugger) -> Result<R, VMHooksEarlyExit>,
{
assert_handles_on_same_context(&handle1, &handle2);
assert_handles_on_same_context(&handle1, &handle3);
Expand Down
58 changes: 44 additions & 14 deletions framework/scenario/src/api/impl_vh/debug_handle_vh.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::sync::Arc;
use std::sync::Weak;

use multiversx_chain_vm::host::context::TxContext;
use multiversx_chain_vm::host::context::{TxContext, TxContextRef};
use multiversx_sc::{
api::{HandleConstraints, RawHandle},
codec::TryStaticCast,
Expand All @@ -10,22 +10,30 @@ use crate::executor::debug::ContractDebugStack;

#[derive(Clone)]
pub struct DebugHandle {
/// TODO: would be nice to be an actual TxContextRef,
/// but that requires changing the debugger scripts
pub(crate) context: Arc<TxContext>,
/// Only keep a weak reference to the context, to avoid stray handles keeping the context from being released.
/// Using the pointer after the context is released will panic.
pub(crate) context: Weak<TxContext>,
raw_handle: RawHandle,
}

impl DebugHandle {
/// Should almost never call directly, only used directly in a test.
pub fn new_with_explicit_context_ref(context: Weak<TxContext>, raw_handle: RawHandle) -> Self {
Self {
context,
raw_handle,
}
}

pub fn is_on_current_context(&self) -> bool {
Arc::ptr_eq(
&self.context,
&ContractDebugStack::static_peek().tx_context_ref.into_ref(),
std::ptr::eq(
self.context.as_ptr(),
ContractDebugStack::static_peek().tx_context_ref.as_ptr(),
)
}

pub fn is_on_same_context(&self, other: &DebugHandle) -> bool {
Arc::ptr_eq(&self.context, &other.context)
Weak::ptr_eq(&self.context, &other.context)
}

pub fn assert_current_context(&self) {
Expand All @@ -34,6 +42,30 @@ impl DebugHandle {
"Managed value not used in original context"
);
}

/// Upgrades the weak reference to a strong `TxContextRef`.
///
/// This method attempts to upgrade the weak reference stored in this handle
/// to a strong reference. This is necessary when you need to access the
/// underlying `TxContext` for operations.
///
/// # Panics
///
/// Panics if the `TxContext` is no longer valid (has been dropped). This can
/// happen if the object was created on a VM execution stack frame that has
/// already been popped, or if objects are mixed between different execution
/// contexts during whitebox testing.
pub fn to_tx_context_ref(&self) -> TxContextRef {
let tx_context_arc = self.context.upgrade().unwrap_or_else(|| {
panic!(
"TxContext is no longer valid for handle {}.
The object was created on a VM execution stack frame that has already been popped.
This can sometimes happen during whitebox testing if the objects are mixed between execution contexts.",
self.raw_handle
)
});
TxContextRef::new(tx_context_arc)
}
}

impl core::fmt::Debug for DebugHandle {
Expand All @@ -44,10 +76,8 @@ impl core::fmt::Debug for DebugHandle {

impl HandleConstraints for DebugHandle {
fn new(handle: multiversx_sc::api::RawHandle) -> Self {
Self {
context: ContractDebugStack::static_peek().tx_context_ref.into_ref(),
raw_handle: handle,
}
let context = ContractDebugStack::static_peek().tx_context_ref.downgrade();
DebugHandle::new_with_explicit_context_ref(context, handle)
}

fn to_be_bytes(&self) -> [u8; 4] {
Expand All @@ -73,7 +103,7 @@ impl PartialEq<RawHandle> for DebugHandle {

impl PartialEq<DebugHandle> for DebugHandle {
fn eq(&self, other: &DebugHandle) -> bool {
Arc::ptr_eq(&self.context, &other.context) && self.raw_handle == other.raw_handle
Weak::ptr_eq(&self.context, &other.context) && self.raw_handle == other.raw_handle
}
}

Expand Down
Loading
Loading