2 * generic-sharing.c: Support functions for generic sharing.
5 * Mark Probst (mark.probst@gmail.com)
7 * Copyright 2007-2009 Novell, Inc (http://www.novell.com)
16 #include <mono/utils/mono-membar.h>
17 #include <mono/utils/mono-counters.h>
19 #include "metadata-internals.h"
21 #include "class-internals.h"
23 #include "debug-helpers.h"
24 #include "tabledefs.h"
25 #include "mempool-internals.h"
28 type_check_context_used (MonoType *type, gboolean recursive)
30 switch (mono_type_get_type (type)) {
32 return MONO_GENERIC_CONTEXT_USED_CLASS;
34 return MONO_GENERIC_CONTEXT_USED_METHOD;
35 case MONO_TYPE_SZARRAY:
36 return mono_class_check_context_used (mono_type_get_class (type));
38 return mono_class_check_context_used (mono_type_get_array_type (type)->eklass);
41 return mono_class_check_context_used (mono_type_get_class (type));
44 case MONO_TYPE_GENERICINST:
46 MonoGenericClass *gclass = type->data.generic_class;
48 g_assert (gclass->container_class->generic_container);
49 return mono_generic_context_check_used (&gclass->context);
59 inst_check_context_used (MonoGenericInst *inst)
67 for (i = 0; i < inst->type_argc; ++i)
68 context_used |= type_check_context_used (inst->type_argv [i], TRUE);
74 * mono_generic_context_check_used:
75 * @context: a generic context
77 * Checks whether the context uses a type variable. Returns an int
78 * with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to reflect whether
79 * the context's class instantiation uses type variables.
82 mono_generic_context_check_used (MonoGenericContext *context)
86 context_used |= inst_check_context_used (context->class_inst);
87 context_used |= inst_check_context_used (context->method_inst);
93 * mono_class_check_context_used:
96 * Checks whether the class's generic context uses a type variable.
97 * Returns an int with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to
98 * reflect whether the context's class instantiation uses type
102 mono_class_check_context_used (MonoClass *class)
104 int context_used = 0;
106 context_used |= type_check_context_used (&class->this_arg, FALSE);
107 context_used |= type_check_context_used (&class->byval_arg, FALSE);
109 if (class->generic_class)
110 context_used |= mono_generic_context_check_used (&class->generic_class->context);
111 else if (class->generic_container)
112 context_used |= mono_generic_context_check_used (&class->generic_container->context);
118 * Guards the two global rgctx (template) hash tables and all rgctx
121 * Ordering: The loader lock can be taken while the templates lock is
124 static CRITICAL_SECTION templates_mutex;
127 templates_lock (void)
129 static gboolean inited = FALSE;
134 InitializeCriticalSection (&templates_mutex);
137 mono_loader_unlock ();
140 EnterCriticalSection (&templates_mutex);
144 templates_unlock (void)
146 LeaveCriticalSection (&templates_mutex);
150 * LOCKING: templates lock
152 static MonoRuntimeGenericContextOtherInfoTemplate*
153 get_other_info_templates (MonoRuntimeGenericContextTemplate *template, int type_argc)
155 g_assert (type_argc >= 0);
157 return template->other_infos;
158 return g_slist_nth_data (template->method_templates, type_argc - 1);
162 * LOCKING: templates lock
165 set_other_info_templates (MonoMemPool *mp, MonoRuntimeGenericContextTemplate *template, int type_argc,
166 MonoRuntimeGenericContextOtherInfoTemplate *oti)
168 g_assert (type_argc >= 0);
170 template->other_infos = oti;
172 int length = g_slist_length (template->method_templates);
175 /* FIXME: quadratic! */
176 while (length < type_argc) {
177 template->method_templates = g_slist_append_mempool (mp, template->method_templates, NULL);
181 list = g_slist_nth (template->method_templates, type_argc - 1);
188 * LOCKING: templates lock
191 template_get_max_argc (MonoRuntimeGenericContextTemplate *template)
193 return g_slist_length (template->method_templates);
197 * LOCKING: templates lock
199 static MonoRuntimeGenericContextOtherInfoTemplate*
200 rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate *template, int type_argc, int slot)
203 MonoRuntimeGenericContextOtherInfoTemplate *oti;
205 g_assert (slot >= 0);
207 for (oti = get_other_info_templates (template, type_argc), i = 0; i < slot; oti = oti->next, ++i) {
216 * LOCKING: templates lock
219 rgctx_template_num_other_infos (MonoRuntimeGenericContextTemplate *template, int type_argc)
221 MonoRuntimeGenericContextOtherInfoTemplate *oti;
224 for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next)
230 /* Maps from uninstantiated generic classes to GList's of
231 * uninstantiated generic classes whose parent is the key class or an
232 * instance of the key class.
234 * LOCKING: templates lock
236 static GHashTable *generic_subclass_hash;
239 * LOCKING: templates lock
242 class_set_rgctx_template (MonoClass *class, MonoRuntimeGenericContextTemplate *rgctx_template)
244 if (!class->image->rgctx_template_hash)
245 class->image->rgctx_template_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
247 g_hash_table_insert (class->image->rgctx_template_hash, class, rgctx_template);
251 * LOCKING: templates lock
253 static MonoRuntimeGenericContextTemplate*
254 class_lookup_rgctx_template (MonoClass *class)
256 MonoRuntimeGenericContextTemplate *template;
258 if (!class->image->rgctx_template_hash)
261 template = g_hash_table_lookup (class->image->rgctx_template_hash, class);
267 * LOCKING: templates lock
270 register_generic_subclass (MonoClass *class)
272 MonoClass *parent = class->parent;
274 MonoRuntimeGenericContextTemplate *rgctx_template = class_lookup_rgctx_template (class);
276 g_assert (rgctx_template);
278 if (parent->generic_class)
279 parent = parent->generic_class->container_class;
281 if (!generic_subclass_hash)
282 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
284 subclass = g_hash_table_lookup (generic_subclass_hash, parent);
285 rgctx_template->next_subclass = subclass;
286 g_hash_table_insert (generic_subclass_hash, parent, class);
290 move_subclasses_not_in_image_foreach_func (MonoClass *class, MonoClass *subclass, MonoImage *image)
294 if (class->image == image) {
295 /* The parent class itself is in the image, so all the
296 subclasses must be in the image, too. If not,
297 we're removing an image containing a class which
298 still has a subclass in another image. */
301 g_assert (subclass->image == image);
302 subclass = class_lookup_rgctx_template (subclass)->next_subclass;
310 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
311 MonoClass *next = subclass_template->next_subclass;
313 if (subclass->image != image) {
314 subclass_template->next_subclass = new_list;
322 g_hash_table_insert (generic_subclass_hash, class, new_list);
326 * mono_class_unregister_image_generic_subclasses:
329 * Removes all classes of the image from the generic subclass hash.
330 * Must be called when an image is unloaded.
333 mono_class_unregister_image_generic_subclasses (MonoImage *image)
335 GHashTable *old_hash;
337 //g_print ("unregistering image %s\n", image->name);
339 if (!generic_subclass_hash)
344 old_hash = generic_subclass_hash;
345 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
347 g_hash_table_foreach (old_hash, (GHFunc)move_subclasses_not_in_image_foreach_func, image);
351 g_hash_table_destroy (old_hash);
355 * LOCKING: loader lock
357 static MonoRuntimeGenericContextTemplate*
358 alloc_template (MonoClass *class)
360 static gboolean inited = FALSE;
361 static int num_allocted = 0;
362 static int num_bytes = 0;
364 int size = sizeof (MonoRuntimeGenericContextTemplate);
367 mono_counters_register ("RGCTX template num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_allocted);
368 mono_counters_register ("RGCTX template bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_bytes);
375 return mono_image_alloc0 (class->image, size);
379 * LOCKING: loader lock
381 static MonoRuntimeGenericContextOtherInfoTemplate*
382 alloc_oti (MonoImage *image)
384 static gboolean inited = FALSE;
385 static int num_allocted = 0;
386 static int num_bytes = 0;
388 int size = sizeof (MonoRuntimeGenericContextOtherInfoTemplate);
391 mono_counters_register ("RGCTX oti num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_allocted);
392 mono_counters_register ("RGCTX oti bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_bytes);
399 return mono_image_alloc0 (image, size);
402 #define MONO_RGCTX_SLOT_USED_MARKER ((gpointer)&mono_defaults.object_class->byval_arg)
405 * LOCKING: templates lock
408 rgctx_template_set_other_slot (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
409 int slot, gpointer data, int info_type)
411 static gboolean inited = FALSE;
412 static int num_markers = 0;
413 static int num_data = 0;
416 MonoRuntimeGenericContextOtherInfoTemplate *list = get_other_info_templates (template, type_argc);
417 MonoRuntimeGenericContextOtherInfoTemplate **oti = &list;
420 mono_counters_register ("RGCTX oti num markers", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_markers);
421 mono_counters_register ("RGCTX oti num data", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_data);
425 g_assert (slot >= 0);
435 *oti = alloc_oti (image);
439 mono_loader_unlock ();
441 g_assert (!(*oti)->data);
443 (*oti)->info_type = info_type;
445 set_other_info_templates (image->mempool, template, type_argc, list);
447 if (data == MONO_RGCTX_SLOT_USED_MARKER)
454 * mono_method_get_declaring_generic_method:
455 * @method: an inflated method
457 * Returns an inflated method's declaring method.
460 mono_method_get_declaring_generic_method (MonoMethod *method)
462 MonoMethodInflated *inflated;
464 g_assert (method->is_inflated);
466 inflated = (MonoMethodInflated*)method;
468 return inflated->declaring;
472 * mono_class_get_method_generic:
476 * Given a class and a generic method, which has to be of an
477 * instantiation of the same class that klass is an instantiation of,
478 * returns the corresponding method in klass. Example:
480 * klass is Gen<string>
481 * method is Gen<object>.work<int>
483 * returns: Gen<string>.work<int>
486 mono_class_get_method_generic (MonoClass *klass, MonoMethod *method)
488 MonoMethod *declaring, *m;
491 if (method->is_inflated)
492 declaring = mono_method_get_declaring_generic_method (method);
497 if (klass->generic_class)
498 m = mono_class_get_inflated_method (klass, declaring);
501 mono_class_setup_methods (klass);
502 for (i = 0; i < klass->method.count; ++i) {
503 m = klass->methods [i];
506 if (m->is_inflated && mono_method_get_declaring_generic_method (m) == declaring)
509 if (i >= klass->method.count)
513 if (method != declaring) {
514 MonoGenericContext context;
516 context.class_inst = NULL;
517 context.method_inst = mono_method_get_context (method)->method_inst;
519 m = mono_class_inflate_generic_method (m, &context);
526 inflate_other_data (gpointer data, int info_type, MonoGenericContext *context, MonoClass *class, gboolean temporary)
530 if (data == MONO_RGCTX_SLOT_USED_MARKER)
531 return MONO_RGCTX_SLOT_USED_MARKER;
535 case MONO_RGCTX_INFO_STATIC_DATA:
536 case MONO_RGCTX_INFO_KLASS:
537 case MONO_RGCTX_INFO_VTABLE:
538 case MONO_RGCTX_INFO_TYPE:
539 case MONO_RGCTX_INFO_REFLECTION_TYPE:
540 return mono_class_inflate_generic_type_with_mempool (temporary ? NULL : class->image->mempool,
543 case MONO_RGCTX_INFO_METHOD:
544 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
545 case MONO_RGCTX_INFO_METHOD_RGCTX:
546 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
547 MonoMethod *method = data;
548 MonoMethod *inflated_method;
549 MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
550 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
552 mono_metadata_free_type (inflated_type);
554 mono_class_init (inflated_class);
556 if (method->wrapper_type != MONO_WRAPPER_NONE) {
557 g_assert (info_type != MONO_RGCTX_INFO_METHOD_RGCTX);
558 g_assert (method->wrapper_type == MONO_WRAPPER_STATIC_RGCTX_INVOKE);
560 method = mono_marshal_method_from_wrapper (method);
561 method = mono_class_inflate_generic_method (method, context);
562 method = mono_marshal_get_static_rgctx_invoke (method);
565 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
566 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
567 inflated_method = mono_method_search_in_array_class (inflated_class,
568 method->name, method->signature);
570 inflated_method = mono_class_inflate_generic_method (method, context);
572 mono_class_init (inflated_method->klass);
573 g_assert (inflated_method->klass == inflated_class);
574 return inflated_method;
577 case MONO_RGCTX_INFO_CLASS_FIELD: {
578 MonoClassField *field = data;
579 MonoType *inflated_type = mono_class_inflate_generic_type (&field->parent->byval_arg, context);
580 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
581 int i = field - field->parent->fields;
582 gpointer dummy = NULL;
584 mono_metadata_free_type (inflated_type);
586 mono_class_get_fields (inflated_class, &dummy);
587 g_assert (inflated_class->fields);
589 return &inflated_class->fields [i];
593 g_assert_not_reached ();
598 inflate_other_info (MonoRuntimeGenericContextOtherInfoTemplate *oti,
599 MonoGenericContext *context, MonoClass *class, gboolean temporary)
601 return inflate_other_data (oti->data, oti->info_type, context, class, temporary);
605 free_inflated_info (int info_type, gpointer info)
611 case MONO_RGCTX_INFO_STATIC_DATA:
612 case MONO_RGCTX_INFO_KLASS:
613 case MONO_RGCTX_INFO_VTABLE:
614 case MONO_RGCTX_INFO_TYPE:
615 case MONO_RGCTX_INFO_REFLECTION_TYPE:
616 mono_metadata_free_type (info);
623 static MonoRuntimeGenericContextOtherInfoTemplate
624 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free);
627 * mono_class_get_runtime_generic_context_template:
630 * Looks up or constructs, if necessary, the runtime generic context
633 static MonoRuntimeGenericContextTemplate*
634 mono_class_get_runtime_generic_context_template (MonoClass *class)
636 MonoRuntimeGenericContextTemplate *parent_template, *template;
637 MonoGenericInst *inst;
640 g_assert (!class->generic_class);
643 template = class_lookup_rgctx_template (class);
649 if (class->generic_container)
650 inst = class->generic_container->context.class_inst;
655 template = alloc_template (class);
656 mono_loader_unlock ();
661 if (class->parent->generic_class) {
663 int max_argc, type_argc;
665 parent_template = mono_class_get_runtime_generic_context_template
666 (class->parent->generic_class->container_class);
668 max_argc = template_get_max_argc (parent_template);
670 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
671 num_entries = rgctx_template_num_other_infos (parent_template, type_argc);
673 /* FIXME: quadratic! */
674 for (i = 0; i < num_entries; ++i) {
675 MonoRuntimeGenericContextOtherInfoTemplate oti;
677 oti = class_get_rgctx_template_oti (class->parent, type_argc, i, FALSE, NULL);
678 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
679 rgctx_template_set_other_slot (class->image, template, type_argc, i,
680 oti.data, oti.info_type);
685 MonoRuntimeGenericContextOtherInfoTemplate *oti;
686 int max_argc, type_argc;
688 parent_template = mono_class_get_runtime_generic_context_template (class->parent);
690 max_argc = template_get_max_argc (parent_template);
692 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
693 /* FIXME: quadratic! */
694 for (i = 0, oti = parent_template->other_infos; oti; ++i, oti = oti->next) {
695 if (oti->data && oti->data != MONO_RGCTX_SLOT_USED_MARKER) {
696 rgctx_template_set_other_slot (class->image, template, type_argc, i,
697 oti->data, oti->info_type);
704 if (class_lookup_rgctx_template (class)) {
705 /* some other thread already set the template */
706 template = class_lookup_rgctx_template (class);
708 class_set_rgctx_template (class, template);
711 register_generic_subclass (class);
720 * temporary signifies whether the inflated info (oti.data) will be
721 * used temporarily, in which case it might be heap-allocated, or
722 * permanently, in which case it will be mempool-allocated. If
723 * temporary is set then *do_free will return whether the returned
724 * data must be freed.
726 static MonoRuntimeGenericContextOtherInfoTemplate
727 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free)
729 g_assert ((temporary && do_free) || (!temporary && !do_free));
731 if (class->generic_class) {
732 MonoRuntimeGenericContextOtherInfoTemplate oti;
733 gboolean tmp_do_free;
735 oti = class_get_rgctx_template_oti (class->generic_class->container_class,
736 type_argc, slot, TRUE, &tmp_do_free);
738 gpointer info = oti.data;
739 oti.data = inflate_other_info (&oti, &class->generic_class->context, class, temporary);
741 free_inflated_info (oti.info_type, info);
748 MonoRuntimeGenericContextTemplate *template;
749 MonoRuntimeGenericContextOtherInfoTemplate *oti;
751 template = mono_class_get_runtime_generic_context_template (class);
752 oti = rgctx_template_get_other_slot (template, type_argc, slot);
763 class_uninstantiated (MonoClass *class)
765 if (class->generic_class)
766 return class->generic_class->container_class;
771 class_type_info (MonoDomain *domain, MonoClass *class, int info_type)
774 case MONO_RGCTX_INFO_STATIC_DATA:
775 return mono_class_vtable (domain, class)->data;
776 case MONO_RGCTX_INFO_KLASS:
778 case MONO_RGCTX_INFO_VTABLE:
779 return mono_class_vtable (domain, class);
781 g_assert_not_reached ();
786 instantiate_other_info (MonoDomain *domain, MonoRuntimeGenericContextOtherInfoTemplate *oti,
787 MonoGenericContext *context, MonoClass *class)
795 switch (oti->info_type) {
796 case MONO_RGCTX_INFO_STATIC_DATA:
797 case MONO_RGCTX_INFO_KLASS:
798 case MONO_RGCTX_INFO_VTABLE:
805 data = inflate_other_info (oti, context, class, temporary);
807 switch (oti->info_type) {
808 case MONO_RGCTX_INFO_STATIC_DATA:
809 case MONO_RGCTX_INFO_KLASS:
810 case MONO_RGCTX_INFO_VTABLE: {
811 MonoClass *arg_class = mono_class_from_mono_type (data);
813 free_inflated_info (oti->info_type, data);
814 g_assert (arg_class);
816 return class_type_info (domain, arg_class, oti->info_type);
818 case MONO_RGCTX_INFO_TYPE:
820 case MONO_RGCTX_INFO_REFLECTION_TYPE:
821 return mono_type_get_object (domain, data);
822 case MONO_RGCTX_INFO_METHOD:
824 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
825 return mono_compile_method (data);
826 case MONO_RGCTX_INFO_CLASS_FIELD:
828 case MONO_RGCTX_INFO_METHOD_RGCTX: {
829 MonoMethodInflated *method = data;
831 g_assert (method->method.method.is_inflated);
832 g_assert (method->context.method_inst);
834 return mono_method_lookup_rgctx (mono_class_vtable (domain, method->method.method.klass),
835 method->context.method_inst);
837 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
838 MonoMethodInflated *method = data;
840 g_assert (method->method.method.is_inflated);
841 g_assert (method->context.method_inst);
843 return method->context.method_inst;
846 g_assert_not_reached ();
851 * LOCKING: templates lock
854 fill_in_rgctx_template_slot (MonoClass *class, int type_argc, int index, gpointer data, int info_type)
856 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
859 g_assert (!class->generic_class);
861 rgctx_template_set_other_slot (class->image, template, type_argc, index, data, info_type);
863 /* Recurse for all subclasses */
864 if (generic_subclass_hash)
865 subclass = g_hash_table_lookup (generic_subclass_hash, class);
870 MonoRuntimeGenericContextOtherInfoTemplate subclass_oti;
871 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
873 g_assert (!subclass->generic_class);
874 g_assert (subclass_template);
876 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, NULL);
877 g_assert (subclass_oti.data);
879 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
881 subclass = subclass_template->next_subclass;
886 * LOCKING: templates lock
889 register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type)
892 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
894 MonoRuntimeGenericContextOtherInfoTemplate *oti;
896 for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
901 //g_print ("template %s . other_infos [%d] = %s\n", mono_type_get_full_name (class), i, mono_type_get_full_name (other_class));
903 /* Mark the slot as used in all parent classes (until we find
904 a parent class which already has it marked used). */
905 parent = class->parent;
906 while (parent != NULL) {
907 MonoRuntimeGenericContextTemplate *parent_template;
908 MonoRuntimeGenericContextOtherInfoTemplate *oti;
910 if (parent->generic_class)
911 parent = parent->generic_class->container_class;
913 parent_template = mono_class_get_runtime_generic_context_template (parent);
914 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
916 if (oti && oti->data)
919 rgctx_template_set_other_slot (parent->image, parent_template, type_argc, i,
920 MONO_RGCTX_SLOT_USED_MARKER, 0);
922 parent = parent->parent;
925 /* Fill in the slot in this class and in all subclasses
927 fill_in_rgctx_template_slot (class, type_argc, i, data, info_type);
933 other_info_equal (gpointer data1, gpointer data2, int info_type)
936 case MONO_RGCTX_INFO_STATIC_DATA:
937 case MONO_RGCTX_INFO_KLASS:
938 case MONO_RGCTX_INFO_VTABLE:
939 case MONO_RGCTX_INFO_TYPE:
940 case MONO_RGCTX_INFO_REFLECTION_TYPE:
941 return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
942 case MONO_RGCTX_INFO_METHOD:
943 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
944 case MONO_RGCTX_INFO_CLASS_FIELD:
945 case MONO_RGCTX_INFO_METHOD_RGCTX:
946 case MONO_RGCTX_INFO_METHOD_CONTEXT:
947 return data1 == data2;
949 g_assert_not_reached ();
954 lookup_or_register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type,
955 MonoGenericContext *generic_context)
957 static gboolean inited = FALSE;
958 static int max_slot = 0;
960 MonoRuntimeGenericContextTemplate *rgctx_template =
961 mono_class_get_runtime_generic_context_template (class);
962 MonoRuntimeGenericContextOtherInfoTemplate *oti_list, *oti, *copy;
965 g_assert (!class->generic_class);
966 g_assert (class->generic_container || type_argc);
969 * We must not call inflate_other_info() with the templates
970 * lock held, because it calls metadata functions which might
971 * cause the loader lock to be taken, which must not happen if
972 * the templates lock is held.
974 * Only two things can happen to an oti list: An unused
975 * (data==NULL) node can be filled in and nodes can be
976 * appended at the end of the list.
978 * To solve the lock problem we first count the number of
979 * nodes in the list, then copy all the data into a separate
980 * array. With the templates lock not held we then search for
981 * our info in the array - this is where the calls to
982 * inflate_other_info() happen. If we don't find the info
983 * we're looking for, we take the templates lock again and
984 * check if the oti list has changed since we've copied it.
985 * If it has, we start again. If it hasn't, we register the
992 oti_list = get_other_info_templates (rgctx_template, type_argc);
995 for (oti = oti_list; oti; oti = oti->next)
998 copy = g_new (MonoRuntimeGenericContextOtherInfoTemplate, length);
1000 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
1001 copy [i].info_type = oti->info_type;
1002 copy [i].data = oti->data;
1004 g_assert (i == length);
1006 templates_unlock ();
1008 /* We've copied the list. Now look for the info. */
1010 for (i = 0; i < length; ++i) {
1011 gpointer inflated_data;
1013 if (copy [i].info_type != info_type || !copy [i].data)
1016 inflated_data = inflate_other_info (© [i], generic_context, class, TRUE);
1018 if (other_info_equal (data, inflated_data, info_type)) {
1019 free_inflated_info (info_type, inflated_data);
1023 free_inflated_info (info_type, inflated_data);
1026 /* We haven't found the info, so check if the list is still
1031 /* We need to fetch oti_list again here because the list could
1033 oti_list = get_other_info_templates (rgctx_template, type_argc);
1035 for (oti = oti_list, i = 0; i < length; oti = oti->next, ++i) {
1038 if (copy [i].info_type != oti->info_type || copy [i].data != oti->data) {
1047 /* The list is still the same - success. */
1049 i = register_other_info (class, type_argc, data, info_type);
1051 templates_unlock ();
1054 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
1064 * mono_method_lookup_or_register_other_info:
1066 * @in_mrgctx: whether to put the data into the MRGCTX
1067 * @data: the info data
1068 * @info_type: the type of info to register about data
1069 * @generic_context: a generic context
1071 * Looks up and, if necessary, adds information about other_class in
1072 * method's or method's class runtime generic context. Returns the
1073 * encoded slot number.
1076 mono_method_lookup_or_register_other_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
1077 int info_type, MonoGenericContext *generic_context)
1079 MonoClass *class = method->klass;
1080 int type_argc, index;
1083 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
1085 g_assert (method->is_inflated && method_inst);
1086 type_argc = method_inst->type_argc;
1087 g_assert (type_argc > 0);
1092 index = lookup_or_register_other_info (class, type_argc, data, info_type, generic_context);
1094 //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
1097 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
1099 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
1103 * mono_class_rgctx_get_array_size:
1104 * @n: The number of the array
1105 * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
1107 * Returns the number of slots in the n'th array of a (M)RGCTX. That
1108 * number includes the slot for linking and - for MRGCTXs - the two
1109 * slots in the first array for additional information.
1112 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
1114 g_assert (n >= 0 && n < 30);
1123 * LOCKING: domain lock
1126 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
1128 static gboolean inited = FALSE;
1129 static int rgctx_num_alloced = 0;
1130 static int rgctx_bytes_alloced = 0;
1131 static int mrgctx_num_alloced = 0;
1132 static int mrgctx_bytes_alloced = 0;
1134 int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
1135 gpointer array = mono_domain_alloc0 (domain, size);
1138 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
1139 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
1140 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
1141 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
1146 mrgctx_num_alloced++;
1147 mrgctx_bytes_alloced += size;
1149 rgctx_num_alloced++;
1150 rgctx_bytes_alloced += size;
1157 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
1158 MonoGenericInst *method_inst)
1161 int i, first_slot, size;
1162 MonoDomain *domain = class_vtable->domain;
1163 MonoClass *class = class_vtable->klass;
1164 MonoGenericContext *class_context = class->generic_class ? &class->generic_class->context : NULL;
1165 MonoRuntimeGenericContextOtherInfoTemplate oti;
1166 MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
1172 mono_domain_lock (domain);
1174 /* First check whether that slot isn't already instantiated.
1175 This might happen because lookup doesn't lock. Allocate
1176 arrays on the way. */
1178 size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
1180 size -= sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
1181 for (i = 0; ; ++i) {
1184 if (method_inst && i == 0)
1185 offset = sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
1189 if (slot < first_slot + size - 1) {
1190 rgctx_index = slot - first_slot + 1 + offset;
1191 info = rgctx [rgctx_index];
1193 mono_domain_unlock (domain);
1198 if (!rgctx [offset + 0])
1199 rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
1200 rgctx = rgctx [offset + 0];
1201 first_slot += size - 1;
1202 size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
1205 g_assert (!rgctx [rgctx_index]);
1207 mono_domain_unlock (domain);
1209 oti = class_get_rgctx_template_oti (class_uninstantiated (class),
1210 method_inst ? method_inst->type_argc : 0, slot, TRUE, &do_free);
1211 /* This might take the loader lock */
1212 info = instantiate_other_info (domain, &oti, &context, class);
1216 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
1219 mono_domain_lock (domain);
1221 /* Check whether the slot hasn't been instantiated in the
1223 if (rgctx [rgctx_index])
1224 info = rgctx [rgctx_index];
1226 rgctx [rgctx_index] = info;
1228 mono_domain_unlock (domain);
1231 free_inflated_info (oti.info_type, oti.data);
1237 * mono_class_fill_runtime_generic_context:
1238 * @class_vtable: a vtable
1239 * @slot: a slot index to be instantiated
1241 * Instantiates a slot in the RGCTX.
1244 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot)
1246 static gboolean inited = FALSE;
1247 static int num_alloced = 0;
1249 MonoDomain *domain = class_vtable->domain;
1250 MonoRuntimeGenericContext *rgctx;
1253 mono_domain_lock (domain);
1256 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
1260 rgctx = class_vtable->runtime_generic_context;
1262 rgctx = alloc_rgctx_array (domain, 0, FALSE);
1263 class_vtable->runtime_generic_context = rgctx;
1267 mono_domain_unlock (domain);
1269 info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
1275 * mono_method_fill_runtime_generic_context:
1276 * @mrgctx: an MRGCTX
1277 * @slot: a slot index to be instantiated
1279 * Instantiates a slot in the MRGCTX.
1282 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot)
1284 MonoDomain *domain = mrgctx->class_vtable->domain;
1287 info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot,
1288 mrgctx->method_inst);
1294 mrgctx_hash_func (gconstpointer key)
1296 const MonoMethodRuntimeGenericContext *mrgctx = key;
1298 return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
1302 mrgctx_equal_func (gconstpointer a, gconstpointer b)
1304 const MonoMethodRuntimeGenericContext *mrgctx1 = a;
1305 const MonoMethodRuntimeGenericContext *mrgctx2 = b;
1307 return mrgctx1->class_vtable == mrgctx2->class_vtable &&
1308 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
1312 * mono_method_lookup_rgctx:
1313 * @class_vtable: a vtable
1314 * @method_inst: the method inst of a generic method
1316 * Returns the MRGCTX for the generic method(s) with the given
1317 * method_inst of the given class_vtable.
1319 MonoMethodRuntimeGenericContext*
1320 mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
1322 MonoDomain *domain = class_vtable->domain;
1323 MonoMethodRuntimeGenericContext *mrgctx;
1324 MonoMethodRuntimeGenericContext key;
1326 g_assert (!class_vtable->klass->generic_container);
1327 g_assert (!method_inst->is_open);
1329 mono_domain_lock (domain);
1330 if (!domain->method_rgctx_hash)
1331 domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
1333 key.class_vtable = class_vtable;
1334 key.method_inst = method_inst;
1336 mrgctx = g_hash_table_lookup (domain->method_rgctx_hash, &key);
1341 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
1342 mrgctx->class_vtable = class_vtable;
1343 mrgctx->method_inst = method_inst;
1345 g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
1348 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
1349 for (i = 0; i < method_inst->type_argc; ++i)
1350 g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
1355 mono_domain_unlock (domain);
1363 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars)
1367 for (i = 0; i < inst->type_argc; ++i) {
1368 MonoType *type = inst->type_argv [i];
1371 if (MONO_TYPE_IS_REFERENCE (type))
1374 type_type = mono_type_get_type (type);
1375 if (allow_type_vars && (type_type == MONO_TYPE_VAR || type_type == MONO_TYPE_MVAR))
1385 * mono_generic_context_is_sharable:
1386 * @context: a generic context
1388 * Returns whether the generic context is sharable. A generic context
1389 * is sharable iff all of its type arguments are reference type.
1392 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
1394 g_assert (context->class_inst || context->method_inst);
1396 if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars))
1399 if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars))
1406 * mono_method_is_generic_impl:
1409 * Returns whether the method is either generic or part of a generic
1413 mono_method_is_generic_impl (MonoMethod *method)
1415 if (method->is_inflated) {
1416 g_assert (method->wrapper_type == MONO_WRAPPER_NONE);
1419 /* We don't treat wrappers as generic code, i.e., we never
1420 apply generic sharing to them. This is especially
1421 important for static rgctx invoke wrappers, which only work
1422 if not compiled with sharing. */
1423 if (method->wrapper_type != MONO_WRAPPER_NONE)
1425 if (method->klass->generic_container)
1431 has_constraints (MonoGenericContainer *container)
1437 g_assert (container->type_argc > 0);
1438 g_assert (container->type_params);
1440 for (i = 0; i < container->type_argc; ++i)
1441 if (container->type_params [i].constraints)
1448 * mono_method_is_generic_sharable_impl:
1450 * @allow_type_vars: whether to regard type variables as reference types
1452 * Returns TRUE iff the method is inflated or part of an inflated
1453 * class, its context is sharable and it has no constraints on its
1454 * type parameters. Otherwise returns FALSE.
1457 mono_method_is_generic_sharable_impl (MonoMethod *method, gboolean allow_type_vars)
1459 if (!mono_method_is_generic_impl (method))
1462 if (method->is_inflated) {
1463 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
1464 MonoGenericContext *context = &inflated->context;
1466 if (!mono_generic_context_is_sharable (context, allow_type_vars))
1469 g_assert (inflated->declaring);
1471 if (inflated->declaring->is_generic) {
1472 if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
1477 if (method->klass->generic_class) {
1478 if (!mono_generic_context_is_sharable (&method->klass->generic_class->context, allow_type_vars))
1481 g_assert (method->klass->generic_class->container_class &&
1482 method->klass->generic_class->container_class->generic_container);
1484 if (has_constraints (method->klass->generic_class->container_class->generic_container))
1488 if (method->klass->generic_container && !allow_type_vars)
1495 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
1497 if (!mono_class_generic_sharing_enabled (method->klass))
1500 if (!mono_method_is_generic_sharable_impl (method, allow_type_vars))
1503 if (method->is_inflated && mono_method_get_context (method)->method_inst)
1506 return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
1507 method->klass->valuetype) &&
1508 (method->klass->generic_class || method->klass->generic_container);