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 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: {
541 MonoMethod *method = data;
542 MonoMethod *inflated_method;
543 MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
544 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
546 mono_metadata_free_type (inflated_type);
548 mono_class_init (inflated_class);
550 g_assert (!method->wrapper_type);
552 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
553 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
554 inflated_method = mono_method_search_in_array_class (inflated_class,
555 method->name, method->signature);
557 inflated_method = mono_class_inflate_generic_method (method, context);
559 mono_class_init (inflated_method->klass);
560 g_assert (inflated_method->klass == inflated_class);
561 return inflated_method;
564 case MONO_RGCTX_INFO_CLASS_FIELD: {
565 MonoClassField *field = data;
566 MonoType *inflated_type = mono_class_inflate_generic_type (&field->parent->byval_arg, context);
567 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
568 int i = field - field->parent->fields;
569 gpointer dummy = NULL;
571 mono_metadata_free_type (inflated_type);
573 mono_class_get_fields (inflated_class, &dummy);
574 g_assert (inflated_class->fields);
576 return &inflated_class->fields [i];
580 g_assert_not_reached ();
582 /* Not reached, quiet compiler */
587 inflate_other_info (MonoRuntimeGenericContextOtherInfoTemplate *oti,
588 MonoGenericContext *context, MonoClass *class, gboolean temporary)
590 return inflate_other_data (oti->data, oti->info_type, context, class, temporary);
594 free_inflated_info (int info_type, gpointer info)
600 case MONO_RGCTX_INFO_STATIC_DATA:
601 case MONO_RGCTX_INFO_KLASS:
602 case MONO_RGCTX_INFO_VTABLE:
603 case MONO_RGCTX_INFO_TYPE:
604 case MONO_RGCTX_INFO_REFLECTION_TYPE:
605 mono_metadata_free_type (info);
612 static MonoRuntimeGenericContextOtherInfoTemplate
613 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free);
616 * mono_class_get_runtime_generic_context_template:
619 * Looks up or constructs, if necessary, the runtime generic context
622 static MonoRuntimeGenericContextTemplate*
623 mono_class_get_runtime_generic_context_template (MonoClass *class)
625 MonoRuntimeGenericContextTemplate *parent_template, *template;
626 MonoGenericInst *inst;
629 g_assert (!class->generic_class);
632 template = class_lookup_rgctx_template (class);
638 if (class->generic_container)
639 inst = class->generic_container->context.class_inst;
643 template = alloc_template (class);
648 if (class->parent->generic_class) {
650 int max_argc, type_argc;
652 parent_template = mono_class_get_runtime_generic_context_template
653 (class->parent->generic_class->container_class);
655 max_argc = template_get_max_argc (parent_template);
657 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
658 num_entries = rgctx_template_num_other_infos (parent_template, type_argc);
660 /* FIXME: quadratic! */
661 for (i = 0; i < num_entries; ++i) {
662 MonoRuntimeGenericContextOtherInfoTemplate oti;
664 oti = class_get_rgctx_template_oti (class->parent, type_argc, i, FALSE, NULL);
665 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
666 rgctx_template_set_other_slot (class->image, template, type_argc, i,
667 oti.data, oti.info_type);
672 MonoRuntimeGenericContextOtherInfoTemplate *oti;
673 int max_argc, type_argc;
675 parent_template = mono_class_get_runtime_generic_context_template (class->parent);
677 max_argc = template_get_max_argc (parent_template);
679 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
680 /* FIXME: quadratic! */
681 for (i = 0, oti = parent_template->other_infos; oti; ++i, oti = oti->next) {
682 if (oti->data && oti->data != MONO_RGCTX_SLOT_USED_MARKER) {
683 rgctx_template_set_other_slot (class->image, template, type_argc, i,
684 oti->data, oti->info_type);
691 if (class_lookup_rgctx_template (class)) {
692 /* some other thread already set the template */
693 template = class_lookup_rgctx_template (class);
695 class_set_rgctx_template (class, template);
698 register_generic_subclass (class);
707 * temporary signifies whether the inflated info (oti.data) will be
708 * used temporarily, in which case it might be heap-allocated, or
709 * permanently, in which case it will be mempool-allocated. If
710 * temporary is set then *do_free will return whether the returned
711 * data must be freed.
713 static MonoRuntimeGenericContextOtherInfoTemplate
714 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free)
716 g_assert ((temporary && do_free) || (!temporary && !do_free));
718 if (class->generic_class) {
719 MonoRuntimeGenericContextOtherInfoTemplate oti;
720 gboolean tmp_do_free;
722 oti = class_get_rgctx_template_oti (class->generic_class->container_class,
723 type_argc, slot, TRUE, &tmp_do_free);
725 gpointer info = oti.data;
726 oti.data = inflate_other_info (&oti, &class->generic_class->context, class, temporary);
728 free_inflated_info (oti.info_type, info);
735 MonoRuntimeGenericContextTemplate *template;
736 MonoRuntimeGenericContextOtherInfoTemplate *oti;
738 template = mono_class_get_runtime_generic_context_template (class);
739 oti = rgctx_template_get_other_slot (template, type_argc, slot);
750 class_uninstantiated (MonoClass *class)
752 if (class->generic_class)
753 return class->generic_class->container_class;
758 class_type_info (MonoDomain *domain, MonoClass *class, int info_type)
761 case MONO_RGCTX_INFO_STATIC_DATA:
762 return mono_class_vtable (domain, class)->data;
763 case MONO_RGCTX_INFO_KLASS:
765 case MONO_RGCTX_INFO_VTABLE:
766 return mono_class_vtable (domain, class);
768 g_assert_not_reached ();
775 instantiate_other_info (MonoDomain *domain, MonoRuntimeGenericContextOtherInfoTemplate *oti,
776 MonoGenericContext *context, MonoClass *class)
784 switch (oti->info_type) {
785 case MONO_RGCTX_INFO_STATIC_DATA:
786 case MONO_RGCTX_INFO_KLASS:
787 case MONO_RGCTX_INFO_VTABLE:
794 data = inflate_other_info (oti, context, class, temporary);
796 switch (oti->info_type) {
797 case MONO_RGCTX_INFO_STATIC_DATA:
798 case MONO_RGCTX_INFO_KLASS:
799 case MONO_RGCTX_INFO_VTABLE: {
800 MonoClass *arg_class = mono_class_from_mono_type (data);
802 free_inflated_info (oti->info_type, data);
803 g_assert (arg_class);
805 return class_type_info (domain, arg_class, oti->info_type);
807 case MONO_RGCTX_INFO_TYPE:
809 case MONO_RGCTX_INFO_REFLECTION_TYPE:
810 return mono_type_get_object (domain, data);
811 case MONO_RGCTX_INFO_METHOD:
813 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
814 return mono_create_ftnptr (mono_domain_get (),
815 mono_runtime_create_jump_trampoline (mono_domain_get (), data, TRUE));
816 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
817 return mono_create_ftnptr (mono_domain_get (),
818 mono_runtime_create_jump_trampoline (mono_domain_get (),
819 mono_marshal_get_remoting_invoke_with_check (data), TRUE));
820 case MONO_RGCTX_INFO_CLASS_FIELD:
822 case MONO_RGCTX_INFO_METHOD_RGCTX: {
823 MonoMethodInflated *method = data;
825 g_assert (method->method.method.is_inflated);
826 g_assert (method->context.method_inst);
828 return mono_method_lookup_rgctx (mono_class_vtable (domain, method->method.method.klass),
829 method->context.method_inst);
831 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
832 MonoMethodInflated *method = data;
834 g_assert (method->method.method.is_inflated);
835 g_assert (method->context.method_inst);
837 return method->context.method_inst;
840 g_assert_not_reached ();
847 * LOCKING: templates lock
850 fill_in_rgctx_template_slot (MonoClass *class, int type_argc, int index, gpointer data, int info_type)
852 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
855 g_assert (!class->generic_class);
857 rgctx_template_set_other_slot (class->image, template, type_argc, index, data, info_type);
859 /* Recurse for all subclasses */
860 if (generic_subclass_hash)
861 subclass = g_hash_table_lookup (generic_subclass_hash, class);
866 MonoRuntimeGenericContextOtherInfoTemplate subclass_oti;
867 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
869 g_assert (!subclass->generic_class);
870 g_assert (subclass_template);
872 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, NULL);
873 g_assert (subclass_oti.data);
875 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
877 subclass = subclass_template->next_subclass;
882 * LOCKING: templates lock
885 register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type)
888 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
890 MonoRuntimeGenericContextOtherInfoTemplate *oti;
892 for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
897 //g_print ("template %s . other_infos [%d] = %s\n", mono_type_get_full_name (class), i, mono_type_get_full_name (other_class));
899 /* Mark the slot as used in all parent classes (until we find
900 a parent class which already has it marked used). */
901 parent = class->parent;
902 while (parent != NULL) {
903 MonoRuntimeGenericContextTemplate *parent_template;
904 MonoRuntimeGenericContextOtherInfoTemplate *oti;
906 if (parent->generic_class)
907 parent = parent->generic_class->container_class;
909 parent_template = mono_class_get_runtime_generic_context_template (parent);
910 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
912 if (oti && oti->data)
915 rgctx_template_set_other_slot (parent->image, parent_template, type_argc, i,
916 MONO_RGCTX_SLOT_USED_MARKER, 0);
918 parent = parent->parent;
921 /* Fill in the slot in this class and in all subclasses
923 fill_in_rgctx_template_slot (class, type_argc, i, data, info_type);
929 other_info_equal (gpointer data1, gpointer data2, int info_type)
932 case MONO_RGCTX_INFO_STATIC_DATA:
933 case MONO_RGCTX_INFO_KLASS:
934 case MONO_RGCTX_INFO_VTABLE:
935 case MONO_RGCTX_INFO_TYPE:
936 case MONO_RGCTX_INFO_REFLECTION_TYPE:
937 return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
938 case MONO_RGCTX_INFO_METHOD:
939 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
940 case MONO_RGCTX_INFO_CLASS_FIELD:
941 case MONO_RGCTX_INFO_METHOD_RGCTX:
942 case MONO_RGCTX_INFO_METHOD_CONTEXT:
943 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
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);