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);
557 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
558 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
559 inflated_method = mono_method_search_in_array_class (inflated_class,
560 method->name, method->signature);
562 inflated_method = mono_class_inflate_generic_method (method, context);
564 mono_class_init (inflated_method->klass);
565 g_assert (inflated_method->klass == inflated_class);
566 return inflated_method;
569 case MONO_RGCTX_INFO_CLASS_FIELD: {
570 MonoClassField *field = data;
571 MonoType *inflated_type = mono_class_inflate_generic_type (&field->parent->byval_arg, context);
572 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
573 int i = field - field->parent->fields;
574 gpointer dummy = NULL;
576 mono_metadata_free_type (inflated_type);
578 mono_class_get_fields (inflated_class, &dummy);
579 g_assert (inflated_class->fields);
581 return &inflated_class->fields [i];
585 g_assert_not_reached ();
587 /* Not reached, quiet compiler */
592 inflate_other_info (MonoRuntimeGenericContextOtherInfoTemplate *oti,
593 MonoGenericContext *context, MonoClass *class, gboolean temporary)
595 return inflate_other_data (oti->data, oti->info_type, context, class, temporary);
599 free_inflated_info (int info_type, gpointer info)
605 case MONO_RGCTX_INFO_STATIC_DATA:
606 case MONO_RGCTX_INFO_KLASS:
607 case MONO_RGCTX_INFO_VTABLE:
608 case MONO_RGCTX_INFO_TYPE:
609 case MONO_RGCTX_INFO_REFLECTION_TYPE:
610 mono_metadata_free_type (info);
617 static MonoRuntimeGenericContextOtherInfoTemplate
618 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free);
621 * mono_class_get_runtime_generic_context_template:
624 * Looks up or constructs, if necessary, the runtime generic context
627 static MonoRuntimeGenericContextTemplate*
628 mono_class_get_runtime_generic_context_template (MonoClass *class)
630 MonoRuntimeGenericContextTemplate *parent_template, *template;
631 MonoGenericInst *inst;
634 g_assert (!class->generic_class);
637 template = class_lookup_rgctx_template (class);
643 if (class->generic_container)
644 inst = class->generic_container->context.class_inst;
648 template = alloc_template (class);
653 if (class->parent->generic_class) {
655 int max_argc, type_argc;
657 parent_template = mono_class_get_runtime_generic_context_template
658 (class->parent->generic_class->container_class);
660 max_argc = template_get_max_argc (parent_template);
662 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
663 num_entries = rgctx_template_num_other_infos (parent_template, type_argc);
665 /* FIXME: quadratic! */
666 for (i = 0; i < num_entries; ++i) {
667 MonoRuntimeGenericContextOtherInfoTemplate oti;
669 oti = class_get_rgctx_template_oti (class->parent, type_argc, i, FALSE, NULL);
670 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
671 rgctx_template_set_other_slot (class->image, template, type_argc, i,
672 oti.data, oti.info_type);
677 MonoRuntimeGenericContextOtherInfoTemplate *oti;
678 int max_argc, type_argc;
680 parent_template = mono_class_get_runtime_generic_context_template (class->parent);
682 max_argc = template_get_max_argc (parent_template);
684 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
685 /* FIXME: quadratic! */
686 for (i = 0, oti = parent_template->other_infos; oti; ++i, oti = oti->next) {
687 if (oti->data && oti->data != MONO_RGCTX_SLOT_USED_MARKER) {
688 rgctx_template_set_other_slot (class->image, template, type_argc, i,
689 oti->data, oti->info_type);
696 if (class_lookup_rgctx_template (class)) {
697 /* some other thread already set the template */
698 template = class_lookup_rgctx_template (class);
700 class_set_rgctx_template (class, template);
703 register_generic_subclass (class);
712 * temporary signifies whether the inflated info (oti.data) will be
713 * used temporarily, in which case it might be heap-allocated, or
714 * permanently, in which case it will be mempool-allocated. If
715 * temporary is set then *do_free will return whether the returned
716 * data must be freed.
718 static MonoRuntimeGenericContextOtherInfoTemplate
719 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free)
721 g_assert ((temporary && do_free) || (!temporary && !do_free));
723 if (class->generic_class) {
724 MonoRuntimeGenericContextOtherInfoTemplate oti;
725 gboolean tmp_do_free;
727 oti = class_get_rgctx_template_oti (class->generic_class->container_class,
728 type_argc, slot, TRUE, &tmp_do_free);
730 gpointer info = oti.data;
731 oti.data = inflate_other_info (&oti, &class->generic_class->context, class, temporary);
733 free_inflated_info (oti.info_type, info);
740 MonoRuntimeGenericContextTemplate *template;
741 MonoRuntimeGenericContextOtherInfoTemplate *oti;
743 template = mono_class_get_runtime_generic_context_template (class);
744 oti = rgctx_template_get_other_slot (template, type_argc, slot);
755 class_uninstantiated (MonoClass *class)
757 if (class->generic_class)
758 return class->generic_class->container_class;
763 class_type_info (MonoDomain *domain, MonoClass *class, int info_type)
766 case MONO_RGCTX_INFO_STATIC_DATA:
767 return mono_class_vtable (domain, class)->data;
768 case MONO_RGCTX_INFO_KLASS:
770 case MONO_RGCTX_INFO_VTABLE:
771 return mono_class_vtable (domain, class);
773 g_assert_not_reached ();
780 instantiate_other_info (MonoDomain *domain, MonoRuntimeGenericContextOtherInfoTemplate *oti,
781 MonoGenericContext *context, MonoClass *class)
789 switch (oti->info_type) {
790 case MONO_RGCTX_INFO_STATIC_DATA:
791 case MONO_RGCTX_INFO_KLASS:
792 case MONO_RGCTX_INFO_VTABLE:
799 data = inflate_other_info (oti, context, class, temporary);
801 switch (oti->info_type) {
802 case MONO_RGCTX_INFO_STATIC_DATA:
803 case MONO_RGCTX_INFO_KLASS:
804 case MONO_RGCTX_INFO_VTABLE: {
805 MonoClass *arg_class = mono_class_from_mono_type (data);
807 free_inflated_info (oti->info_type, data);
808 g_assert (arg_class);
810 return class_type_info (domain, arg_class, oti->info_type);
812 case MONO_RGCTX_INFO_TYPE:
814 case MONO_RGCTX_INFO_REFLECTION_TYPE:
815 return mono_type_get_object (domain, data);
816 case MONO_RGCTX_INFO_METHOD:
818 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
819 return mono_create_ftnptr (mono_domain_get (),
820 mono_runtime_create_jump_trampoline (mono_domain_get (), data, TRUE));
821 case MONO_RGCTX_INFO_CLASS_FIELD:
823 case MONO_RGCTX_INFO_METHOD_RGCTX: {
824 MonoMethodInflated *method = data;
826 g_assert (method->method.method.is_inflated);
827 g_assert (method->context.method_inst);
829 return mono_method_lookup_rgctx (mono_class_vtable (domain, method->method.method.klass),
830 method->context.method_inst);
832 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
833 MonoMethodInflated *method = data;
835 g_assert (method->method.method.is_inflated);
836 g_assert (method->context.method_inst);
838 return method->context.method_inst;
841 g_assert_not_reached ();
848 * LOCKING: templates lock
851 fill_in_rgctx_template_slot (MonoClass *class, int type_argc, int index, gpointer data, int info_type)
853 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
856 g_assert (!class->generic_class);
858 rgctx_template_set_other_slot (class->image, template, type_argc, index, data, info_type);
860 /* Recurse for all subclasses */
861 if (generic_subclass_hash)
862 subclass = g_hash_table_lookup (generic_subclass_hash, class);
867 MonoRuntimeGenericContextOtherInfoTemplate subclass_oti;
868 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
870 g_assert (!subclass->generic_class);
871 g_assert (subclass_template);
873 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, NULL);
874 g_assert (subclass_oti.data);
876 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
878 subclass = subclass_template->next_subclass;
883 * LOCKING: templates lock
886 register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type)
889 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
891 MonoRuntimeGenericContextOtherInfoTemplate *oti;
893 for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
898 //g_print ("template %s . other_infos [%d] = %s\n", mono_type_get_full_name (class), i, mono_type_get_full_name (other_class));
900 /* Mark the slot as used in all parent classes (until we find
901 a parent class which already has it marked used). */
902 parent = class->parent;
903 while (parent != NULL) {
904 MonoRuntimeGenericContextTemplate *parent_template;
905 MonoRuntimeGenericContextOtherInfoTemplate *oti;
907 if (parent->generic_class)
908 parent = parent->generic_class->container_class;
910 parent_template = mono_class_get_runtime_generic_context_template (parent);
911 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
913 if (oti && oti->data)
916 rgctx_template_set_other_slot (parent->image, parent_template, type_argc, i,
917 MONO_RGCTX_SLOT_USED_MARKER, 0);
919 parent = parent->parent;
922 /* Fill in the slot in this class and in all subclasses
924 fill_in_rgctx_template_slot (class, type_argc, i, data, info_type);
930 other_info_equal (gpointer data1, gpointer data2, int info_type)
933 case MONO_RGCTX_INFO_STATIC_DATA:
934 case MONO_RGCTX_INFO_KLASS:
935 case MONO_RGCTX_INFO_VTABLE:
936 case MONO_RGCTX_INFO_TYPE:
937 case MONO_RGCTX_INFO_REFLECTION_TYPE:
938 return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
939 case MONO_RGCTX_INFO_METHOD:
940 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
941 case MONO_RGCTX_INFO_CLASS_FIELD:
942 case MONO_RGCTX_INFO_METHOD_RGCTX:
943 case MONO_RGCTX_INFO_METHOD_CONTEXT:
944 return data1 == data2;
946 g_assert_not_reached ();
953 lookup_or_register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type,
954 MonoGenericContext *generic_context)
956 static gboolean inited = FALSE;
957 static int max_slot = 0;
959 MonoRuntimeGenericContextTemplate *rgctx_template =
960 mono_class_get_runtime_generic_context_template (class);
961 MonoRuntimeGenericContextOtherInfoTemplate *oti_list, *oti, *copy;
964 g_assert (!class->generic_class);
965 g_assert (class->generic_container || type_argc);
968 * We must not call inflate_other_info() with the templates
969 * lock held, because it calls metadata functions which might
970 * cause the loader lock to be taken, which must not happen if
971 * the templates lock is held.
973 * Only two things can happen to an oti list: An unused
974 * (data==NULL) node can be filled in and nodes can be
975 * appended at the end of the list.
977 * To solve the lock problem we first count the number of
978 * nodes in the list, then copy all the data into a separate
979 * array. With the templates lock not held we then search for
980 * our info in the array - this is where the calls to
981 * inflate_other_info() happen. If we don't find the info
982 * we're looking for, we take the templates lock again and
983 * check if the oti list has changed since we've copied it.
984 * If it has, we start again. If it hasn't, we register the
991 oti_list = get_other_info_templates (rgctx_template, type_argc);
994 for (oti = oti_list; oti; oti = oti->next)
997 copy = g_new (MonoRuntimeGenericContextOtherInfoTemplate, length);
999 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
1000 copy [i].info_type = oti->info_type;
1001 copy [i].data = oti->data;
1003 g_assert (i == length);
1005 templates_unlock ();
1007 /* We've copied the list. Now look for the info. */
1009 for (i = 0; i < length; ++i) {
1010 gpointer inflated_data;
1012 if (copy [i].info_type != info_type || !copy [i].data)
1015 inflated_data = inflate_other_info (© [i], generic_context, class, TRUE);
1017 if (other_info_equal (data, inflated_data, info_type)) {
1018 free_inflated_info (info_type, inflated_data);
1022 free_inflated_info (info_type, inflated_data);
1025 /* We haven't found the info, so check if the list is still
1030 /* We need to fetch oti_list again here because the list could
1032 oti_list = get_other_info_templates (rgctx_template, type_argc);
1034 for (oti = oti_list, i = 0; i < length; oti = oti->next, ++i) {
1037 if (copy [i].info_type != oti->info_type || copy [i].data != oti->data) {
1046 /* The list is still the same - success. */
1048 i = register_other_info (class, type_argc, data, info_type);
1050 templates_unlock ();
1053 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
1063 * mono_method_lookup_or_register_other_info:
1065 * @in_mrgctx: whether to put the data into the MRGCTX
1066 * @data: the info data
1067 * @info_type: the type of info to register about data
1068 * @generic_context: a generic context
1070 * Looks up and, if necessary, adds information about other_class in
1071 * method's or method's class runtime generic context. Returns the
1072 * encoded slot number.
1075 mono_method_lookup_or_register_other_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
1076 int info_type, MonoGenericContext *generic_context)
1078 MonoClass *class = method->klass;
1079 int type_argc, index;
1082 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
1084 g_assert (method->is_inflated && method_inst);
1085 type_argc = method_inst->type_argc;
1086 g_assert (type_argc > 0);
1091 index = lookup_or_register_other_info (class, type_argc, data, info_type, generic_context);
1093 //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
1096 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
1098 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
1102 * mono_class_rgctx_get_array_size:
1103 * @n: The number of the array
1104 * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
1106 * Returns the number of slots in the n'th array of a (M)RGCTX. That
1107 * number includes the slot for linking and - for MRGCTXs - the two
1108 * slots in the first array for additional information.
1111 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
1113 g_assert (n >= 0 && n < 30);
1122 * LOCKING: domain lock
1125 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
1127 static gboolean inited = FALSE;
1128 static int rgctx_num_alloced = 0;
1129 static int rgctx_bytes_alloced = 0;
1130 static int mrgctx_num_alloced = 0;
1131 static int mrgctx_bytes_alloced = 0;
1133 int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
1134 gpointer array = mono_domain_alloc0 (domain, size);
1137 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
1138 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
1139 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
1140 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
1145 mrgctx_num_alloced++;
1146 mrgctx_bytes_alloced += size;
1148 rgctx_num_alloced++;
1149 rgctx_bytes_alloced += size;
1156 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
1157 MonoGenericInst *method_inst)
1160 int i, first_slot, size;
1161 MonoDomain *domain = class_vtable->domain;
1162 MonoClass *class = class_vtable->klass;
1163 MonoGenericContext *class_context = class->generic_class ? &class->generic_class->context : NULL;
1164 MonoRuntimeGenericContextOtherInfoTemplate oti;
1165 MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
1171 mono_domain_lock (domain);
1173 /* First check whether that slot isn't already instantiated.
1174 This might happen because lookup doesn't lock. Allocate
1175 arrays on the way. */
1177 size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
1179 size -= sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
1180 for (i = 0; ; ++i) {
1183 if (method_inst && i == 0)
1184 offset = sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
1188 if (slot < first_slot + size - 1) {
1189 rgctx_index = slot - first_slot + 1 + offset;
1190 info = rgctx [rgctx_index];
1192 mono_domain_unlock (domain);
1197 if (!rgctx [offset + 0])
1198 rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
1199 rgctx = rgctx [offset + 0];
1200 first_slot += size - 1;
1201 size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
1204 g_assert (!rgctx [rgctx_index]);
1206 mono_domain_unlock (domain);
1208 oti = class_get_rgctx_template_oti (class_uninstantiated (class),
1209 method_inst ? method_inst->type_argc : 0, slot, TRUE, &do_free);
1210 /* This might take the loader lock */
1211 info = instantiate_other_info (domain, &oti, &context, class);
1215 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
1218 /*FIXME We should use CAS here, no need to take a lock.*/
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)
1286 info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot,
1287 mrgctx->method_inst);
1293 mrgctx_hash_func (gconstpointer key)
1295 const MonoMethodRuntimeGenericContext *mrgctx = key;
1297 return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
1301 mrgctx_equal_func (gconstpointer a, gconstpointer b)
1303 const MonoMethodRuntimeGenericContext *mrgctx1 = a;
1304 const MonoMethodRuntimeGenericContext *mrgctx2 = b;
1306 return mrgctx1->class_vtable == mrgctx2->class_vtable &&
1307 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
1311 * mono_method_lookup_rgctx:
1312 * @class_vtable: a vtable
1313 * @method_inst: the method inst of a generic method
1315 * Returns the MRGCTX for the generic method(s) with the given
1316 * method_inst of the given class_vtable.
1318 * LOCKING: Take the domain lock.
1320 MonoMethodRuntimeGenericContext*
1321 mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
1323 MonoDomain *domain = class_vtable->domain;
1324 MonoMethodRuntimeGenericContext *mrgctx;
1325 MonoMethodRuntimeGenericContext key;
1327 g_assert (!class_vtable->klass->generic_container);
1328 g_assert (!method_inst->is_open);
1330 mono_domain_lock (domain);
1331 if (!domain->method_rgctx_hash)
1332 domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
1334 key.class_vtable = class_vtable;
1335 key.method_inst = method_inst;
1337 mrgctx = g_hash_table_lookup (domain->method_rgctx_hash, &key);
1342 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
1343 mrgctx->class_vtable = class_vtable;
1344 mrgctx->method_inst = method_inst;
1346 g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
1349 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
1350 for (i = 0; i < method_inst->type_argc; ++i)
1351 g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
1356 mono_domain_unlock (domain);
1364 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars)
1368 for (i = 0; i < inst->type_argc; ++i) {
1369 MonoType *type = inst->type_argv [i];
1372 if (MONO_TYPE_IS_REFERENCE (type))
1375 type_type = mono_type_get_type (type);
1376 if (allow_type_vars && (type_type == MONO_TYPE_VAR || type_type == MONO_TYPE_MVAR))
1386 * mono_generic_context_is_sharable:
1387 * @context: a generic context
1389 * Returns whether the generic context is sharable. A generic context
1390 * is sharable iff all of its type arguments are reference type.
1393 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
1395 g_assert (context->class_inst || context->method_inst);
1397 if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars))
1400 if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars))
1407 * mono_method_is_generic_impl:
1410 * Returns whether the method is either generic or part of a generic
1414 mono_method_is_generic_impl (MonoMethod *method)
1416 if (method->is_inflated) {
1417 g_assert (method->wrapper_type == MONO_WRAPPER_NONE);
1420 /* We don't treat wrappers as generic code, i.e., we never
1421 apply generic sharing to them. This is especially
1422 important for static rgctx invoke wrappers, which only work
1423 if not compiled with sharing. */
1424 if (method->wrapper_type != MONO_WRAPPER_NONE)
1426 if (method->klass->generic_container)
1432 has_constraints (MonoGenericContainer *container)
1438 g_assert (container->type_argc > 0);
1439 g_assert (container->type_params);
1441 for (i = 0; i < container->type_argc; ++i)
1442 if (container->type_params [i].constraints)
1449 * mono_method_is_generic_sharable_impl:
1451 * @allow_type_vars: whether to regard type variables as reference types
1453 * Returns TRUE iff the method is inflated or part of an inflated
1454 * class, its context is sharable and it has no constraints on its
1455 * type parameters. Otherwise returns FALSE.
1458 mono_method_is_generic_sharable_impl (MonoMethod *method, gboolean allow_type_vars)
1460 if (!mono_method_is_generic_impl (method))
1463 if (method->is_inflated) {
1464 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
1465 MonoGenericContext *context = &inflated->context;
1467 if (!mono_generic_context_is_sharable (context, allow_type_vars))
1470 g_assert (inflated->declaring);
1472 if (inflated->declaring->is_generic) {
1473 if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
1478 if (method->klass->generic_class) {
1479 if (!mono_generic_context_is_sharable (&method->klass->generic_class->context, allow_type_vars))
1482 g_assert (method->klass->generic_class->container_class &&
1483 method->klass->generic_class->container_class->generic_container);
1485 if (has_constraints (method->klass->generic_class->container_class->generic_container))
1489 if (method->klass->generic_container && !allow_type_vars)
1496 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
1498 if (!mono_class_generic_sharing_enabled (method->klass))
1501 if (!mono_method_is_generic_sharable_impl (method, allow_type_vars))
1504 if (method->is_inflated && mono_method_get_context (method)->method_inst)
1507 return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
1508 method->klass->valuetype) &&
1509 (method->klass->generic_class || method->klass->generic_container);
1512 static MonoGenericInst*
1513 get_object_generic_inst (int type_argc)
1515 MonoType **type_argv;
1518 type_argv = alloca (sizeof (MonoType*) * type_argc);
1520 for (i = 0; i < type_argc; ++i)
1521 type_argv [i] = &mono_defaults.object_class->byval_arg;
1523 return mono_metadata_get_generic_inst (type_argc, type_argv);
1527 * mono_method_construct_object_context:
1530 * Returns a generic context for method with all type variables for
1531 * class and method instantiated with Object.
1534 mono_method_construct_object_context (MonoMethod *method)
1536 MonoGenericContext object_context;
1538 g_assert (!method->klass->generic_class);
1539 if (method->klass->generic_container) {
1540 int type_argc = method->klass->generic_container->type_argc;
1542 object_context.class_inst = get_object_generic_inst (type_argc);
1544 object_context.class_inst = NULL;
1547 if (mono_method_get_context_general (method, TRUE)->method_inst) {
1548 int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
1550 object_context.method_inst = get_object_generic_inst (type_argc);
1552 object_context.method_inst = NULL;
1555 g_assert (object_context.class_inst || object_context.method_inst);
1557 return object_context;
1561 * mono_domain_lookup_shared_generic:
1563 * @open_method: an open generic method
1565 * Looks up the jit info for method via the domain's jit code hash.
1568 mono_domain_lookup_shared_generic (MonoDomain *domain, MonoMethod *open_method)
1570 static gboolean inited = FALSE;
1571 static int lookups = 0;
1572 static int failed_lookups = 0;
1574 MonoGenericContext object_context;
1575 MonoMethod *object_method;
1578 object_context = mono_method_construct_object_context (open_method);
1579 object_method = mono_class_inflate_generic_method (open_method, &object_context);
1581 mono_domain_jit_code_hash_lock (domain);
1582 ji = mono_internal_hash_table_lookup (&domain->jit_code_hash, object_method);
1583 if (ji && !ji->has_generic_jit_info)
1585 mono_domain_jit_code_hash_unlock (domain);
1588 mono_counters_register ("Shared generic lookups", MONO_COUNTER_INT|MONO_COUNTER_GENERICS, &lookups);
1589 mono_counters_register ("Failed shared generic lookups", MONO_COUNTER_INT|MONO_COUNTER_GENERICS, &failed_lookups);