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 * LOCKING: loader lock
123 static MonoRuntimeGenericContextOtherInfoTemplate*
124 get_other_info_templates (MonoRuntimeGenericContextTemplate *template, int type_argc)
126 g_assert (type_argc >= 0);
128 return template->other_infos;
129 return g_slist_nth_data (template->method_templates, type_argc - 1);
133 * LOCKING: loader lock
136 set_other_info_templates (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
137 MonoRuntimeGenericContextOtherInfoTemplate *oti)
139 g_assert (type_argc >= 0);
141 template->other_infos = oti;
143 int length = g_slist_length (template->method_templates);
146 /* FIXME: quadratic! */
147 while (length < type_argc) {
148 template->method_templates = g_slist_append_image (image, template->method_templates, NULL);
152 list = g_slist_nth (template->method_templates, type_argc - 1);
159 * LOCKING: loader lock
162 template_get_max_argc (MonoRuntimeGenericContextTemplate *template)
164 return g_slist_length (template->method_templates);
168 * LOCKING: loader lock
170 static MonoRuntimeGenericContextOtherInfoTemplate*
171 rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate *template, int type_argc, int slot)
174 MonoRuntimeGenericContextOtherInfoTemplate *oti;
176 g_assert (slot >= 0);
178 for (oti = get_other_info_templates (template, type_argc), i = 0; i < slot; oti = oti->next, ++i) {
187 * LOCKING: loader lock
190 rgctx_template_num_other_infos (MonoRuntimeGenericContextTemplate *template, int type_argc)
192 MonoRuntimeGenericContextOtherInfoTemplate *oti;
195 for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next)
201 /* Maps from uninstantiated generic classes to GList's of
202 * uninstantiated generic classes whose parent is the key class or an
203 * instance of the key class.
205 * LOCKING: loader lock
207 static GHashTable *generic_subclass_hash;
210 * LOCKING: templates lock
213 class_set_rgctx_template (MonoClass *class, MonoRuntimeGenericContextTemplate *rgctx_template)
215 if (!class->image->rgctx_template_hash)
216 class->image->rgctx_template_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
218 g_hash_table_insert (class->image->rgctx_template_hash, class, rgctx_template);
222 * LOCKING: loader lock
224 static MonoRuntimeGenericContextTemplate*
225 class_lookup_rgctx_template (MonoClass *class)
227 MonoRuntimeGenericContextTemplate *template;
229 if (!class->image->rgctx_template_hash)
232 template = g_hash_table_lookup (class->image->rgctx_template_hash, class);
238 * LOCKING: loader lock
241 register_generic_subclass (MonoClass *class)
243 MonoClass *parent = class->parent;
245 MonoRuntimeGenericContextTemplate *rgctx_template = class_lookup_rgctx_template (class);
247 g_assert (rgctx_template);
249 if (parent->generic_class)
250 parent = parent->generic_class->container_class;
252 if (!generic_subclass_hash)
253 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
255 subclass = g_hash_table_lookup (generic_subclass_hash, parent);
256 rgctx_template->next_subclass = subclass;
257 g_hash_table_insert (generic_subclass_hash, parent, class);
261 move_subclasses_not_in_image_foreach_func (MonoClass *class, MonoClass *subclass, MonoImage *image)
265 if (class->image == image) {
266 /* The parent class itself is in the image, so all the
267 subclasses must be in the image, too. If not,
268 we're removing an image containing a class which
269 still has a subclass in another image. */
272 g_assert (subclass->image == image);
273 subclass = class_lookup_rgctx_template (subclass)->next_subclass;
281 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
282 MonoClass *next = subclass_template->next_subclass;
284 if (subclass->image != image) {
285 subclass_template->next_subclass = new_list;
293 g_hash_table_insert (generic_subclass_hash, class, new_list);
297 * mono_class_unregister_image_generic_subclasses:
300 * Removes all classes of the image from the generic subclass hash.
301 * Must be called when an image is unloaded.
304 mono_class_unregister_image_generic_subclasses (MonoImage *image)
306 GHashTable *old_hash;
308 //g_print ("unregistering image %s\n", image->name);
310 if (!generic_subclass_hash)
315 old_hash = generic_subclass_hash;
316 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
318 g_hash_table_foreach (old_hash, (GHFunc)move_subclasses_not_in_image_foreach_func, image);
320 mono_loader_unlock ();
322 g_hash_table_destroy (old_hash);
325 static MonoRuntimeGenericContextTemplate*
326 alloc_template (MonoClass *class)
328 static gboolean inited = FALSE;
329 static int num_allocted = 0;
330 static int num_bytes = 0;
332 int size = sizeof (MonoRuntimeGenericContextTemplate);
335 mono_counters_register ("RGCTX template num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_allocted);
336 mono_counters_register ("RGCTX template bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_bytes);
343 return mono_image_alloc0 (class->image, size);
346 static MonoRuntimeGenericContextOtherInfoTemplate*
347 alloc_oti (MonoImage *image)
349 static gboolean inited = FALSE;
350 static int num_allocted = 0;
351 static int num_bytes = 0;
353 int size = sizeof (MonoRuntimeGenericContextOtherInfoTemplate);
356 mono_counters_register ("RGCTX oti num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_allocted);
357 mono_counters_register ("RGCTX oti bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_bytes);
364 return mono_image_alloc0 (image, size);
367 #define MONO_RGCTX_SLOT_USED_MARKER ((gpointer)&mono_defaults.object_class->byval_arg)
370 * LOCKING: loader lock
373 rgctx_template_set_other_slot (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
374 int slot, gpointer data, int info_type)
376 static gboolean inited = FALSE;
377 static int num_markers = 0;
378 static int num_data = 0;
381 MonoRuntimeGenericContextOtherInfoTemplate *list = get_other_info_templates (template, type_argc);
382 MonoRuntimeGenericContextOtherInfoTemplate **oti = &list;
385 mono_counters_register ("RGCTX oti num markers", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_markers);
386 mono_counters_register ("RGCTX oti num data", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_data);
390 g_assert (slot >= 0);
398 *oti = alloc_oti (image);
402 g_assert (!(*oti)->data);
404 (*oti)->info_type = info_type;
406 set_other_info_templates (image, template, type_argc, list);
408 if (data == MONO_RGCTX_SLOT_USED_MARKER)
415 * mono_method_get_declaring_generic_method:
416 * @method: an inflated method
418 * Returns an inflated method's declaring method.
421 mono_method_get_declaring_generic_method (MonoMethod *method)
423 MonoMethodInflated *inflated;
425 g_assert (method->is_inflated);
427 inflated = (MonoMethodInflated*)method;
429 return inflated->declaring;
433 * mono_class_get_method_generic:
437 * Given a class and a generic method, which has to be of an
438 * instantiation of the same class that klass is an instantiation of,
439 * returns the corresponding method in klass. Example:
441 * klass is Gen<string>
442 * method is Gen<object>.work<int>
444 * returns: Gen<string>.work<int>
447 mono_class_get_method_generic (MonoClass *klass, MonoMethod *method)
449 MonoMethod *declaring, *m;
452 if (method->is_inflated)
453 declaring = mono_method_get_declaring_generic_method (method);
458 if (klass->generic_class)
459 m = mono_class_get_inflated_method (klass, declaring);
462 mono_class_setup_methods (klass);
463 for (i = 0; i < klass->method.count; ++i) {
464 m = klass->methods [i];
467 if (m->is_inflated && mono_method_get_declaring_generic_method (m) == declaring)
470 if (i >= klass->method.count)
474 if (method != declaring) {
475 MonoGenericContext context;
477 context.class_inst = NULL;
478 context.method_inst = mono_method_get_context (method)->method_inst;
480 m = mono_class_inflate_generic_method (m, &context);
487 inflate_other_data (gpointer data, int info_type, MonoGenericContext *context, MonoClass *class, gboolean temporary)
491 if (data == MONO_RGCTX_SLOT_USED_MARKER)
492 return MONO_RGCTX_SLOT_USED_MARKER;
496 case MONO_RGCTX_INFO_STATIC_DATA:
497 case MONO_RGCTX_INFO_KLASS:
498 case MONO_RGCTX_INFO_VTABLE:
499 case MONO_RGCTX_INFO_TYPE:
500 case MONO_RGCTX_INFO_REFLECTION_TYPE:
501 return mono_class_inflate_generic_type_with_mempool (temporary ? NULL : class->image,
504 case MONO_RGCTX_INFO_METHOD:
505 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
506 case MONO_RGCTX_INFO_METHOD_RGCTX:
507 case MONO_RGCTX_INFO_METHOD_CONTEXT:
508 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: {
509 MonoMethod *method = data;
510 MonoMethod *inflated_method;
511 MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
512 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
514 mono_metadata_free_type (inflated_type);
516 mono_class_init (inflated_class);
518 g_assert (!method->wrapper_type);
520 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
521 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
522 inflated_method = mono_method_search_in_array_class (inflated_class,
523 method->name, method->signature);
525 inflated_method = mono_class_inflate_generic_method (method, context);
527 mono_class_init (inflated_method->klass);
528 g_assert (inflated_method->klass == inflated_class);
529 return inflated_method;
532 case MONO_RGCTX_INFO_CLASS_FIELD: {
533 MonoClassField *field = data;
534 MonoType *inflated_type = mono_class_inflate_generic_type (&field->parent->byval_arg, context);
535 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
536 int i = field - field->parent->fields;
537 gpointer dummy = NULL;
539 mono_metadata_free_type (inflated_type);
541 mono_class_get_fields (inflated_class, &dummy);
542 g_assert (inflated_class->fields);
544 return &inflated_class->fields [i];
548 g_assert_not_reached ();
550 /* Not reached, quiet compiler */
555 inflate_other_info (MonoRuntimeGenericContextOtherInfoTemplate *oti,
556 MonoGenericContext *context, MonoClass *class, gboolean temporary)
558 return inflate_other_data (oti->data, oti->info_type, context, class, temporary);
562 free_inflated_info (int info_type, gpointer info)
568 case MONO_RGCTX_INFO_STATIC_DATA:
569 case MONO_RGCTX_INFO_KLASS:
570 case MONO_RGCTX_INFO_VTABLE:
571 case MONO_RGCTX_INFO_TYPE:
572 case MONO_RGCTX_INFO_REFLECTION_TYPE:
573 mono_metadata_free_type (info);
580 static MonoRuntimeGenericContextOtherInfoTemplate
581 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free);
584 * mono_class_get_runtime_generic_context_template:
587 * Looks up or constructs, if necessary, the runtime generic context
590 static MonoRuntimeGenericContextTemplate*
591 mono_class_get_runtime_generic_context_template (MonoClass *class)
593 MonoRuntimeGenericContextTemplate *parent_template, *template;
596 g_assert (!class->generic_class);
599 template = class_lookup_rgctx_template (class);
600 mono_loader_unlock ();
605 template = alloc_template (class);
610 if (class->parent->generic_class) {
612 int max_argc, type_argc;
614 parent_template = mono_class_get_runtime_generic_context_template
615 (class->parent->generic_class->container_class);
617 max_argc = template_get_max_argc (parent_template);
619 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
620 num_entries = rgctx_template_num_other_infos (parent_template, type_argc);
622 /* FIXME: quadratic! */
623 for (i = 0; i < num_entries; ++i) {
624 MonoRuntimeGenericContextOtherInfoTemplate oti;
626 oti = class_get_rgctx_template_oti (class->parent, type_argc, i, FALSE, NULL);
627 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
628 rgctx_template_set_other_slot (class->image, template, type_argc, i,
629 oti.data, oti.info_type);
634 MonoRuntimeGenericContextOtherInfoTemplate *oti;
635 int max_argc, type_argc;
637 parent_template = mono_class_get_runtime_generic_context_template (class->parent);
639 max_argc = template_get_max_argc (parent_template);
641 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
642 /* FIXME: quadratic! */
643 for (i = 0, oti = parent_template->other_infos; oti; ++i, oti = oti->next) {
644 if (oti->data && oti->data != MONO_RGCTX_SLOT_USED_MARKER) {
645 rgctx_template_set_other_slot (class->image, template, type_argc, i,
646 oti->data, oti->info_type);
653 if (class_lookup_rgctx_template (class)) {
654 /* some other thread already set the template */
655 template = class_lookup_rgctx_template (class);
657 class_set_rgctx_template (class, template);
660 register_generic_subclass (class);
663 mono_loader_unlock ();
669 * temporary signifies whether the inflated info (oti.data) will be
670 * used temporarily, in which case it might be heap-allocated, or
671 * permanently, in which case it will be mempool-allocated. If
672 * temporary is set then *do_free will return whether the returned
673 * data must be freed.
675 * LOCKING: loader lock
677 static MonoRuntimeGenericContextOtherInfoTemplate
678 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free)
680 g_assert ((temporary && do_free) || (!temporary && !do_free));
682 if (class->generic_class) {
683 MonoRuntimeGenericContextOtherInfoTemplate oti;
684 gboolean tmp_do_free;
686 oti = class_get_rgctx_template_oti (class->generic_class->container_class,
687 type_argc, slot, TRUE, &tmp_do_free);
689 gpointer info = oti.data;
690 oti.data = inflate_other_info (&oti, &class->generic_class->context, class, temporary);
692 free_inflated_info (oti.info_type, info);
699 MonoRuntimeGenericContextTemplate *template;
700 MonoRuntimeGenericContextOtherInfoTemplate *oti;
702 template = mono_class_get_runtime_generic_context_template (class);
703 oti = rgctx_template_get_other_slot (template, type_argc, slot);
714 class_uninstantiated (MonoClass *class)
716 if (class->generic_class)
717 return class->generic_class->container_class;
722 class_type_info (MonoDomain *domain, MonoClass *class, int info_type)
725 case MONO_RGCTX_INFO_STATIC_DATA: {
726 MonoVTable *vtable = mono_class_vtable (domain, class);
728 mono_raise_exception (mono_class_get_exception_for_failure (class));
731 case MONO_RGCTX_INFO_KLASS:
733 case MONO_RGCTX_INFO_VTABLE: {
734 MonoVTable *vtable = mono_class_vtable (domain, class);
736 mono_raise_exception (mono_class_get_exception_for_failure (class));
740 g_assert_not_reached ();
747 instantiate_other_info (MonoDomain *domain, MonoRuntimeGenericContextOtherInfoTemplate *oti,
748 MonoGenericContext *context, MonoClass *class)
756 switch (oti->info_type) {
757 case MONO_RGCTX_INFO_STATIC_DATA:
758 case MONO_RGCTX_INFO_KLASS:
759 case MONO_RGCTX_INFO_VTABLE:
766 data = inflate_other_info (oti, context, class, temporary);
768 switch (oti->info_type) {
769 case MONO_RGCTX_INFO_STATIC_DATA:
770 case MONO_RGCTX_INFO_KLASS:
771 case MONO_RGCTX_INFO_VTABLE: {
772 MonoClass *arg_class = mono_class_from_mono_type (data);
774 free_inflated_info (oti->info_type, data);
775 g_assert (arg_class);
777 /* The class might be used as an argument to
778 mono_value_copy(), which requires that its GC
779 descriptor has been computed. */
780 if (oti->info_type == MONO_RGCTX_INFO_KLASS)
781 mono_class_compute_gc_descriptor (arg_class);
783 return class_type_info (domain, arg_class, oti->info_type);
785 case MONO_RGCTX_INFO_TYPE:
787 case MONO_RGCTX_INFO_REFLECTION_TYPE:
788 return mono_type_get_object (domain, data);
789 case MONO_RGCTX_INFO_METHOD:
791 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
792 return mono_create_ftnptr (mono_domain_get (),
793 mono_runtime_create_jump_trampoline (mono_domain_get (), data, TRUE));
794 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
795 return mono_create_ftnptr (mono_domain_get (),
796 mono_runtime_create_jump_trampoline (mono_domain_get (),
797 mono_marshal_get_remoting_invoke_with_check (data), TRUE));
798 case MONO_RGCTX_INFO_CLASS_FIELD:
800 case MONO_RGCTX_INFO_METHOD_RGCTX: {
801 MonoMethodInflated *method = data;
804 g_assert (method->method.method.is_inflated);
805 g_assert (method->context.method_inst);
807 vtable = mono_class_vtable (domain, method->method.method.klass);
809 mono_raise_exception (mono_class_get_exception_for_failure (method->method.method.klass));
811 return mono_method_lookup_rgctx (vtable, method->context.method_inst);
813 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
814 MonoMethodInflated *method = data;
816 g_assert (method->method.method.is_inflated);
817 g_assert (method->context.method_inst);
819 return method->context.method_inst;
822 g_assert_not_reached ();
829 * LOCKING: loader lock
832 fill_in_rgctx_template_slot (MonoClass *class, int type_argc, int index, gpointer data, int info_type)
834 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
837 g_assert (!class->generic_class);
839 rgctx_template_set_other_slot (class->image, template, type_argc, index, data, info_type);
841 /* Recurse for all subclasses */
842 if (generic_subclass_hash)
843 subclass = g_hash_table_lookup (generic_subclass_hash, class);
848 MonoRuntimeGenericContextOtherInfoTemplate subclass_oti;
849 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
851 g_assert (!subclass->generic_class);
852 g_assert (subclass_template);
854 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, NULL);
855 g_assert (subclass_oti.data);
857 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
859 subclass = subclass_template->next_subclass;
864 * LOCKING: loader lock
867 register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type)
870 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
872 MonoRuntimeGenericContextOtherInfoTemplate *oti;
874 for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
879 //g_print ("template %s . other_infos [%d] = %s\n", mono_type_get_full_name (class), i, mono_type_get_full_name (other_class));
881 /* Mark the slot as used in all parent classes (until we find
882 a parent class which already has it marked used). */
883 parent = class->parent;
884 while (parent != NULL) {
885 MonoRuntimeGenericContextTemplate *parent_template;
886 MonoRuntimeGenericContextOtherInfoTemplate *oti;
888 if (parent->generic_class)
889 parent = parent->generic_class->container_class;
891 parent_template = mono_class_get_runtime_generic_context_template (parent);
892 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
894 if (oti && oti->data)
897 rgctx_template_set_other_slot (parent->image, parent_template, type_argc, i,
898 MONO_RGCTX_SLOT_USED_MARKER, 0);
900 parent = parent->parent;
903 /* Fill in the slot in this class and in all subclasses
905 fill_in_rgctx_template_slot (class, type_argc, i, data, info_type);
911 other_info_equal (gpointer data1, gpointer data2, int info_type)
914 case MONO_RGCTX_INFO_STATIC_DATA:
915 case MONO_RGCTX_INFO_KLASS:
916 case MONO_RGCTX_INFO_VTABLE:
917 case MONO_RGCTX_INFO_TYPE:
918 case MONO_RGCTX_INFO_REFLECTION_TYPE:
919 return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
920 case MONO_RGCTX_INFO_METHOD:
921 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
922 case MONO_RGCTX_INFO_CLASS_FIELD:
923 case MONO_RGCTX_INFO_METHOD_RGCTX:
924 case MONO_RGCTX_INFO_METHOD_CONTEXT:
925 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
926 return data1 == data2;
928 g_assert_not_reached ();
935 lookup_or_register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type,
936 MonoGenericContext *generic_context)
938 static gboolean inited = FALSE;
939 static int max_slot = 0;
941 MonoRuntimeGenericContextTemplate *rgctx_template =
942 mono_class_get_runtime_generic_context_template (class);
943 MonoRuntimeGenericContextOtherInfoTemplate *oti_list, *oti;
946 g_assert (!class->generic_class);
947 g_assert (class->generic_container || type_argc);
951 oti_list = get_other_info_templates (rgctx_template, type_argc);
953 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
954 gpointer inflated_data;
956 if (oti->info_type != info_type || !oti->data)
959 inflated_data = inflate_other_info (oti, generic_context, class, TRUE);
961 if (other_info_equal (data, inflated_data, info_type)) {
962 free_inflated_info (info_type, inflated_data);
963 mono_loader_unlock ();
966 free_inflated_info (info_type, inflated_data);
969 /* We haven't found the info */
970 i = register_other_info (class, type_argc, data, info_type);
972 mono_loader_unlock ();
975 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
985 * mono_method_lookup_or_register_other_info:
987 * @in_mrgctx: whether to put the data into the MRGCTX
988 * @data: the info data
989 * @info_type: the type of info to register about data
990 * @generic_context: a generic context
992 * Looks up and, if necessary, adds information about other_class in
993 * method's or method's class runtime generic context. Returns the
994 * encoded slot number.
997 mono_method_lookup_or_register_other_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
998 int info_type, MonoGenericContext *generic_context)
1000 MonoClass *class = method->klass;
1001 int type_argc, index;
1004 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
1006 g_assert (method->is_inflated && method_inst);
1007 type_argc = method_inst->type_argc;
1008 g_assert (type_argc > 0);
1013 index = lookup_or_register_other_info (class, type_argc, data, info_type, generic_context);
1015 //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
1018 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
1020 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
1024 * mono_class_rgctx_get_array_size:
1025 * @n: The number of the array
1026 * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
1028 * Returns the number of slots in the n'th array of a (M)RGCTX. That
1029 * number includes the slot for linking and - for MRGCTXs - the two
1030 * slots in the first array for additional information.
1033 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
1035 g_assert (n >= 0 && n < 30);
1044 * LOCKING: domain lock
1047 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
1049 static gboolean inited = FALSE;
1050 static int rgctx_num_alloced = 0;
1051 static int rgctx_bytes_alloced = 0;
1052 static int mrgctx_num_alloced = 0;
1053 static int mrgctx_bytes_alloced = 0;
1055 int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
1056 gpointer array = mono_domain_alloc0 (domain, size);
1059 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
1060 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
1061 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
1062 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
1067 mrgctx_num_alloced++;
1068 mrgctx_bytes_alloced += size;
1070 rgctx_num_alloced++;
1071 rgctx_bytes_alloced += size;
1078 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
1079 MonoGenericInst *method_inst)
1082 int i, first_slot, size;
1083 MonoDomain *domain = class_vtable->domain;
1084 MonoClass *class = class_vtable->klass;
1085 MonoGenericContext *class_context = class->generic_class ? &class->generic_class->context : NULL;
1086 MonoRuntimeGenericContextOtherInfoTemplate oti;
1087 MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
1093 mono_domain_lock (domain);
1095 /* First check whether that slot isn't already instantiated.
1096 This might happen because lookup doesn't lock. Allocate
1097 arrays on the way. */
1099 size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
1101 size -= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
1102 for (i = 0; ; ++i) {
1105 if (method_inst && i == 0)
1106 offset = MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
1110 if (slot < first_slot + size - 1) {
1111 rgctx_index = slot - first_slot + 1 + offset;
1112 info = rgctx [rgctx_index];
1114 mono_domain_unlock (domain);
1119 if (!rgctx [offset + 0])
1120 rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
1121 rgctx = rgctx [offset + 0];
1122 first_slot += size - 1;
1123 size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
1126 g_assert (!rgctx [rgctx_index]);
1128 mono_domain_unlock (domain);
1130 oti = class_get_rgctx_template_oti (class_uninstantiated (class),
1131 method_inst ? method_inst->type_argc : 0, slot, TRUE, &do_free);
1132 /* This might take the loader lock */
1133 info = instantiate_other_info (domain, &oti, &context, class);
1137 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
1140 /*FIXME We should use CAS here, no need to take a lock.*/
1141 mono_domain_lock (domain);
1143 /* Check whether the slot hasn't been instantiated in the
1145 if (rgctx [rgctx_index])
1146 info = rgctx [rgctx_index];
1148 rgctx [rgctx_index] = info;
1150 mono_domain_unlock (domain);
1153 free_inflated_info (oti.info_type, oti.data);
1159 * mono_class_fill_runtime_generic_context:
1160 * @class_vtable: a vtable
1161 * @slot: a slot index to be instantiated
1163 * Instantiates a slot in the RGCTX.
1166 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot)
1168 static gboolean inited = FALSE;
1169 static int num_alloced = 0;
1171 MonoDomain *domain = class_vtable->domain;
1172 MonoRuntimeGenericContext *rgctx;
1175 mono_domain_lock (domain);
1178 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
1182 rgctx = class_vtable->runtime_generic_context;
1184 rgctx = alloc_rgctx_array (domain, 0, FALSE);
1185 class_vtable->runtime_generic_context = rgctx;
1189 mono_domain_unlock (domain);
1191 info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
1197 * mono_method_fill_runtime_generic_context:
1198 * @mrgctx: an MRGCTX
1199 * @slot: a slot index to be instantiated
1201 * Instantiates a slot in the MRGCTX.
1204 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot)
1208 info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot,
1209 mrgctx->method_inst);
1215 mrgctx_hash_func (gconstpointer key)
1217 const MonoMethodRuntimeGenericContext *mrgctx = key;
1219 return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
1223 mrgctx_equal_func (gconstpointer a, gconstpointer b)
1225 const MonoMethodRuntimeGenericContext *mrgctx1 = a;
1226 const MonoMethodRuntimeGenericContext *mrgctx2 = b;
1228 return mrgctx1->class_vtable == mrgctx2->class_vtable &&
1229 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
1233 * mono_method_lookup_rgctx:
1234 * @class_vtable: a vtable
1235 * @method_inst: the method inst of a generic method
1237 * Returns the MRGCTX for the generic method(s) with the given
1238 * method_inst of the given class_vtable.
1240 * LOCKING: Take the domain lock.
1242 MonoMethodRuntimeGenericContext*
1243 mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
1245 MonoDomain *domain = class_vtable->domain;
1246 MonoMethodRuntimeGenericContext *mrgctx;
1247 MonoMethodRuntimeGenericContext key;
1249 g_assert (!class_vtable->klass->generic_container);
1250 g_assert (!method_inst->is_open);
1252 mono_domain_lock (domain);
1253 if (!domain->method_rgctx_hash)
1254 domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
1256 key.class_vtable = class_vtable;
1257 key.method_inst = method_inst;
1259 mrgctx = g_hash_table_lookup (domain->method_rgctx_hash, &key);
1264 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
1265 mrgctx->class_vtable = class_vtable;
1266 mrgctx->method_inst = method_inst;
1268 g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
1271 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
1272 for (i = 0; i < method_inst->type_argc; ++i)
1273 g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
1278 mono_domain_unlock (domain);
1286 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars)
1290 for (i = 0; i < inst->type_argc; ++i) {
1291 MonoType *type = inst->type_argv [i];
1294 if (MONO_TYPE_IS_REFERENCE (type))
1297 type_type = mono_type_get_type (type);
1298 if (allow_type_vars && (type_type == MONO_TYPE_VAR || type_type == MONO_TYPE_MVAR))
1308 * mono_generic_context_is_sharable:
1309 * @context: a generic context
1311 * Returns whether the generic context is sharable. A generic context
1312 * is sharable iff all of its type arguments are reference type.
1315 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
1317 g_assert (context->class_inst || context->method_inst);
1319 if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars))
1322 if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars))
1329 * mono_method_is_generic_impl:
1332 * Returns whether the method is either generic or part of a generic
1336 mono_method_is_generic_impl (MonoMethod *method)
1338 if (method->is_inflated) {
1339 g_assert (method->wrapper_type == MONO_WRAPPER_NONE);
1342 /* We don't treat wrappers as generic code, i.e., we never
1343 apply generic sharing to them. This is especially
1344 important for static rgctx invoke wrappers, which only work
1345 if not compiled with sharing. */
1346 if (method->wrapper_type != MONO_WRAPPER_NONE)
1348 if (method->klass->generic_container)
1354 has_constraints (MonoGenericContainer *container)
1360 g_assert (container->type_argc > 0);
1361 g_assert (container->type_params);
1363 for (i = 0; i < container->type_argc; ++i)
1364 if (container->type_params [i].constraints)
1371 * mono_method_is_generic_sharable_impl:
1373 * @allow_type_vars: whether to regard type variables as reference types
1375 * Returns TRUE iff the method is inflated or part of an inflated
1376 * class, its context is sharable and it has no constraints on its
1377 * type parameters. Otherwise returns FALSE.
1380 mono_method_is_generic_sharable_impl (MonoMethod *method, gboolean allow_type_vars)
1382 if (!mono_method_is_generic_impl (method))
1385 if (method->is_inflated) {
1386 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
1387 MonoGenericContext *context = &inflated->context;
1389 if (!mono_generic_context_is_sharable (context, allow_type_vars))
1392 g_assert (inflated->declaring);
1394 if (inflated->declaring->is_generic) {
1395 if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
1400 if (method->klass->generic_class) {
1401 if (!mono_generic_context_is_sharable (&method->klass->generic_class->context, allow_type_vars))
1404 g_assert (method->klass->generic_class->container_class &&
1405 method->klass->generic_class->container_class->generic_container);
1407 if (has_constraints (method->klass->generic_class->container_class->generic_container))
1411 if (method->klass->generic_container && !allow_type_vars)
1418 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
1420 if (!mono_class_generic_sharing_enabled (method->klass))
1423 if (!mono_method_is_generic_sharable_impl (method, allow_type_vars))
1426 if (method->is_inflated && mono_method_get_context (method)->method_inst)
1429 return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
1430 method->klass->valuetype) &&
1431 (method->klass->generic_class || method->klass->generic_container);
1434 static MonoGenericInst*
1435 get_object_generic_inst (int type_argc)
1437 MonoType **type_argv;
1440 type_argv = alloca (sizeof (MonoType*) * type_argc);
1442 for (i = 0; i < type_argc; ++i)
1443 type_argv [i] = &mono_defaults.object_class->byval_arg;
1445 return mono_metadata_get_generic_inst (type_argc, type_argv);
1449 * mono_method_construct_object_context:
1452 * Returns a generic context for method with all type variables for
1453 * class and method instantiated with Object.
1456 mono_method_construct_object_context (MonoMethod *method)
1458 MonoGenericContext object_context;
1460 g_assert (!method->klass->generic_class);
1461 if (method->klass->generic_container) {
1462 int type_argc = method->klass->generic_container->type_argc;
1464 object_context.class_inst = get_object_generic_inst (type_argc);
1466 object_context.class_inst = NULL;
1469 if (mono_method_get_context_general (method, TRUE)->method_inst) {
1470 int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
1472 object_context.method_inst = get_object_generic_inst (type_argc);
1474 object_context.method_inst = NULL;
1477 g_assert (object_context.class_inst || object_context.method_inst);
1479 return object_context;
1483 * mono_domain_lookup_shared_generic:
1485 * @open_method: an open generic method
1487 * Looks up the jit info for method via the domain's jit code hash.
1490 mono_domain_lookup_shared_generic (MonoDomain *domain, MonoMethod *open_method)
1492 static gboolean inited = FALSE;
1493 static int lookups = 0;
1494 static int failed_lookups = 0;
1496 MonoGenericContext object_context;
1497 MonoMethod *object_method;
1500 object_context = mono_method_construct_object_context (open_method);
1501 object_method = mono_class_inflate_generic_method (open_method, &object_context);
1503 mono_domain_jit_code_hash_lock (domain);
1504 ji = mono_internal_hash_table_lookup (&domain->jit_code_hash, object_method);
1505 if (ji && !ji->has_generic_jit_info)
1507 mono_domain_jit_code_hash_unlock (domain);
1510 mono_counters_register ("Shared generic lookups", MONO_COUNTER_INT|MONO_COUNTER_GENERICS, &lookups);
1511 mono_counters_register ("Failed shared generic lookups", MONO_COUNTER_INT|MONO_COUNTER_GENERICS, &failed_lookups);