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 g_assert (!method->wrapper_type);
551 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
552 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
553 inflated_method = mono_method_search_in_array_class (inflated_class,
554 method->name, method->signature);
556 inflated_method = mono_class_inflate_generic_method (method, context);
558 mono_class_init (inflated_method->klass);
559 g_assert (inflated_method->klass == inflated_class);
560 return inflated_method;
563 case MONO_RGCTX_INFO_CLASS_FIELD: {
564 MonoClassField *field = data;
565 MonoType *inflated_type = mono_class_inflate_generic_type (&field->parent->byval_arg, context);
566 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
567 int i = field - field->parent->fields;
568 gpointer dummy = NULL;
570 mono_metadata_free_type (inflated_type);
572 mono_class_get_fields (inflated_class, &dummy);
573 g_assert (inflated_class->fields);
575 return &inflated_class->fields [i];
579 g_assert_not_reached ();
581 /* Not reached, quiet compiler */
586 inflate_other_info (MonoRuntimeGenericContextOtherInfoTemplate *oti,
587 MonoGenericContext *context, MonoClass *class, gboolean temporary)
589 return inflate_other_data (oti->data, oti->info_type, context, class, temporary);
593 free_inflated_info (int info_type, gpointer info)
599 case MONO_RGCTX_INFO_STATIC_DATA:
600 case MONO_RGCTX_INFO_KLASS:
601 case MONO_RGCTX_INFO_VTABLE:
602 case MONO_RGCTX_INFO_TYPE:
603 case MONO_RGCTX_INFO_REFLECTION_TYPE:
604 mono_metadata_free_type (info);
611 static MonoRuntimeGenericContextOtherInfoTemplate
612 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free);
615 * mono_class_get_runtime_generic_context_template:
618 * Looks up or constructs, if necessary, the runtime generic context
621 static MonoRuntimeGenericContextTemplate*
622 mono_class_get_runtime_generic_context_template (MonoClass *class)
624 MonoRuntimeGenericContextTemplate *parent_template, *template;
625 MonoGenericInst *inst;
628 g_assert (!class->generic_class);
631 template = class_lookup_rgctx_template (class);
637 if (class->generic_container)
638 inst = class->generic_container->context.class_inst;
642 template = alloc_template (class);
647 if (class->parent->generic_class) {
649 int max_argc, type_argc;
651 parent_template = mono_class_get_runtime_generic_context_template
652 (class->parent->generic_class->container_class);
654 max_argc = template_get_max_argc (parent_template);
656 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
657 num_entries = rgctx_template_num_other_infos (parent_template, type_argc);
659 /* FIXME: quadratic! */
660 for (i = 0; i < num_entries; ++i) {
661 MonoRuntimeGenericContextOtherInfoTemplate oti;
663 oti = class_get_rgctx_template_oti (class->parent, type_argc, i, FALSE, NULL);
664 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
665 rgctx_template_set_other_slot (class->image, template, type_argc, i,
666 oti.data, oti.info_type);
671 MonoRuntimeGenericContextOtherInfoTemplate *oti;
672 int max_argc, type_argc;
674 parent_template = mono_class_get_runtime_generic_context_template (class->parent);
676 max_argc = template_get_max_argc (parent_template);
678 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
679 /* FIXME: quadratic! */
680 for (i = 0, oti = parent_template->other_infos; oti; ++i, oti = oti->next) {
681 if (oti->data && oti->data != MONO_RGCTX_SLOT_USED_MARKER) {
682 rgctx_template_set_other_slot (class->image, template, type_argc, i,
683 oti->data, oti->info_type);
690 if (class_lookup_rgctx_template (class)) {
691 /* some other thread already set the template */
692 template = class_lookup_rgctx_template (class);
694 class_set_rgctx_template (class, template);
697 register_generic_subclass (class);
706 * temporary signifies whether the inflated info (oti.data) will be
707 * used temporarily, in which case it might be heap-allocated, or
708 * permanently, in which case it will be mempool-allocated. If
709 * temporary is set then *do_free will return whether the returned
710 * data must be freed.
712 static MonoRuntimeGenericContextOtherInfoTemplate
713 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free)
715 g_assert ((temporary && do_free) || (!temporary && !do_free));
717 if (class->generic_class) {
718 MonoRuntimeGenericContextOtherInfoTemplate oti;
719 gboolean tmp_do_free;
721 oti = class_get_rgctx_template_oti (class->generic_class->container_class,
722 type_argc, slot, TRUE, &tmp_do_free);
724 gpointer info = oti.data;
725 oti.data = inflate_other_info (&oti, &class->generic_class->context, class, temporary);
727 free_inflated_info (oti.info_type, info);
734 MonoRuntimeGenericContextTemplate *template;
735 MonoRuntimeGenericContextOtherInfoTemplate *oti;
737 template = mono_class_get_runtime_generic_context_template (class);
738 oti = rgctx_template_get_other_slot (template, type_argc, slot);
749 class_uninstantiated (MonoClass *class)
751 if (class->generic_class)
752 return class->generic_class->container_class;
757 class_type_info (MonoDomain *domain, MonoClass *class, int info_type)
760 case MONO_RGCTX_INFO_STATIC_DATA:
761 return mono_class_vtable (domain, class)->data;
762 case MONO_RGCTX_INFO_KLASS:
764 case MONO_RGCTX_INFO_VTABLE:
765 return mono_class_vtable (domain, class);
767 g_assert_not_reached ();
774 instantiate_other_info (MonoDomain *domain, MonoRuntimeGenericContextOtherInfoTemplate *oti,
775 MonoGenericContext *context, MonoClass *class)
783 switch (oti->info_type) {
784 case MONO_RGCTX_INFO_STATIC_DATA:
785 case MONO_RGCTX_INFO_KLASS:
786 case MONO_RGCTX_INFO_VTABLE:
793 data = inflate_other_info (oti, context, class, temporary);
795 switch (oti->info_type) {
796 case MONO_RGCTX_INFO_STATIC_DATA:
797 case MONO_RGCTX_INFO_KLASS:
798 case MONO_RGCTX_INFO_VTABLE: {
799 MonoClass *arg_class = mono_class_from_mono_type (data);
801 free_inflated_info (oti->info_type, data);
802 g_assert (arg_class);
804 return class_type_info (domain, arg_class, oti->info_type);
806 case MONO_RGCTX_INFO_TYPE:
808 case MONO_RGCTX_INFO_REFLECTION_TYPE:
809 return mono_type_get_object (domain, data);
810 case MONO_RGCTX_INFO_METHOD:
812 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
813 return mono_create_ftnptr (mono_domain_get (),
814 mono_runtime_create_jump_trampoline (mono_domain_get (), data, TRUE));
815 case MONO_RGCTX_INFO_CLASS_FIELD:
817 case MONO_RGCTX_INFO_METHOD_RGCTX: {
818 MonoMethodInflated *method = data;
820 g_assert (method->method.method.is_inflated);
821 g_assert (method->context.method_inst);
823 return mono_method_lookup_rgctx (mono_class_vtable (domain, method->method.method.klass),
824 method->context.method_inst);
826 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
827 MonoMethodInflated *method = data;
829 g_assert (method->method.method.is_inflated);
830 g_assert (method->context.method_inst);
832 return method->context.method_inst;
835 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 ();
947 lookup_or_register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type,
948 MonoGenericContext *generic_context)
950 static gboolean inited = FALSE;
951 static int max_slot = 0;
953 MonoRuntimeGenericContextTemplate *rgctx_template =
954 mono_class_get_runtime_generic_context_template (class);
955 MonoRuntimeGenericContextOtherInfoTemplate *oti_list, *oti, *copy;
958 g_assert (!class->generic_class);
959 g_assert (class->generic_container || type_argc);
962 * We must not call inflate_other_info() with the templates
963 * lock held, because it calls metadata functions which might
964 * cause the loader lock to be taken, which must not happen if
965 * the templates lock is held.
967 * Only two things can happen to an oti list: An unused
968 * (data==NULL) node can be filled in and nodes can be
969 * appended at the end of the list.
971 * To solve the lock problem we first count the number of
972 * nodes in the list, then copy all the data into a separate
973 * array. With the templates lock not held we then search for
974 * our info in the array - this is where the calls to
975 * inflate_other_info() happen. If we don't find the info
976 * we're looking for, we take the templates lock again and
977 * check if the oti list has changed since we've copied it.
978 * If it has, we start again. If it hasn't, we register the
985 oti_list = get_other_info_templates (rgctx_template, type_argc);
988 for (oti = oti_list; oti; oti = oti->next)
991 copy = g_new (MonoRuntimeGenericContextOtherInfoTemplate, length);
993 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
994 copy [i].info_type = oti->info_type;
995 copy [i].data = oti->data;
997 g_assert (i == length);
1001 /* We've copied the list. Now look for the info. */
1003 for (i = 0; i < length; ++i) {
1004 gpointer inflated_data;
1006 if (copy [i].info_type != info_type || !copy [i].data)
1009 inflated_data = inflate_other_info (© [i], generic_context, class, TRUE);
1011 if (other_info_equal (data, inflated_data, info_type)) {
1012 free_inflated_info (info_type, inflated_data);
1016 free_inflated_info (info_type, inflated_data);
1019 /* We haven't found the info, so check if the list is still
1024 /* We need to fetch oti_list again here because the list could
1026 oti_list = get_other_info_templates (rgctx_template, type_argc);
1028 for (oti = oti_list, i = 0; i < length; oti = oti->next, ++i) {
1031 if (copy [i].info_type != oti->info_type || copy [i].data != oti->data) {
1040 /* The list is still the same - success. */
1042 i = register_other_info (class, type_argc, data, info_type);
1044 templates_unlock ();
1047 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
1057 * mono_method_lookup_or_register_other_info:
1059 * @in_mrgctx: whether to put the data into the MRGCTX
1060 * @data: the info data
1061 * @info_type: the type of info to register about data
1062 * @generic_context: a generic context
1064 * Looks up and, if necessary, adds information about other_class in
1065 * method's or method's class runtime generic context. Returns the
1066 * encoded slot number.
1069 mono_method_lookup_or_register_other_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
1070 int info_type, MonoGenericContext *generic_context)
1072 MonoClass *class = method->klass;
1073 int type_argc, index;
1076 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
1078 g_assert (method->is_inflated && method_inst);
1079 type_argc = method_inst->type_argc;
1080 g_assert (type_argc > 0);
1085 index = lookup_or_register_other_info (class, type_argc, data, info_type, generic_context);
1087 //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
1090 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
1092 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
1096 * mono_class_rgctx_get_array_size:
1097 * @n: The number of the array
1098 * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
1100 * Returns the number of slots in the n'th array of a (M)RGCTX. That
1101 * number includes the slot for linking and - for MRGCTXs - the two
1102 * slots in the first array for additional information.
1105 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
1107 g_assert (n >= 0 && n < 30);
1116 * LOCKING: domain lock
1119 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
1121 static gboolean inited = FALSE;
1122 static int rgctx_num_alloced = 0;
1123 static int rgctx_bytes_alloced = 0;
1124 static int mrgctx_num_alloced = 0;
1125 static int mrgctx_bytes_alloced = 0;
1127 int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
1128 gpointer array = mono_domain_alloc0 (domain, size);
1131 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
1132 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
1133 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
1134 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
1139 mrgctx_num_alloced++;
1140 mrgctx_bytes_alloced += size;
1142 rgctx_num_alloced++;
1143 rgctx_bytes_alloced += size;
1150 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
1151 MonoGenericInst *method_inst)
1154 int i, first_slot, size;
1155 MonoDomain *domain = class_vtable->domain;
1156 MonoClass *class = class_vtable->klass;
1157 MonoGenericContext *class_context = class->generic_class ? &class->generic_class->context : NULL;
1158 MonoRuntimeGenericContextOtherInfoTemplate oti;
1159 MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
1165 mono_domain_lock (domain);
1167 /* First check whether that slot isn't already instantiated.
1168 This might happen because lookup doesn't lock. Allocate
1169 arrays on the way. */
1171 size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
1173 size -= sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
1174 for (i = 0; ; ++i) {
1177 if (method_inst && i == 0)
1178 offset = sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
1182 if (slot < first_slot + size - 1) {
1183 rgctx_index = slot - first_slot + 1 + offset;
1184 info = rgctx [rgctx_index];
1186 mono_domain_unlock (domain);
1191 if (!rgctx [offset + 0])
1192 rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
1193 rgctx = rgctx [offset + 0];
1194 first_slot += size - 1;
1195 size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
1198 g_assert (!rgctx [rgctx_index]);
1200 mono_domain_unlock (domain);
1202 oti = class_get_rgctx_template_oti (class_uninstantiated (class),
1203 method_inst ? method_inst->type_argc : 0, slot, TRUE, &do_free);
1204 /* This might take the loader lock */
1205 info = instantiate_other_info (domain, &oti, &context, class);
1209 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
1212 /*FIXME We should use CAS here, no need to take a lock.*/
1213 mono_domain_lock (domain);
1215 /* Check whether the slot hasn't been instantiated in the
1217 if (rgctx [rgctx_index])
1218 info = rgctx [rgctx_index];
1220 rgctx [rgctx_index] = info;
1222 mono_domain_unlock (domain);
1225 free_inflated_info (oti.info_type, oti.data);
1231 * mono_class_fill_runtime_generic_context:
1232 * @class_vtable: a vtable
1233 * @slot: a slot index to be instantiated
1235 * Instantiates a slot in the RGCTX.
1238 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot)
1240 static gboolean inited = FALSE;
1241 static int num_alloced = 0;
1243 MonoDomain *domain = class_vtable->domain;
1244 MonoRuntimeGenericContext *rgctx;
1247 mono_domain_lock (domain);
1250 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
1254 rgctx = class_vtable->runtime_generic_context;
1256 rgctx = alloc_rgctx_array (domain, 0, FALSE);
1257 class_vtable->runtime_generic_context = rgctx;
1261 mono_domain_unlock (domain);
1263 info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
1269 * mono_method_fill_runtime_generic_context:
1270 * @mrgctx: an MRGCTX
1271 * @slot: a slot index to be instantiated
1273 * Instantiates a slot in the MRGCTX.
1276 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot)
1280 info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot,
1281 mrgctx->method_inst);
1287 mrgctx_hash_func (gconstpointer key)
1289 const MonoMethodRuntimeGenericContext *mrgctx = key;
1291 return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
1295 mrgctx_equal_func (gconstpointer a, gconstpointer b)
1297 const MonoMethodRuntimeGenericContext *mrgctx1 = a;
1298 const MonoMethodRuntimeGenericContext *mrgctx2 = b;
1300 return mrgctx1->class_vtable == mrgctx2->class_vtable &&
1301 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
1305 * mono_method_lookup_rgctx:
1306 * @class_vtable: a vtable
1307 * @method_inst: the method inst of a generic method
1309 * Returns the MRGCTX for the generic method(s) with the given
1310 * method_inst of the given class_vtable.
1312 * LOCKING: Take the domain lock.
1314 MonoMethodRuntimeGenericContext*
1315 mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
1317 MonoDomain *domain = class_vtable->domain;
1318 MonoMethodRuntimeGenericContext *mrgctx;
1319 MonoMethodRuntimeGenericContext key;
1321 g_assert (!class_vtable->klass->generic_container);
1322 g_assert (!method_inst->is_open);
1324 mono_domain_lock (domain);
1325 if (!domain->method_rgctx_hash)
1326 domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
1328 key.class_vtable = class_vtable;
1329 key.method_inst = method_inst;
1331 mrgctx = g_hash_table_lookup (domain->method_rgctx_hash, &key);
1336 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
1337 mrgctx->class_vtable = class_vtable;
1338 mrgctx->method_inst = method_inst;
1340 g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
1343 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
1344 for (i = 0; i < method_inst->type_argc; ++i)
1345 g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
1350 mono_domain_unlock (domain);
1358 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars)
1362 for (i = 0; i < inst->type_argc; ++i) {
1363 MonoType *type = inst->type_argv [i];
1366 if (MONO_TYPE_IS_REFERENCE (type))
1369 type_type = mono_type_get_type (type);
1370 if (allow_type_vars && (type_type == MONO_TYPE_VAR || type_type == MONO_TYPE_MVAR))
1380 * mono_generic_context_is_sharable:
1381 * @context: a generic context
1383 * Returns whether the generic context is sharable. A generic context
1384 * is sharable iff all of its type arguments are reference type.
1387 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
1389 g_assert (context->class_inst || context->method_inst);
1391 if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars))
1394 if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars))
1401 * mono_method_is_generic_impl:
1404 * Returns whether the method is either generic or part of a generic
1408 mono_method_is_generic_impl (MonoMethod *method)
1410 if (method->is_inflated) {
1411 g_assert (method->wrapper_type == MONO_WRAPPER_NONE);
1414 /* We don't treat wrappers as generic code, i.e., we never
1415 apply generic sharing to them. This is especially
1416 important for static rgctx invoke wrappers, which only work
1417 if not compiled with sharing. */
1418 if (method->wrapper_type != MONO_WRAPPER_NONE)
1420 if (method->klass->generic_container)
1426 has_constraints (MonoGenericContainer *container)
1432 g_assert (container->type_argc > 0);
1433 g_assert (container->type_params);
1435 for (i = 0; i < container->type_argc; ++i)
1436 if (container->type_params [i].constraints)
1443 * mono_method_is_generic_sharable_impl:
1445 * @allow_type_vars: whether to regard type variables as reference types
1447 * Returns TRUE iff the method is inflated or part of an inflated
1448 * class, its context is sharable and it has no constraints on its
1449 * type parameters. Otherwise returns FALSE.
1452 mono_method_is_generic_sharable_impl (MonoMethod *method, gboolean allow_type_vars)
1454 if (!mono_method_is_generic_impl (method))
1457 if (method->is_inflated) {
1458 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
1459 MonoGenericContext *context = &inflated->context;
1461 if (!mono_generic_context_is_sharable (context, allow_type_vars))
1464 g_assert (inflated->declaring);
1466 if (inflated->declaring->is_generic) {
1467 if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
1472 if (method->klass->generic_class) {
1473 if (!mono_generic_context_is_sharable (&method->klass->generic_class->context, allow_type_vars))
1476 g_assert (method->klass->generic_class->container_class &&
1477 method->klass->generic_class->container_class->generic_container);
1479 if (has_constraints (method->klass->generic_class->container_class->generic_container))
1483 if (method->klass->generic_container && !allow_type_vars)
1490 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
1492 if (!mono_class_generic_sharing_enabled (method->klass))
1495 if (!mono_method_is_generic_sharable_impl (method, allow_type_vars))
1498 if (method->is_inflated && mono_method_get_context (method)->method_inst)
1501 return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
1502 method->klass->valuetype) &&
1503 (method->klass->generic_class || method->klass->generic_container);
1506 static MonoGenericInst*
1507 get_object_generic_inst (int type_argc)
1509 MonoType **type_argv;
1512 type_argv = alloca (sizeof (MonoType*) * type_argc);
1514 for (i = 0; i < type_argc; ++i)
1515 type_argv [i] = &mono_defaults.object_class->byval_arg;
1517 return mono_metadata_get_generic_inst (type_argc, type_argv);
1521 * mono_method_construct_object_context:
1524 * Returns a generic context for method with all type variables for
1525 * class and method instantiated with Object.
1528 mono_method_construct_object_context (MonoMethod *method)
1530 MonoGenericContext object_context;
1532 g_assert (!method->klass->generic_class);
1533 if (method->klass->generic_container) {
1534 int type_argc = method->klass->generic_container->type_argc;
1536 object_context.class_inst = get_object_generic_inst (type_argc);
1538 object_context.class_inst = NULL;
1541 if (mono_method_get_context_general (method, TRUE)->method_inst) {
1542 int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
1544 object_context.method_inst = get_object_generic_inst (type_argc);
1546 object_context.method_inst = NULL;
1549 g_assert (object_context.class_inst || object_context.method_inst);
1551 return object_context;
1555 * mono_domain_lookup_shared_generic:
1557 * @open_method: an open generic method
1559 * Looks up the jit info for method via the domain's jit code hash.
1562 mono_domain_lookup_shared_generic (MonoDomain *domain, MonoMethod *open_method)
1564 static gboolean inited = FALSE;
1565 static int lookups = 0;
1566 static int failed_lookups = 0;
1568 MonoGenericContext object_context;
1569 MonoMethod *object_method;
1572 object_context = mono_method_construct_object_context (open_method);
1573 object_method = mono_class_inflate_generic_method (open_method, &object_context);
1575 mono_domain_jit_code_hash_lock (domain);
1576 ji = mono_internal_hash_table_lookup (&domain->jit_code_hash, object_method);
1577 if (ji && !ji->has_generic_jit_info)
1579 mono_domain_jit_code_hash_unlock (domain);
1582 mono_counters_register ("Shared generic lookups", MONO_COUNTER_INT|MONO_COUNTER_GENERICS, &lookups);
1583 mono_counters_register ("Failed shared generic lookups", MONO_COUNTER_INT|MONO_COUNTER_GENERICS, &failed_lookups);