Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mono] refactor metadata update code #85177

Merged
merged 17 commits into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions src/mono/mono/component/hot_reload-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ struct _MonoClassMetadataUpdateInfo {
GSList *added_events; /* a set of MonoClassMetadataUpdateEvent* values */

MonoClassRuntimeMetadataUpdateInfo runtime;

uint32_t generation; /* must be updated when a GTD gets added props, events or fields; must be updated when a GINST copies updated info from the parent */
};

/*
Expand Down
147 changes: 137 additions & 10 deletions src/mono/mono/component/hot_reload.c
Original file line number Diff line number Diff line change
Expand Up @@ -3318,6 +3318,8 @@ hot_reload_get_static_field_addr (MonoClassField *field)
static MonoMethod *
hot_reload_find_method_by_name (MonoClass *klass, const char *name, int param_count, int flags, MonoError *error)
{
g_assert (!mono_class_is_ginst (klass));

GSList *members = hot_reload_get_added_members (klass);
if (!members)
return NULL;
Expand Down Expand Up @@ -3366,13 +3368,19 @@ hot_reload_added_methods_iter (MonoClass *klass, gpointer *iter)
uint32_t idx = GPOINTER_TO_UINT (*iter);
g_assert (idx >= mono_class_get_method_count (klass));

GSList *members = hot_reload_get_added_members (klass);
GSList *members = NULL;
int class_kind = m_class_get_class_kind (klass);
if (class_kind == MONO_CLASS_GINST) {
MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
members = hot_reload_get_added_members (gklass);
} else {
members = hot_reload_get_added_members (klass);
}

if (!members)
return NULL;
// expect to only see class defs here. Rationale: adding methods to generic classes is not
// allowed (if a generation adds a new generic class, it won't be here - those methods will
// be in the normal iteration code, not here.
g_assert (m_class_get_class_kind (klass) == MONO_CLASS_DEF);
// We don't expect to see arrays or pointers here since they don't own MONO_TABLE_METHOD entries.
g_assert (class_kind == MONO_CLASS_DEF || class_kind == MONO_CLASS_GTD || class_kind == MONO_CLASS_GINST);

mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Iterating added methods of 0x%08x idx = %u", m_class_get_type_token (klass), idx);

Expand All @@ -3386,9 +3394,18 @@ hot_reload_added_methods_iter (MonoClass *klass, gpointer *iter)
if (cur_count == idx) {
// found a method, advance iter and return the method.
*iter = GUINT_TO_POINTER (1+idx);
ERROR_DECL (local_error);
MonoMethod *method = mono_get_method_checked (m_class_get_image (klass), token, klass, NULL, local_error);
mono_error_cleanup (local_error);
MonoMethod *method = NULL;
if (class_kind == MONO_CLASS_GINST) {
MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
ERROR_DECL (local_error);
MonoMethod *gmethod = mono_get_method_checked (m_class_get_image (gklass), token, klass, NULL, local_error);
method = mono_class_inflate_generic_method_full_checked (gmethod, klass, mono_class_get_context (klass), local_error);
mono_error_cleanup (local_error);
} else {
ERROR_DECL (local_error);
method = mono_get_method_checked (m_class_get_image (klass), token, klass, NULL, local_error);
mono_error_cleanup (local_error);
}
return method;
}
cur_count++;
Expand Down Expand Up @@ -3477,7 +3494,7 @@ hot_reload_get_method_params (MonoImage *base_image, uint32_t methoddef_token, u
static const char *
hot_reload_get_capabilities (void)
{
return "Baseline AddMethodToExistingType AddStaticFieldToExistingType NewTypeDefinition ChangeCustomAttributes AddInstanceFieldToExistingType";
return "Baseline AddMethodToExistingType AddStaticFieldToExistingType NewTypeDefinition ChangeCustomAttributes AddInstanceFieldToExistingType GenericAddMethodToExistingType GenericUpdateMethod";
}

static GENERATE_GET_CLASS_WITH_CACHE_DECL (hot_reload_instance_field_table);
Expand Down Expand Up @@ -3515,10 +3532,120 @@ hot_reload_added_field_ldflda (MonoObject *instance, MonoType *field_type, uint3
return result;
}

static void recompute_ginst_update_info(MonoClass *ginst, MonoClass *gtd, MonoClassMetadataUpdateInfo *gtd_info);

static MonoClassMetadataUpdateInfo *
hot_reload_get_or_add_ginst_update_info(MonoClass *ginst)
{
g_assert (m_class_get_class_kind (ginst) == MONO_CLASS_GINST);
MonoClassMetadataUpdateInfo *info = mono_class_get_metadata_update_info (ginst);

gboolean needToRefresh = FALSE;

if (!info)
needToRefresh = TRUE;

MonoClass *gtd = mono_class_get_generic_type_definition (ginst);
MonoClassMetadataUpdateInfo *gtd_info = mono_class_get_metadata_update_info (gtd);

if (!gtd_info)
return NULL;

if (info && info->generation < gtd_info->generation)
needToRefresh = TRUE;

if (!needToRefresh)
return info;

mono_loader_lock ();
info = mono_class_get_metadata_update_info (ginst);
if (info && info->generation == gtd_info->generation) {
mono_loader_unlock ();
return info;
}

recompute_ginst_update_info (ginst, gtd, gtd_info);

info = mono_class_get_metadata_update_info (ginst);
mono_loader_unlock ();
return info;
}

#define mono_class_new0(klass,struct_type, n_structs) \
((struct_type *) mono_class_alloc0 ((klass), ((gsize) sizeof (struct_type)) * ((gsize) (n_structs))))

static void
recompute_ginst_update_info(MonoClass *ginst, MonoClass *gtd, MonoClassMetadataUpdateInfo *gtd_info)
{
// if ginst has a `MonoClassMetadataUpdateInfo`, use it to start with, otherwise, allocate a new one
MonoClassMetadataUpdateInfo *info = mono_class_get_or_add_metadata_update_info (ginst);

if (!info)
info = mono_class_new0 (ginst, MonoClassMetadataUpdateInfo, 1);

// replace info->added_props by a new list re-computed from gtd_info->added_props
fanyang-mono marked this conversation as resolved.
Show resolved Hide resolved
info->added_props = NULL;
for (GSList *ptr = gtd_info->added_props; ptr; ptr = ptr->next) {
MonoClassMetadataUpdateProperty *gtd_added_prop = (MonoClassMetadataUpdateProperty *)ptr->data;
MonoClassMetadataUpdateProperty *added_prop = mono_class_new0 (ginst, MonoClassMetadataUpdateProperty, 1);

added_prop->prop = gtd_added_prop->prop;
added_prop->token = gtd_added_prop->token;

ERROR_DECL (error);
if (added_prop->prop.get)
added_prop->prop.get = mono_class_inflate_generic_method_full_checked (
added_prop->prop.get, ginst, mono_class_get_context (ginst), error);
if (added_prop->prop.set)
added_prop->prop.set = mono_class_inflate_generic_method_full_checked (
added_prop->prop.set, ginst, mono_class_get_context (ginst), error);
g_assert (is_ok (error)); /*FIXME proper error handling*/

added_prop->prop.parent = ginst;

info->added_props = g_slist_prepend_mem_manager (m_class_get_mem_manager (ginst), info->added_props, (gpointer)added_prop);
}

// replace info->added_events by a new list re-computed from gtd_info->added_events
fanyang-mono marked this conversation as resolved.
Show resolved Hide resolved
info->added_events = NULL;
for (GSList *ptr = gtd_info->added_events; ptr; ptr = ptr->next) {
MonoClassMetadataUpdateEvent *gtd_added_event = (MonoClassMetadataUpdateEvent *)ptr->data;
MonoClassMetadataUpdateEvent *added_event = mono_class_new0 (ginst, MonoClassMetadataUpdateEvent, 1);

added_event->evt = gtd_added_event->evt;

ERROR_DECL (error);
if (added_event->evt.add)
added_event->evt.add = mono_class_inflate_generic_method_full_checked (
added_event->evt.add, ginst, mono_class_get_context (ginst), error);
if (added_event->evt.remove)
added_event->evt.remove = mono_class_inflate_generic_method_full_checked (
added_event->evt.remove, ginst, mono_class_get_context (ginst), error);
if (added_event->evt.raise)
added_event->evt.raise = mono_class_inflate_generic_method_full_checked (
added_event->evt.raise, ginst, mono_class_get_context (ginst), error);
g_assert (is_ok (error)); /*FIXME proper error handling*/

added_event->evt.parent = ginst;

info->added_events = g_slist_prepend_mem_manager (m_class_get_mem_manager (ginst), info->added_events, (gpointer)added_event);
}

// finally, update the generation of the ginst info to the same one as the gtd
info->generation = gtd_info->generation;
// we're done info is now up to date
}

static MonoProperty *
hot_reload_added_properties_iter (MonoClass *klass, gpointer *iter)
{
MonoClassMetadataUpdateInfo *info = mono_class_get_metadata_update_info (klass);
MonoClassMetadataUpdateInfo *info;
if (mono_class_is_ginst (klass)) {
info = hot_reload_get_or_add_ginst_update_info (klass);
} else {
info = mono_class_get_metadata_update_info (klass);
}

if (!info)
return NULL;

Expand Down
14 changes: 7 additions & 7 deletions src/mono/mono/metadata/class-accessors.c
Original file line number Diff line number Diff line change
Expand Up @@ -620,11 +620,10 @@ MonoClassMetadataUpdateInfo*
mono_class_get_metadata_update_info (MonoClass *klass)
{
switch (m_class_get_class_kind (klass)) {
case MONO_CLASS_GTD:
return NULL;
case MONO_CLASS_DEF:
return (MonoClassMetadataUpdateInfo *)get_pointer_property (klass, PROP_METADATA_UPDATE_INFO);
case MONO_CLASS_GTD:
case MONO_CLASS_GINST:
return (MonoClassMetadataUpdateInfo *)get_pointer_property (klass, PROP_METADATA_UPDATE_INFO);
case MONO_CLASS_GPARAM:
case MONO_CLASS_ARRAY:
case MONO_CLASS_POINTER:
Expand All @@ -643,13 +642,13 @@ mono_class_set_metadata_update_info (MonoClass *klass, MonoClassMetadataUpdateIn
{
switch (m_class_get_class_kind (klass)) {
case MONO_CLASS_GTD:
g_assertf (0, "%s: EnC metadata update info on generic types is not supported", __func__);
break;
case MONO_CLASS_DEF:
case MONO_CLASS_GINST:
set_pointer_property (klass, PROP_METADATA_UPDATE_INFO, value);
return;
case MONO_CLASS_GINST:
case MONO_CLASS_GPARAM:
/* metadata-update: this shouldn't happen */
g_assert_not_reached();
case MONO_CLASS_POINTER:
case MONO_CLASS_GC_FILLER:
g_assert_not_reached ();
Expand All @@ -664,11 +663,12 @@ mono_class_has_metadata_update_info (MonoClass *klass)
{
switch (m_class_get_class_kind (klass)) {
case MONO_CLASS_GTD:
return FALSE;
case MONO_CLASS_DEF:
return get_pointer_property (klass, PROP_METADATA_UPDATE_INFO) != NULL;
case MONO_CLASS_GINST:
case MONO_CLASS_GPARAM:
/* metadata-update: this shouldn't happen */
g_assert_not_reached();
case MONO_CLASS_POINTER:
case MONO_CLASS_GC_FILLER:
return FALSE;
Expand Down
2 changes: 1 addition & 1 deletion src/mono/mono/metadata/class-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -1142,7 +1142,7 @@ mono_method_can_access_field_full (MonoMethod *method, MonoClassField *field, Mo
gboolean
mono_class_can_access_class (MonoClass *access_class, MonoClass *target_class);

MonoClass *
MONO_COMPONENT_API MonoClass *
mono_class_get_generic_type_definition (MonoClass *klass);

gboolean
Expand Down
2 changes: 1 addition & 1 deletion src/mono/mono/metadata/class.c
Original file line number Diff line number Diff line change
Expand Up @@ -5913,7 +5913,7 @@ mono_class_get_method_from_name_checked (MonoClass *klass, const char *name,

mono_class_init_internal (klass);

if (mono_class_is_ginst (klass) && !m_class_get_methods (klass)) {
if (mono_class_is_ginst (klass) && (!m_class_get_methods (klass) || m_class_get_image (klass)->has_updates)) {
res = mono_class_get_method_from_name_checked (mono_class_get_generic_class (klass)->container_class, name, param_count, flags, error);

if (res)
Expand Down
38 changes: 31 additions & 7 deletions src/mono/mono/metadata/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -486,9 +486,16 @@ find_method_in_class (MonoClass *klass, const char *name, const char *qname, con
return NULL;
}
int mcount = mono_class_get_method_count (klass);
MonoMethod **klass_methods = m_class_get_methods (klass);
for (i = 0; i < mcount; ++i) {
MonoMethod *m = klass_methods [i];
i = -1;
gpointer iter = NULL;
MonoMethod *m = NULL;
gboolean matched = FALSE;
/* FIXME: metadata-update iterating using
* mono_class_get_methods will break if `m` is NULL. Need to
* reconcile with the `if (!m)` "we must cope" comment below.
*/
while ((m = mono_class_get_methods (klass, &iter))) {
++i;
MonoMethodSignature *msig;

/* We must cope with failing to load some of the types. */
Expand All @@ -507,16 +514,33 @@ find_method_in_class (MonoClass *klass, const char *name, const char *qname, con
continue;

if (sig->call_convention == MONO_CALL_VARARG) {
if (mono_metadata_signature_vararg_match (sig, msig))
if (mono_metadata_signature_vararg_match (sig, msig)) {
matched = TRUE;
break;
}
} else {
if (mono_metadata_signature_equal (sig, msig))
if (mono_metadata_signature_equal (sig, msig)) {
matched = TRUE;
break;
}
}
}

if (i < mcount)
return mono_class_get_method_by_index (from_class, i);
if (matched) {
if (i < mcount)
return mono_class_get_method_by_index (from_class, i);
else if (m != NULL) {
// FIXME: metadata-update: hack
// it's from a metadata-update, probably
m = mono_class_inflate_generic_method_full_checked (
m, from_class, mono_class_get_context (from_class), error);
mono_error_assert_ok (error);
g_assert (m != NULL);
g_assert (m->klass == from_class);
g_assert (m->is_inflated);
return m;
}
}
return NULL;
}

Expand Down