Skip to content

Commit

Permalink
[mono][interp] Keep delegate alive during invocation (dotnet#100832)
Browse files Browse the repository at this point in the history
When invoking a delegate, we were overwritting the stack slot containing the delegate object reference. In the case of invoking a delegate for a dynamic method, we were running into issues when the delegate object is collected while the method is executed because the method code is also discarded.
  • Loading branch information
BrzVlad authored and matouskozak committed Apr 30, 2024
1 parent 3f172dc commit 76aaedc
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/mono/mono/mini/interp/interp-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ struct InterpMethod {
unsigned int hasthis; // boolean
MonoProfilerCallInstrumentationFlags prof_flags;
InterpMethodCodeType code_type;
int ref_slot_offset; // GC visible pointer slot
MonoBitSet *ref_slots;
#ifdef ENABLE_EXPERIMENT_TIERED
MiniTieredCounter tiered_counter;
Expand Down
3 changes: 3 additions & 0 deletions src/mono/mono/mini/interp/interp.c
Original file line number Diff line number Diff line change
Expand Up @@ -4164,6 +4164,9 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause
}
cmethod = del_imethod;
if (!is_multicast) {
int ref_slot_offset = frame->imethod->ref_slot_offset;
if (ref_slot_offset >= 0)
LOCAL_VAR (ref_slot_offset, gpointer) = del;
int param_count = ip [4];
if (cmethod->param_count == param_count + 1) {
// Target method is static but the delegate has a target object. We handle
Expand Down
19 changes: 19 additions & 0 deletions src/mono/mono/mini/interp/transform.c
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,15 @@ interp_create_dummy_var (TransformData *td)
td->vars [td->dummy_var].global = TRUE;
}

static void
interp_create_ref_handle_var (TransformData *td)
{
int var = interp_create_var_explicit (td, m_class_get_byval_arg (mono_defaults.int_class), sizeof (gpointer));
td->vars [var].global = TRUE;
interp_alloc_global_var_offset (td, var);
td->ref_handle_var = var;
}

static int
get_tos_offset (TransformData *td)
{
Expand Down Expand Up @@ -3756,6 +3765,10 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target
td->last_ins->data [0] = get_data_item_index_imethod (td, mono_interp_get_imethod (target_method));
} else {
if (is_delegate_invoke) {
// MINT_CALL_DELEGATE will store the delegate object into this slot so it is kept alive
// while the method is invoked
if (td->ref_handle_var == -1)
interp_create_ref_handle_var (td);
interp_add_ins (td, MINT_CALL_DELEGATE);
interp_ins_set_dreg (td->last_ins, dreg);
interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG);
Expand Down Expand Up @@ -9085,6 +9098,7 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG
td->n_data_items = 0;
td->max_data_items = 0;
td->dummy_var = -1;
td->ref_handle_var = -1;
td->data_items = NULL;
td->data_hash = g_hash_table_new (NULL, NULL);
#ifdef ENABLE_EXPERIMENT_TIERED
Expand Down Expand Up @@ -9260,6 +9274,11 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG
}
}

if (td->ref_handle_var != -1)
rtm->ref_slot_offset = td->vars [td->ref_handle_var].offset;
else
rtm->ref_slot_offset = -1;

/* Save debug info */
interp_save_debug_info (rtm, header, td, td->line_numbers);

Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/mini/interp/transform.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ typedef struct
gint32 total_locals_size;
gint32 max_stack_size;
int dummy_var;
int ref_handle_var;
int *local_ref_count;
unsigned int il_locals_offset;
unsigned int il_locals_size;
Expand Down

0 comments on commit 76aaedc

Please sign in to comment.