-
Notifications
You must be signed in to change notification settings - Fork 3.8k
[runtime] Add support for weak fields. #5972
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
Changes from all commits
f679fc0
aec5efb
cb72e05
3fbd420
c0ee75b
009eee9
562d878
e56c86f
42bab3b
543b3e2
5f8a2dd
1bb9d9b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| +5 −0 | profiles/monodroid/mscorlib.cs | |
| +5 −0 | profiles/monotouch/mscorlib.cs |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| using System; | ||
|
|
||
| #if MOBILE | ||
| namespace System { | ||
|
|
||
| [AttributeUsage(AttributeTargets.Field)] | ||
| public sealed class WeakAttribute : Attribute | ||
| { | ||
| } | ||
|
|
||
| } | ||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,7 +16,8 @@ typedef enum { | |
| PROP_PROPERTY_INFO = 5, /* MonoClassPropertyInfo* */ | ||
| PROP_EVENT_INFO = 6, /* MonoClassEventInfo* */ | ||
| PROP_FIELD_DEF_VALUES = 7, /* MonoFieldDefaultValue* */ | ||
| PROP_DECLSEC_FLAGS = 8 /* guint32 */ | ||
| PROP_DECLSEC_FLAGS = 8, /* guint32 */ | ||
| PROP_WEAK_BITMAP = 9 | ||
| } InfrequentDataKind; | ||
|
|
||
| /* Accessors based on class kind*/ | ||
|
|
@@ -390,3 +391,31 @@ mono_class_gtd_get_canonical_inst (MonoClass *klass) | |
| g_assert (mono_class_is_gtd (klass)); | ||
| return &((MonoClassGtd*)klass)->canonical_inst; | ||
| } | ||
|
|
||
| typedef struct { | ||
| MonoPropertyBagItem head; | ||
|
|
||
| int nbits; | ||
| gsize *bits; | ||
| } WeakBitmapData; | ||
|
|
||
| void | ||
| mono_class_set_weak_bitmap (MonoClass *klass, int nbits, gsize *bits) | ||
| { | ||
| WeakBitmapData *info = mono_class_alloc (klass, sizeof (WeakBitmapData)); | ||
| info->nbits = nbits; | ||
| info->bits = bits; | ||
|
||
|
|
||
| info->head.tag = PROP_WEAK_BITMAP; | ||
| mono_property_bag_add (&klass->infrequent_data, info); | ||
| } | ||
|
|
||
| gsize* | ||
| mono_class_get_weak_bitmap (MonoClass *klass, int *nbits) | ||
| { | ||
| WeakBitmapData *prop = mono_property_bag_get (&klass->infrequent_data, PROP_WEAK_BITMAP); | ||
|
|
||
| g_assert (prop); | ||
| *nbits = prop->nbits; | ||
| return prop->bits; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,6 +40,7 @@ | |
| #include <mono/metadata/gc-internals.h> | ||
| #include <mono/metadata/verify-internals.h> | ||
| #include <mono/metadata/mono-debug.h> | ||
| #include <mono/metadata/custom-attrs-internals.h> | ||
| #include <mono/utils/mono-counters.h> | ||
| #include <mono/utils/mono-string.h> | ||
| #include <mono/utils/mono-error-internals.h> | ||
|
|
@@ -1666,6 +1667,7 @@ init_sizes_with_info (MonoClass *klass, MonoCachedClassInfo *cached_info) | |
| klass->has_references = cached_info->has_references; | ||
| klass->has_static_refs = cached_info->has_static_refs; | ||
| klass->no_special_static_fields = cached_info->no_special_static_fields; | ||
| klass->has_weak_fields = cached_info->has_weak_fields; | ||
| mono_loader_unlock (); | ||
| } | ||
| else { | ||
|
|
@@ -2204,18 +2206,40 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ | |
| mono_class_set_type_load_failure (klass, "Value type instance size (%d) cannot be zero, negative, or bigger than 1Mb", klass->instance_size); | ||
| } | ||
|
|
||
| // Weak field support | ||
| // | ||
| // FIXME: | ||
| // - generic instances | ||
| // - Disallow on structs/static fields/nonref fields | ||
| gboolean has_weak_fields = FALSE; | ||
|
|
||
| if (mono_class_has_static_metadata (klass)) { | ||
| for (MonoClass *p = klass; p != NULL; p = p->parent) { | ||
| gpointer iter = NULL; | ||
| guint32 first_field_idx = mono_class_get_first_field_idx (p); | ||
|
|
||
| while ((field = mono_class_get_fields (p, &iter))) { | ||
| guint32 field_idx = first_field_idx + (field - p->fields); | ||
| if (MONO_TYPE_IS_REFERENCE (field->type) && mono_assembly_is_weak_field (p->image, field_idx + 1)) { | ||
| has_weak_fields = TRUE; | ||
| mono_trace_message (MONO_TRACE_TYPE, "Field %s:%s at offset %x is weak.", field->parent->name, field->name, field->offset); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does it have to keep scanning?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A class can have multiple weak fields.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure but we are looking only for the first one, right?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are also printing out the trace message. Since these fields are not common, I don't think continuing the loop is a problem.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's still the issue that we're iterating over the parents multiple times. Plus, this breaks SRE if it has a parent with a Weak field. |
||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /* Publish the data */ | ||
| mono_loader_lock (); | ||
| if (!klass->rank) | ||
| klass->sizes.class_size = class_size; | ||
| klass->has_static_refs = has_static_refs; | ||
| klass->has_weak_fields = has_weak_fields; | ||
| for (i = 0; i < top; ++i) { | ||
| field = &klass->fields [i]; | ||
|
|
||
| if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) | ||
| field->offset = field_offsets [i]; | ||
| } | ||
|
|
||
| mono_memory_barrier (); | ||
| klass->fields_inited = 1; | ||
| mono_loader_unlock (); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2155,3 +2155,181 @@ mono_assembly_metadata_foreach_custom_attr (MonoAssembly *assembly, MonoAssembly | |
| stop_iterating = func (image, assembly_token, nspace, name, mtoken, user_data); | ||
| } | ||
| } | ||
|
|
||
| static void | ||
| init_weak_fields_inner (MonoImage *image, GHashTable *indexes) | ||
| { | ||
| MonoTableInfo *tdef; | ||
| MonoError error; | ||
| MonoClass *klass = NULL; | ||
| guint32 memberref_index = -1; | ||
| int first_method_idx = -1; | ||
| int method_count = -1; | ||
|
|
||
| if (image == mono_get_corlib ()) { | ||
| /* Typedef */ | ||
| klass = mono_class_from_name_checked (image, "System", "WeakAttribute", &error); | ||
| if (!is_ok (&error)) { | ||
| mono_error_cleanup (&error); | ||
| return; | ||
| } | ||
| if (!klass) | ||
| return; | ||
| first_method_idx = mono_class_get_first_method_idx (klass); | ||
| method_count = mono_class_get_method_count (klass); | ||
|
|
||
| tdef = &image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; | ||
| guint32 parent, field_idx, col, mtoken, idx; | ||
| for (int i = 0; i < tdef->rows; ++i) { | ||
| parent = mono_metadata_decode_row_col (tdef, i, MONO_CUSTOM_ATTR_PARENT); | ||
| if ((parent & MONO_CUSTOM_ATTR_MASK) != MONO_CUSTOM_ATTR_FIELDDEF) | ||
| continue; | ||
|
|
||
| col = mono_metadata_decode_row_col (tdef, i, MONO_CUSTOM_ATTR_TYPE); | ||
| mtoken = col >> MONO_CUSTOM_ATTR_TYPE_BITS; | ||
| /* 1 based index */ | ||
| idx = mtoken - 1; | ||
| if ((col & MONO_CUSTOM_ATTR_TYPE_MASK) == MONO_CUSTOM_ATTR_TYPE_METHODDEF) { | ||
| field_idx = parent >> MONO_CUSTOM_ATTR_BITS; | ||
| if (idx >= first_method_idx && idx < first_method_idx + method_count) | ||
| g_hash_table_insert (indexes, GUINT_TO_POINTER (field_idx), GUINT_TO_POINTER (1)); | ||
| } | ||
| } | ||
| } else { | ||
| /* Memberref pointing to a typeref */ | ||
| tdef = &image->tables [MONO_TABLE_MEMBERREF]; | ||
|
||
|
|
||
| /* Check whenever the assembly references the WeakAttribute type */ | ||
| gboolean found = FALSE; | ||
| tdef = &image->tables [MONO_TABLE_TYPEREF]; | ||
| for (int i = 0; i < tdef->rows; ++i) { | ||
| guint32 string_offset = mono_metadata_decode_row_col (tdef, i, MONO_TYPEREF_NAME); | ||
| const char *name = mono_metadata_string_heap (image, string_offset); | ||
| if (!strcmp (name, "WeakAttribute")) { | ||
| found = TRUE; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (!found) | ||
| return; | ||
|
|
||
| /* Find the memberref pointing to a typeref */ | ||
| tdef = &image->tables [MONO_TABLE_MEMBERREF]; | ||
| for (int i = 0; i < tdef->rows; ++i) { | ||
| guint32 cols [MONO_MEMBERREF_SIZE]; | ||
| const char *sig; | ||
|
|
||
| mono_metadata_decode_row (tdef, i, cols, MONO_MEMBERREF_SIZE); | ||
| sig = mono_metadata_blob_heap (image, cols [MONO_MEMBERREF_SIGNATURE]); | ||
| mono_metadata_decode_blob_size (sig, &sig); | ||
|
|
||
| guint32 nindex = cols [MONO_MEMBERREF_CLASS] >> MONO_MEMBERREF_PARENT_BITS; | ||
| guint32 class_index = cols [MONO_MEMBERREF_CLASS] & MONO_MEMBERREF_PARENT_MASK; | ||
| const char *fname = mono_metadata_string_heap (image, cols [MONO_MEMBERREF_NAME]); | ||
|
|
||
| if (!strcmp (fname, ".ctor") && class_index == MONO_MEMBERREF_PARENT_TYPEREF) { | ||
| MonoTableInfo *typeref_table = &image->tables [MONO_TABLE_TYPEREF]; | ||
| guint32 cols [MONO_TYPEREF_SIZE]; | ||
|
|
||
| mono_metadata_decode_row (typeref_table, nindex - 1, cols, MONO_TYPEREF_SIZE); | ||
|
|
||
| const char *name = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAME]); | ||
| const char *nspace = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAMESPACE]); | ||
|
|
||
| if (!strcmp (nspace, "System") && !strcmp (name, "WeakAttribute")) { | ||
| MonoClass *klass = mono_class_from_typeref (image, MONO_TOKEN_TYPE_REF | nindex); | ||
| g_assert (!strcmp (klass->name, "WeakAttribute")); | ||
| /* Allow a testing dll as well since some profiles don't have WeakAttribute */ | ||
| if (klass && (klass->image == mono_get_corlib () || strstr (klass->image->name, "Mono.Runtime.Testing"))) { | ||
| /* Sanity check that it only has 1 ctor */ | ||
| gpointer iter = NULL; | ||
| int count = 0; | ||
| MonoMethod *method; | ||
| while ((method = mono_class_get_methods (klass, &iter))) { | ||
| if (!strcmp (method->name, ".ctor")) | ||
| count ++; | ||
| } | ||
| count ++; | ||
| memberref_index = i; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| if (memberref_index == -1) | ||
|
||
| return; | ||
|
|
||
| tdef = &image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; | ||
| guint32 parent, field_idx, col, mtoken, idx; | ||
| for (int i = 0; i < tdef->rows; ++i) { | ||
| parent = mono_metadata_decode_row_col (tdef, i, MONO_CUSTOM_ATTR_PARENT); | ||
| if ((parent & MONO_CUSTOM_ATTR_MASK) != MONO_CUSTOM_ATTR_FIELDDEF) | ||
| continue; | ||
|
|
||
| col = mono_metadata_decode_row_col (tdef, i, MONO_CUSTOM_ATTR_TYPE); | ||
| mtoken = col >> MONO_CUSTOM_ATTR_TYPE_BITS; | ||
| /* 1 based index */ | ||
| idx = mtoken - 1; | ||
| field_idx = parent >> MONO_CUSTOM_ATTR_BITS; | ||
| if ((col & MONO_CUSTOM_ATTR_TYPE_MASK) == MONO_CUSTOM_ATTR_TYPE_MEMBERREF) { | ||
| if (idx == memberref_index) | ||
| g_hash_table_insert (indexes, GUINT_TO_POINTER (field_idx), GUINT_TO_POINTER (1)); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /* | ||
| * mono_assembly_init_weak_fields: | ||
| * | ||
| * Initialize the image->weak_field_indexes hash. | ||
| */ | ||
| void | ||
| mono_assembly_init_weak_fields (MonoImage *image) | ||
| { | ||
| if (image->weak_fields_inited) | ||
| return; | ||
|
|
||
| GHashTable *indexes = NULL; | ||
|
|
||
| if (mono_get_runtime_callbacks ()->get_weak_field_indexes) | ||
| indexes = mono_get_runtime_callbacks ()->get_weak_field_indexes (image); | ||
| if (!indexes) { | ||
| indexes = g_hash_table_new (NULL, NULL); | ||
|
|
||
| /* | ||
| * To avoid lookups for every field, we scan the customattr table for entries whose | ||
| * parent is a field and whose type is WeakAttribute. | ||
| */ | ||
| init_weak_fields_inner (image, indexes); | ||
| } | ||
|
|
||
| mono_image_lock (image); | ||
| if (!image->weak_fields_inited) { | ||
| image->weak_field_indexes = indexes; | ||
| mono_memory_barrier (); | ||
| image->weak_fields_inited = TRUE; | ||
| } else { | ||
| g_hash_table_destroy (indexes); | ||
| } | ||
| mono_image_unlock (image); | ||
| } | ||
|
|
||
| /* | ||
| * mono_assembly_is_weak_field: | ||
| * | ||
| * Return whenever the FIELD table entry with the 1-based index FIELD_IDX has | ||
| * a [Weak] attribute. | ||
| */ | ||
| gboolean | ||
| mono_assembly_is_weak_field (MonoImage *image, guint32 field_idx) | ||
| { | ||
| if (image->dynamic) | ||
| return FALSE; | ||
|
|
||
| mono_assembly_init_weak_fields (image); | ||
|
|
||
| /* The hash is not mutated, no need to lock */ | ||
| return g_hash_table_lookup (image->weak_field_indexes, GINT_TO_POINTER (field_idx)) != NULL; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think the name is very common, this can be easily breaking change due to ambiguity. We should rename it to
WeakReferenceAttributeThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might also be useful to add sample into this file how it's intended to be used