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)
19 #include <mono/utils/mono-membar.h>
20 #include <mono/utils/mono-counters.h>
22 #include "metadata-internals.h"
24 #include "class-internals.h"
26 #include "debug-helpers.h"
27 #include "tabledefs.h"
28 #include "mempool-internals.h"
31 type_check_context_used (MonoType *type, gboolean recursive)
33 switch (mono_type_get_type (type)) {
35 return MONO_GENERIC_CONTEXT_USED_CLASS;
37 return MONO_GENERIC_CONTEXT_USED_METHOD;
38 case MONO_TYPE_SZARRAY:
39 return mono_class_check_context_used (mono_type_get_class (type));
41 return mono_class_check_context_used (mono_type_get_array_type (type)->eklass);
44 return mono_class_check_context_used (mono_type_get_class (type));
47 case MONO_TYPE_GENERICINST:
49 MonoGenericClass *gclass = type->data.generic_class;
51 g_assert (gclass->container_class->generic_container);
52 return mono_generic_context_check_used (&gclass->context);
62 inst_check_context_used (MonoGenericInst *inst)
70 for (i = 0; i < inst->type_argc; ++i)
71 context_used |= type_check_context_used (inst->type_argv [i], TRUE);
77 * mono_generic_context_check_used:
78 * @context: a generic context
80 * Checks whether the context uses a type variable. Returns an int
81 * with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to reflect whether
82 * the context's class instantiation uses type variables.
85 mono_generic_context_check_used (MonoGenericContext *context)
89 context_used |= inst_check_context_used (context->class_inst);
90 context_used |= inst_check_context_used (context->method_inst);
96 * mono_class_check_context_used:
99 * Checks whether the class's generic context uses a type variable.
100 * Returns an int with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to
101 * reflect whether the context's class instantiation uses type
105 mono_class_check_context_used (MonoClass *class)
107 int context_used = 0;
109 context_used |= type_check_context_used (&class->this_arg, FALSE);
110 context_used |= type_check_context_used (&class->byval_arg, FALSE);
112 if (class->generic_class)
113 context_used |= mono_generic_context_check_used (&class->generic_class->context);
114 else if (class->generic_container)
115 context_used |= mono_generic_context_check_used (&class->generic_container->context);
121 * Guards the two global rgctx (template) hash tables and all rgctx
124 * Ordering: The templates lock can be taken while the loader lock is
127 static CRITICAL_SECTION templates_mutex;
130 templates_lock (void)
132 static gboolean inited = FALSE;
137 InitializeCriticalSection (&templates_mutex);
140 mono_loader_unlock ();
143 EnterCriticalSection (&templates_mutex);
147 templates_unlock (void)
149 LeaveCriticalSection (&templates_mutex);
153 * LOCKING: templates lock
155 static MonoRuntimeGenericContextOtherInfoTemplate*
156 get_other_info_templates (MonoRuntimeGenericContextTemplate *template, int type_argc)
158 g_assert (type_argc >= 0);
160 return template->other_infos;
161 return g_slist_nth_data (template->method_templates, type_argc - 1);
165 * LOCKING: templates lock
168 set_other_info_templates (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
169 MonoRuntimeGenericContextOtherInfoTemplate *oti)
171 g_assert (type_argc >= 0);
173 template->other_infos = oti;
175 int length = g_slist_length (template->method_templates);
178 /* FIXME: quadratic! */
179 while (length < type_argc) {
180 template->method_templates = g_slist_append_image (image, template->method_templates, NULL);
184 list = g_slist_nth (template->method_templates, type_argc - 1);
191 * LOCKING: templates lock
194 template_get_max_argc (MonoRuntimeGenericContextTemplate *template)
196 return g_slist_length (template->method_templates);
200 * LOCKING: templates lock
202 static MonoRuntimeGenericContextOtherInfoTemplate*
203 rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate *template, int type_argc, int slot)
206 MonoRuntimeGenericContextOtherInfoTemplate *oti;
208 g_assert (slot >= 0);
210 for (oti = get_other_info_templates (template, type_argc), i = 0; i < slot; oti = oti->next, ++i) {
219 * LOCKING: templates lock
222 rgctx_template_num_other_infos (MonoRuntimeGenericContextTemplate *template, int type_argc)
224 MonoRuntimeGenericContextOtherInfoTemplate *oti;
227 for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next)
233 /* Maps from uninstantiated generic classes to GList's of
234 * uninstantiated generic classes whose parent is the key class or an
235 * instance of the key class.
237 * LOCKING: templates lock
239 static GHashTable *generic_subclass_hash;
242 * LOCKING: templates lock
245 class_set_rgctx_template (MonoClass *class, MonoRuntimeGenericContextTemplate *rgctx_template)
247 if (!class->image->rgctx_template_hash)
248 class->image->rgctx_template_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
250 g_hash_table_insert (class->image->rgctx_template_hash, class, rgctx_template);
254 * LOCKING: templates lock
256 static MonoRuntimeGenericContextTemplate*
257 class_lookup_rgctx_template (MonoClass *class)
259 MonoRuntimeGenericContextTemplate *template;
261 if (!class->image->rgctx_template_hash)
264 template = g_hash_table_lookup (class->image->rgctx_template_hash, class);
270 * LOCKING: templates lock
273 register_generic_subclass (MonoClass *class)
275 MonoClass *parent = class->parent;
277 MonoRuntimeGenericContextTemplate *rgctx_template = class_lookup_rgctx_template (class);
279 g_assert (rgctx_template);
281 if (parent->generic_class)
282 parent = parent->generic_class->container_class;
284 if (!generic_subclass_hash)
285 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
287 subclass = g_hash_table_lookup (generic_subclass_hash, parent);
288 rgctx_template->next_subclass = subclass;
289 g_hash_table_insert (generic_subclass_hash, parent, class);
293 move_subclasses_not_in_image_foreach_func (MonoClass *class, MonoClass *subclass, MonoImage *image)
297 if (class->image == image) {
298 /* The parent class itself is in the image, so all the
299 subclasses must be in the image, too. If not,
300 we're removing an image containing a class which
301 still has a subclass in another image. */
304 g_assert (subclass->image == image);
305 subclass = class_lookup_rgctx_template (subclass)->next_subclass;
313 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
314 MonoClass *next = subclass_template->next_subclass;
316 if (subclass->image != image) {
317 subclass_template->next_subclass = new_list;
325 g_hash_table_insert (generic_subclass_hash, class, new_list);
329 * mono_class_unregister_image_generic_subclasses:
332 * Removes all classes of the image from the generic subclass hash.
333 * Must be called when an image is unloaded.
336 mono_class_unregister_image_generic_subclasses (MonoImage *image)
338 GHashTable *old_hash;
340 //g_print ("unregistering image %s\n", image->name);
342 if (!generic_subclass_hash)
347 old_hash = generic_subclass_hash;
348 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
350 g_hash_table_foreach (old_hash, (GHFunc)move_subclasses_not_in_image_foreach_func, image);
354 g_hash_table_destroy (old_hash);
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);
378 static MonoRuntimeGenericContextOtherInfoTemplate*
379 alloc_oti (MonoImage *image)
381 static gboolean inited = FALSE;
382 static int num_allocted = 0;
383 static int num_bytes = 0;
385 int size = sizeof (MonoRuntimeGenericContextOtherInfoTemplate);
388 mono_counters_register ("RGCTX oti num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_allocted);
389 mono_counters_register ("RGCTX oti bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_bytes);
396 return mono_image_alloc0 (image, size);
399 #define MONO_RGCTX_SLOT_USED_MARKER ((gpointer)&mono_defaults.object_class->byval_arg)
402 * LOCKING: templates lock
405 rgctx_template_set_other_slot (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
406 int slot, gpointer data, int info_type)
408 static gboolean inited = FALSE;
409 static int num_markers = 0;
410 static int num_data = 0;
413 MonoRuntimeGenericContextOtherInfoTemplate *list = get_other_info_templates (template, type_argc);
414 MonoRuntimeGenericContextOtherInfoTemplate **oti = &list;
417 mono_counters_register ("RGCTX oti num markers", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_markers);
418 mono_counters_register ("RGCTX oti num data", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_data);
422 g_assert (slot >= 0);
430 *oti = alloc_oti (image);
434 g_assert (!(*oti)->data);
436 (*oti)->info_type = info_type;
438 set_other_info_templates (image, template, type_argc, list);
440 if (data == MONO_RGCTX_SLOT_USED_MARKER)
447 * mono_method_get_declaring_generic_method:
448 * @method: an inflated method
450 * Returns an inflated method's declaring method.
453 mono_method_get_declaring_generic_method (MonoMethod *method)
455 MonoMethodInflated *inflated;
457 g_assert (method->is_inflated);
459 inflated = (MonoMethodInflated*)method;
461 return inflated->declaring;
465 * mono_class_get_method_generic:
469 * Given a class and a generic method, which has to be of an
470 * instantiation of the same class that klass is an instantiation of,
471 * returns the corresponding method in klass. Example:
473 * klass is Gen<string>
474 * method is Gen<object>.work<int>
476 * returns: Gen<string>.work<int>
479 mono_class_get_method_generic (MonoClass *klass, MonoMethod *method)
481 MonoMethod *declaring, *m;
484 if (method->is_inflated)
485 declaring = mono_method_get_declaring_generic_method (method);
490 if (klass->generic_class)
491 m = mono_class_get_inflated_method (klass, declaring);
494 mono_class_setup_methods (klass);
495 for (i = 0; i < klass->method.count; ++i) {
496 m = klass->methods [i];
499 if (m->is_inflated && mono_method_get_declaring_generic_method (m) == declaring)
502 if (i >= klass->method.count)
506 if (method != declaring) {
507 MonoGenericContext context;
509 context.class_inst = NULL;
510 context.method_inst = mono_method_get_context (method)->method_inst;
512 m = mono_class_inflate_generic_method (m, &context);
519 inflate_other_data (gpointer data, int info_type, MonoGenericContext *context, MonoClass *class, gboolean temporary)
523 if (data == MONO_RGCTX_SLOT_USED_MARKER)
524 return MONO_RGCTX_SLOT_USED_MARKER;
528 case MONO_RGCTX_INFO_STATIC_DATA:
529 case MONO_RGCTX_INFO_KLASS:
530 case MONO_RGCTX_INFO_VTABLE:
531 case MONO_RGCTX_INFO_TYPE:
532 case MONO_RGCTX_INFO_REFLECTION_TYPE:
533 return mono_class_inflate_generic_type_with_mempool (temporary ? NULL : class->image,
536 case MONO_RGCTX_INFO_METHOD:
537 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
538 case MONO_RGCTX_INFO_METHOD_RGCTX:
539 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
540 MonoMethod *method = data;
541 MonoMethod *inflated_method;
542 MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
543 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
545 mono_metadata_free_type (inflated_type);
547 mono_class_init (inflated_class);
549 if (method->wrapper_type != MONO_WRAPPER_NONE) {
550 g_assert (info_type != MONO_RGCTX_INFO_METHOD_RGCTX);
551 g_assert (method->wrapper_type == MONO_WRAPPER_STATIC_RGCTX_INVOKE);
553 method = mono_marshal_method_from_wrapper (method);
554 method = mono_class_inflate_generic_method (method, context);
555 method = mono_marshal_get_static_rgctx_invoke (method);
558 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
559 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
560 inflated_method = mono_method_search_in_array_class (inflated_class,
561 method->name, method->signature);
563 inflated_method = mono_class_inflate_generic_method (method, context);
565 mono_class_init (inflated_method->klass);
566 g_assert (inflated_method->klass == inflated_class);
567 return inflated_method;
570 case MONO_RGCTX_INFO_CLASS_FIELD: {
571 MonoClassField *field = data;
572 MonoType *inflated_type = mono_class_inflate_generic_type (&field->parent->byval_arg, context);
573 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
574 int i = field - field->parent->fields;
575 gpointer dummy = NULL;
577 mono_metadata_free_type (inflated_type);
579 mono_class_get_fields (inflated_class, &dummy);
580 g_assert (inflated_class->fields);
582 return &inflated_class->fields [i];
586 g_assert_not_reached ();
591 inflate_other_info (MonoRuntimeGenericContextOtherInfoTemplate *oti,
592 MonoGenericContext *context, MonoClass *class, gboolean temporary)
594 return inflate_other_data (oti->data, oti->info_type, context, class, temporary);
598 free_inflated_info (int info_type, gpointer info)
604 case MONO_RGCTX_INFO_STATIC_DATA:
605 case MONO_RGCTX_INFO_KLASS:
606 case MONO_RGCTX_INFO_VTABLE:
607 case MONO_RGCTX_INFO_TYPE:
608 case MONO_RGCTX_INFO_REFLECTION_TYPE:
609 mono_metadata_free_type (info);
616 static MonoRuntimeGenericContextOtherInfoTemplate
617 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free);
620 * mono_class_get_runtime_generic_context_template:
623 * Looks up or constructs, if necessary, the runtime generic context
626 static MonoRuntimeGenericContextTemplate*
627 mono_class_get_runtime_generic_context_template (MonoClass *class)
629 MonoRuntimeGenericContextTemplate *parent_template, *template;
630 MonoGenericInst *inst;
633 g_assert (!class->generic_class);
636 template = class_lookup_rgctx_template (class);
642 if (class->generic_container)
643 inst = class->generic_container->context.class_inst;
647 template = alloc_template (class);
652 if (class->parent->generic_class) {
654 int max_argc, type_argc;
656 parent_template = mono_class_get_runtime_generic_context_template
657 (class->parent->generic_class->container_class);
659 max_argc = template_get_max_argc (parent_template);
661 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
662 num_entries = rgctx_template_num_other_infos (parent_template, type_argc);
664 /* FIXME: quadratic! */
665 for (i = 0; i < num_entries; ++i) {
666 MonoRuntimeGenericContextOtherInfoTemplate oti;
668 oti = class_get_rgctx_template_oti (class->parent, type_argc, i, FALSE, NULL);
669 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
670 rgctx_template_set_other_slot (class->image, template, type_argc, i,
671 oti.data, oti.info_type);
676 MonoRuntimeGenericContextOtherInfoTemplate *oti;
677 int max_argc, type_argc;
679 parent_template = mono_class_get_runtime_generic_context_template (class->parent);
681 max_argc = template_get_max_argc (parent_template);
683 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
684 /* FIXME: quadratic! */
685 for (i = 0, oti = parent_template->other_infos; oti; ++i, oti = oti->next) {
686 if (oti->data && oti->data != MONO_RGCTX_SLOT_USED_MARKER) {
687 rgctx_template_set_other_slot (class->image, template, type_argc, i,
688 oti->data, oti->info_type);
695 if (class_lookup_rgctx_template (class)) {
696 /* some other thread already set the template */
697 template = class_lookup_rgctx_template (class);
699 class_set_rgctx_template (class, template);
702 register_generic_subclass (class);
711 * temporary signifies whether the inflated info (oti.data) will be
712 * used temporarily, in which case it might be heap-allocated, or
713 * permanently, in which case it will be mempool-allocated. If
714 * temporary is set then *do_free will return whether the returned
715 * data must be freed.
717 static MonoRuntimeGenericContextOtherInfoTemplate
718 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free)
720 g_assert ((temporary && do_free) || (!temporary && !do_free));
722 if (class->generic_class) {
723 MonoRuntimeGenericContextOtherInfoTemplate oti;
724 gboolean tmp_do_free;
726 oti = class_get_rgctx_template_oti (class->generic_class->container_class,
727 type_argc, slot, TRUE, &tmp_do_free);
729 gpointer info = oti.data;
730 oti.data = inflate_other_info (&oti, &class->generic_class->context, class, temporary);
732 free_inflated_info (oti.info_type, info);
739 MonoRuntimeGenericContextTemplate *template;
740 MonoRuntimeGenericContextOtherInfoTemplate *oti;
742 template = mono_class_get_runtime_generic_context_template (class);
743 oti = rgctx_template_get_other_slot (template, type_argc, slot);
754 class_uninstantiated (MonoClass *class)
756 if (class->generic_class)
757 return class->generic_class->container_class;
762 class_type_info (MonoDomain *domain, MonoClass *class, int info_type)
765 case MONO_RGCTX_INFO_STATIC_DATA:
766 return mono_class_vtable (domain, class)->data;
767 case MONO_RGCTX_INFO_KLASS:
769 case MONO_RGCTX_INFO_VTABLE:
770 return mono_class_vtable (domain, class);
772 g_assert_not_reached ();
777 instantiate_other_info (MonoDomain *domain, MonoRuntimeGenericContextOtherInfoTemplate *oti,
778 MonoGenericContext *context, MonoClass *class)
786 switch (oti->info_type) {
787 case MONO_RGCTX_INFO_STATIC_DATA:
788 case MONO_RGCTX_INFO_KLASS:
789 case MONO_RGCTX_INFO_VTABLE:
796 data = inflate_other_info (oti, context, class, temporary);
798 switch (oti->info_type) {
799 case MONO_RGCTX_INFO_STATIC_DATA:
800 case MONO_RGCTX_INFO_KLASS:
801 case MONO_RGCTX_INFO_VTABLE: {
802 MonoClass *arg_class = mono_class_from_mono_type (data);
804 free_inflated_info (oti->info_type, data);
805 g_assert (arg_class);
807 return class_type_info (domain, arg_class, oti->info_type);
809 case MONO_RGCTX_INFO_TYPE:
811 case MONO_RGCTX_INFO_REFLECTION_TYPE:
812 return mono_type_get_object (domain, data);
813 case MONO_RGCTX_INFO_METHOD:
815 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
816 return mono_compile_method (data);
817 case MONO_RGCTX_INFO_CLASS_FIELD:
819 case MONO_RGCTX_INFO_METHOD_RGCTX: {
820 MonoMethodInflated *method = data;
822 g_assert (method->method.method.is_inflated);
823 g_assert (method->context.method_inst);
825 return mono_method_lookup_rgctx (mono_class_vtable (domain, method->method.method.klass),
826 method->context.method_inst);
828 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
829 MonoMethodInflated *method = data;
831 g_assert (method->method.method.is_inflated);
832 g_assert (method->context.method_inst);
834 return method->context.method_inst;
837 g_assert_not_reached ();
842 * LOCKING: templates lock
845 fill_in_rgctx_template_slot (MonoClass *class, int type_argc, int index, gpointer data, int info_type)
847 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
850 g_assert (!class->generic_class);
852 rgctx_template_set_other_slot (class->image, template, type_argc, index, data, info_type);
854 /* Recurse for all subclasses */
855 if (generic_subclass_hash)
856 subclass = g_hash_table_lookup (generic_subclass_hash, class);
861 MonoRuntimeGenericContextOtherInfoTemplate subclass_oti;
862 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
864 g_assert (!subclass->generic_class);
865 g_assert (subclass_template);
867 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, NULL);
868 g_assert (subclass_oti.data);
870 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
872 subclass = subclass_template->next_subclass;
877 * LOCKING: templates lock
880 register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type)
883 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
885 MonoRuntimeGenericContextOtherInfoTemplate *oti;
887 for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
892 //g_print ("template %s . other_infos [%d] = %s\n", mono_type_get_full_name (class), i, mono_type_get_full_name (other_class));
894 /* Mark the slot as used in all parent classes (until we find
895 a parent class which already has it marked used). */
896 parent = class->parent;
897 while (parent != NULL) {
898 MonoRuntimeGenericContextTemplate *parent_template;
899 MonoRuntimeGenericContextOtherInfoTemplate *oti;
901 if (parent->generic_class)
902 parent = parent->generic_class->container_class;
904 parent_template = mono_class_get_runtime_generic_context_template (parent);
905 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
907 if (oti && oti->data)
910 rgctx_template_set_other_slot (parent->image, parent_template, type_argc, i,
911 MONO_RGCTX_SLOT_USED_MARKER, 0);
913 parent = parent->parent;
916 /* Fill in the slot in this class and in all subclasses
918 fill_in_rgctx_template_slot (class, type_argc, i, data, info_type);
924 other_info_equal (gpointer data1, gpointer data2, int info_type)
927 case MONO_RGCTX_INFO_STATIC_DATA:
928 case MONO_RGCTX_INFO_KLASS:
929 case MONO_RGCTX_INFO_VTABLE:
930 case MONO_RGCTX_INFO_TYPE:
931 case MONO_RGCTX_INFO_REFLECTION_TYPE:
932 return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
933 case MONO_RGCTX_INFO_METHOD:
934 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
935 case MONO_RGCTX_INFO_CLASS_FIELD:
936 case MONO_RGCTX_INFO_METHOD_RGCTX:
937 case MONO_RGCTX_INFO_METHOD_CONTEXT:
938 return data1 == data2;
940 g_assert_not_reached ();
945 lookup_or_register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type,
946 MonoGenericContext *generic_context)
948 static gboolean inited = FALSE;
949 static int max_slot = 0;
951 MonoRuntimeGenericContextTemplate *rgctx_template =
952 mono_class_get_runtime_generic_context_template (class);
953 MonoRuntimeGenericContextOtherInfoTemplate *oti_list, *oti, *copy;
956 g_assert (!class->generic_class);
957 g_assert (class->generic_container || type_argc);
960 * We must not call inflate_other_info() with the templates
961 * lock held, because it calls metadata functions which might
962 * cause the loader lock to be taken, which must not happen if
963 * the templates lock is held.
965 * Only two things can happen to an oti list: An unused
966 * (data==NULL) node can be filled in and nodes can be
967 * appended at the end of the list.
969 * To solve the lock problem we first count the number of
970 * nodes in the list, then copy all the data into a separate
971 * array. With the templates lock not held we then search for
972 * our info in the array - this is where the calls to
973 * inflate_other_info() happen. If we don't find the info
974 * we're looking for, we take the templates lock again and
975 * check if the oti list has changed since we've copied it.
976 * If it has, we start again. If it hasn't, we register the
983 oti_list = get_other_info_templates (rgctx_template, type_argc);
986 for (oti = oti_list; oti; oti = oti->next)
989 copy = g_new (MonoRuntimeGenericContextOtherInfoTemplate, length);
991 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
992 copy [i].info_type = oti->info_type;
993 copy [i].data = oti->data;
995 g_assert (i == length);
999 /* We've copied the list. Now look for the info. */
1001 for (i = 0; i < length; ++i) {
1002 gpointer inflated_data;
1004 if (copy [i].info_type != info_type || !copy [i].data)
1007 inflated_data = inflate_other_info (© [i], generic_context, class, TRUE);
1009 if (other_info_equal (data, inflated_data, info_type)) {
1010 free_inflated_info (info_type, inflated_data);
1014 free_inflated_info (info_type, inflated_data);
1017 /* We haven't found the info, so check if the list is still
1022 /* We need to fetch oti_list again here because the list could
1024 oti_list = get_other_info_templates (rgctx_template, type_argc);
1026 for (oti = oti_list, i = 0; i < length; oti = oti->next, ++i) {
1029 if (copy [i].info_type != oti->info_type || copy [i].data != oti->data) {
1038 /* The list is still the same - success. */
1040 i = register_other_info (class, type_argc, data, info_type);
1042 templates_unlock ();
1045 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
1055 * mono_method_lookup_or_register_other_info:
1057 * @in_mrgctx: whether to put the data into the MRGCTX
1058 * @data: the info data
1059 * @info_type: the type of info to register about data
1060 * @generic_context: a generic context
1062 * Looks up and, if necessary, adds information about other_class in
1063 * method's or method's class runtime generic context. Returns the
1064 * encoded slot number.
1067 mono_method_lookup_or_register_other_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
1068 int info_type, MonoGenericContext *generic_context)
1070 MonoClass *class = method->klass;
1071 int type_argc, index;
1074 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
1076 g_assert (method->is_inflated && method_inst);
1077 type_argc = method_inst->type_argc;
1078 g_assert (type_argc > 0);
1083 index = lookup_or_register_other_info (class, type_argc, data, info_type, generic_context);
1085 //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
1088 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
1090 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
1094 * mono_class_rgctx_get_array_size:
1095 * @n: The number of the array
1096 * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
1098 * Returns the number of slots in the n'th array of a (M)RGCTX. That
1099 * number includes the slot for linking and - for MRGCTXs - the two
1100 * slots in the first array for additional information.
1103 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
1105 g_assert (n >= 0 && n < 30);
1114 * LOCKING: domain lock
1117 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
1119 static gboolean inited = FALSE;
1120 static int rgctx_num_alloced = 0;
1121 static int rgctx_bytes_alloced = 0;
1122 static int mrgctx_num_alloced = 0;
1123 static int mrgctx_bytes_alloced = 0;
1125 int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
1126 gpointer array = mono_domain_alloc0 (domain, size);
1129 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
1130 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
1131 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
1132 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
1137 mrgctx_num_alloced++;
1138 mrgctx_bytes_alloced += size;
1140 rgctx_num_alloced++;
1141 rgctx_bytes_alloced += size;
1148 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
1149 MonoGenericInst *method_inst)
1152 int i, first_slot, size;
1153 MonoDomain *domain = class_vtable->domain;
1154 MonoClass *class = class_vtable->klass;
1155 MonoGenericContext *class_context = class->generic_class ? &class->generic_class->context : NULL;
1156 MonoRuntimeGenericContextOtherInfoTemplate oti;
1157 MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
1163 mono_domain_lock (domain);
1165 /* First check whether that slot isn't already instantiated.
1166 This might happen because lookup doesn't lock. Allocate
1167 arrays on the way. */
1169 size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
1171 size -= sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
1172 for (i = 0; ; ++i) {
1175 if (method_inst && i == 0)
1176 offset = sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
1180 if (slot < first_slot + size - 1) {
1181 rgctx_index = slot - first_slot + 1 + offset;
1182 info = rgctx [rgctx_index];
1184 mono_domain_unlock (domain);
1189 if (!rgctx [offset + 0])
1190 rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
1191 rgctx = rgctx [offset + 0];
1192 first_slot += size - 1;
1193 size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
1196 g_assert (!rgctx [rgctx_index]);
1198 mono_domain_unlock (domain);
1200 oti = class_get_rgctx_template_oti (class_uninstantiated (class),
1201 method_inst ? method_inst->type_argc : 0, slot, TRUE, &do_free);
1202 /* This might take the loader lock */
1203 info = instantiate_other_info (domain, &oti, &context, class);
1207 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
1210 mono_domain_lock (domain);
1212 /* Check whether the slot hasn't been instantiated in the
1214 if (rgctx [rgctx_index])
1215 info = rgctx [rgctx_index];
1217 rgctx [rgctx_index] = info;
1219 mono_domain_unlock (domain);
1222 free_inflated_info (oti.info_type, oti.data);
1228 * mono_class_fill_runtime_generic_context:
1229 * @class_vtable: a vtable
1230 * @slot: a slot index to be instantiated
1232 * Instantiates a slot in the RGCTX.
1235 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot)
1237 static gboolean inited = FALSE;
1238 static int num_alloced = 0;
1240 MonoDomain *domain = class_vtable->domain;
1241 MonoRuntimeGenericContext *rgctx;
1244 mono_domain_lock (domain);
1247 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
1251 rgctx = class_vtable->runtime_generic_context;
1253 rgctx = alloc_rgctx_array (domain, 0, FALSE);
1254 class_vtable->runtime_generic_context = rgctx;
1258 mono_domain_unlock (domain);
1260 info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
1266 * mono_method_fill_runtime_generic_context:
1267 * @mrgctx: an MRGCTX
1268 * @slot: a slot index to be instantiated
1270 * Instantiates a slot in the MRGCTX.
1273 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot)
1277 info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot,
1278 mrgctx->method_inst);
1284 mrgctx_hash_func (gconstpointer key)
1286 const MonoMethodRuntimeGenericContext *mrgctx = key;
1288 return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
1292 mrgctx_equal_func (gconstpointer a, gconstpointer b)
1294 const MonoMethodRuntimeGenericContext *mrgctx1 = a;
1295 const MonoMethodRuntimeGenericContext *mrgctx2 = b;
1297 return mrgctx1->class_vtable == mrgctx2->class_vtable &&
1298 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
1302 * mono_method_lookup_rgctx:
1303 * @class_vtable: a vtable
1304 * @method_inst: the method inst of a generic method
1306 * Returns the MRGCTX for the generic method(s) with the given
1307 * method_inst of the given class_vtable.
1309 MonoMethodRuntimeGenericContext*
1310 mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
1312 MonoDomain *domain = class_vtable->domain;
1313 MonoMethodRuntimeGenericContext *mrgctx;
1314 MonoMethodRuntimeGenericContext key;
1316 g_assert (!class_vtable->klass->generic_container);
1317 g_assert (!method_inst->is_open);
1319 mono_domain_lock (domain);
1320 if (!domain->method_rgctx_hash)
1321 domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
1323 key.class_vtable = class_vtable;
1324 key.method_inst = method_inst;
1326 mrgctx = g_hash_table_lookup (domain->method_rgctx_hash, &key);
1331 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
1332 mrgctx->class_vtable = class_vtable;
1333 mrgctx->method_inst = method_inst;
1335 g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
1338 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
1339 for (i = 0; i < method_inst->type_argc; ++i)
1340 g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
1345 mono_domain_unlock (domain);
1353 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars)
1357 for (i = 0; i < inst->type_argc; ++i) {
1358 MonoType *type = inst->type_argv [i];
1361 if (MONO_TYPE_IS_REFERENCE (type))
1364 type_type = mono_type_get_type (type);
1365 if (allow_type_vars && (type_type == MONO_TYPE_VAR || type_type == MONO_TYPE_MVAR))
1375 * mono_generic_context_is_sharable:
1376 * @context: a generic context
1378 * Returns whether the generic context is sharable. A generic context
1379 * is sharable iff all of its type arguments are reference type.
1382 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
1384 g_assert (context->class_inst || context->method_inst);
1386 if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars))
1389 if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars))
1396 * mono_method_is_generic_impl:
1399 * Returns whether the method is either generic or part of a generic
1403 mono_method_is_generic_impl (MonoMethod *method)
1405 if (method->is_inflated) {
1406 g_assert (method->wrapper_type == MONO_WRAPPER_NONE);
1409 /* We don't treat wrappers as generic code, i.e., we never
1410 apply generic sharing to them. This is especially
1411 important for static rgctx invoke wrappers, which only work
1412 if not compiled with sharing. */
1413 if (method->wrapper_type != MONO_WRAPPER_NONE)
1415 if (method->klass->generic_container)
1421 has_constraints (MonoGenericContainer *container)
1427 g_assert (container->type_argc > 0);
1428 g_assert (container->type_params);
1430 for (i = 0; i < container->type_argc; ++i)
1431 if (container->type_params [i].constraints)
1438 * mono_method_is_generic_sharable_impl:
1440 * @allow_type_vars: whether to regard type variables as reference types
1442 * Returns TRUE iff the method is inflated or part of an inflated
1443 * class, its context is sharable and it has no constraints on its
1444 * type parameters. Otherwise returns FALSE.
1447 mono_method_is_generic_sharable_impl (MonoMethod *method, gboolean allow_type_vars)
1449 if (!mono_method_is_generic_impl (method))
1452 if (method->is_inflated) {
1453 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
1454 MonoGenericContext *context = &inflated->context;
1456 if (!mono_generic_context_is_sharable (context, allow_type_vars))
1459 g_assert (inflated->declaring);
1461 if (inflated->declaring->is_generic) {
1462 if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
1467 if (method->klass->generic_class) {
1468 if (!mono_generic_context_is_sharable (&method->klass->generic_class->context, allow_type_vars))
1471 g_assert (method->klass->generic_class->container_class &&
1472 method->klass->generic_class->container_class->generic_container);
1474 if (has_constraints (method->klass->generic_class->container_class->generic_container))
1478 if (method->klass->generic_container && !allow_type_vars)
1485 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
1487 if (!mono_class_generic_sharing_enabled (method->klass))
1490 if (!mono_method_is_generic_sharable_impl (method, allow_type_vars))
1493 if (method->is_inflated && mono_method_get_context (method)->method_inst)
1496 return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
1497 method->klass->valuetype) &&
1498 (method->klass->generic_class || method->klass->generic_container);
1501 static MonoGenericInst*
1502 get_object_generic_inst (int type_argc)
1504 MonoType **type_argv;
1507 type_argv = alloca (sizeof (MonoType*) * type_argc);
1509 for (i = 0; i < type_argc; ++i)
1510 type_argv [i] = &mono_defaults.object_class->byval_arg;
1512 return mono_metadata_get_generic_inst (type_argc, type_argv);
1516 * mono_method_construct_object_context:
1519 * Returns a generic context for method with all type variables for
1520 * class and method instantiated with Object.
1523 mono_method_construct_object_context (MonoMethod *method)
1525 MonoGenericContext object_context;
1527 g_assert (!method->klass->generic_class);
1528 if (method->klass->generic_container) {
1529 int type_argc = method->klass->generic_container->type_argc;
1531 object_context.class_inst = get_object_generic_inst (type_argc);
1533 object_context.class_inst = NULL;
1536 if (mono_method_get_context_general (method, TRUE)->method_inst) {
1537 int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
1539 object_context.method_inst = get_object_generic_inst (type_argc);
1541 object_context.method_inst = NULL;
1544 g_assert (object_context.class_inst || object_context.method_inst);
1546 return object_context;
1550 * mono_domain_lookup_shared_generic:
1552 * @open_method: an open generic method
1554 * Looks up the jit info for method via the domain's jit code hash.
1557 mono_domain_lookup_shared_generic (MonoDomain *domain, MonoMethod *open_method)
1559 static gboolean inited = FALSE;
1560 static int lookups = 0;
1561 static int failed_lookups = 0;
1563 MonoGenericContext object_context;
1564 MonoMethod *object_method;
1567 object_context = mono_method_construct_object_context (open_method);
1568 object_method = mono_class_inflate_generic_method (open_method, &object_context);
1570 mono_domain_jit_code_hash_lock (domain);
1571 ji = mono_internal_hash_table_lookup (&domain->jit_code_hash, object_method);
1572 if (ji && !ji->has_generic_jit_info)
1574 mono_domain_jit_code_hash_unlock (domain);
1577 mono_counters_register ("Shared generic lookups", MONO_COUNTER_INT|MONO_COUNTER_GENERICS, &lookups);
1578 mono_counters_register ("Failed shared generic lookups", MONO_COUNTER_INT|MONO_COUNTER_GENERICS, &failed_lookups);