2 * generic-sharing.c: Support functions for generic sharing.
5 * Mark Probst (mark.probst@gmail.com)
7 * Copyright 2007-2009 Novell, Inc (http://www.novell.com)
19 #include <mono/utils/mono-membar.h>
20 #include <mono/utils/mono-counters.h>
22 #include "metadata-internals.h"
24 #include "class-internals.h"
26 #include "debug-helpers.h"
27 #include "tabledefs.h"
28 #include "mempool-internals.h"
31 type_check_context_used (MonoType
*type
, gboolean recursive
)
33 switch (mono_type_get_type (type
)) {
35 return MONO_GENERIC_CONTEXT_USED_CLASS
;
37 return MONO_GENERIC_CONTEXT_USED_METHOD
;
38 case MONO_TYPE_SZARRAY
:
39 return mono_class_check_context_used (mono_type_get_class (type
));
41 return mono_class_check_context_used (mono_type_get_array_type (type
)->eklass
);
44 return mono_class_check_context_used (mono_type_get_class (type
));
47 case MONO_TYPE_GENERICINST
:
49 MonoGenericClass
*gclass
= type
->data
.generic_class
;
51 g_assert (gclass
->container_class
->generic_container
);
52 return mono_generic_context_check_used (&gclass
->context
);
62 inst_check_context_used (MonoGenericInst
*inst
)
70 for (i
= 0; i
< inst
->type_argc
; ++i
)
71 context_used
|= type_check_context_used (inst
->type_argv
[i
], TRUE
);
77 * mono_generic_context_check_used:
78 * @context: a generic context
80 * Checks whether the context uses a type variable. Returns an int
81 * with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to reflect whether
82 * the context's class instantiation uses type variables.
85 mono_generic_context_check_used (MonoGenericContext
*context
)
89 context_used
|= inst_check_context_used (context
->class_inst
);
90 context_used
|= inst_check_context_used (context
->method_inst
);
96 * mono_class_check_context_used:
99 * Checks whether the class's generic context uses a type variable.
100 * Returns an int with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to
101 * reflect whether the context's class instantiation uses type
105 mono_class_check_context_used (MonoClass
*class)
107 int context_used
= 0;
109 context_used
|= type_check_context_used (&class->this_arg
, FALSE
);
110 context_used
|= type_check_context_used (&class->byval_arg
, FALSE
);
112 if (class->generic_class
)
113 context_used
|= mono_generic_context_check_used (&class->generic_class
->context
);
114 else if (class->generic_container
)
115 context_used
|= mono_generic_context_check_used (&class->generic_container
->context
);
121 * Guards the two global rgctx (template) hash tables and all rgctx
124 * Ordering: The templates lock can be taken while the loader lock is
127 static CRITICAL_SECTION templates_mutex
;
130 templates_lock (void)
132 static gboolean inited
= FALSE
;
137 InitializeCriticalSection (&templates_mutex
);
140 mono_loader_unlock ();
143 EnterCriticalSection (&templates_mutex
);
147 templates_unlock (void)
149 LeaveCriticalSection (&templates_mutex
);
153 * LOCKING: templates lock
155 static MonoRuntimeGenericContextOtherInfoTemplate
*
156 get_other_info_templates (MonoRuntimeGenericContextTemplate
*template, int type_argc
)
158 g_assert (type_argc
>= 0);
160 return template->other_infos
;
161 return g_slist_nth_data (template->method_templates
, type_argc
- 1);
165 * LOCKING: templates lock
168 set_other_info_templates (MonoImage
*image
, MonoRuntimeGenericContextTemplate
*template, int type_argc
,
169 MonoRuntimeGenericContextOtherInfoTemplate
*oti
)
171 g_assert (type_argc
>= 0);
173 template->other_infos
= oti
;
175 int length
= g_slist_length (template->method_templates
);
178 /* FIXME: quadratic! */
179 while (length
< type_argc
) {
180 template->method_templates
= g_slist_append_image (image
, template->method_templates
, NULL
);
184 list
= g_slist_nth (template->method_templates
, type_argc
- 1);
191 * LOCKING: templates lock
194 template_get_max_argc (MonoRuntimeGenericContextTemplate
*template)
196 return g_slist_length (template->method_templates
);
200 * LOCKING: templates lock
202 static MonoRuntimeGenericContextOtherInfoTemplate
*
203 rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate
*template, int type_argc
, int slot
)
206 MonoRuntimeGenericContextOtherInfoTemplate
*oti
;
208 g_assert (slot
>= 0);
210 for (oti
= get_other_info_templates (template, type_argc
), i
= 0; i
< slot
; oti
= oti
->next
, ++i
) {
219 * LOCKING: templates lock
222 rgctx_template_num_other_infos (MonoRuntimeGenericContextTemplate
*template, int type_argc
)
224 MonoRuntimeGenericContextOtherInfoTemplate
*oti
;
227 for (i
= 0, oti
= get_other_info_templates (template, type_argc
); oti
; ++i
, oti
= oti
->next
)
233 /* Maps from uninstantiated generic classes to GList's of
234 * uninstantiated generic classes whose parent is the key class or an
235 * instance of the key class.
237 * LOCKING: templates lock
239 static GHashTable
*generic_subclass_hash
;
242 * LOCKING: templates lock
245 class_set_rgctx_template (MonoClass
*class, MonoRuntimeGenericContextTemplate
*rgctx_template
)
247 if (!class->image
->rgctx_template_hash
)
248 class->image
->rgctx_template_hash
= g_hash_table_new (mono_aligned_addr_hash
, NULL
);
250 g_hash_table_insert (class->image
->rgctx_template_hash
, class, rgctx_template
);
254 * LOCKING: templates lock
256 static MonoRuntimeGenericContextTemplate
*
257 class_lookup_rgctx_template (MonoClass
*class)
259 MonoRuntimeGenericContextTemplate
*template;
261 if (!class->image
->rgctx_template_hash
)
264 template = g_hash_table_lookup (class->image
->rgctx_template_hash
, class);
270 * LOCKING: templates lock
273 register_generic_subclass (MonoClass
*class)
275 MonoClass
*parent
= class->parent
;
277 MonoRuntimeGenericContextTemplate
*rgctx_template
= class_lookup_rgctx_template (class);
279 g_assert (rgctx_template
);
281 if (parent
->generic_class
)
282 parent
= parent
->generic_class
->container_class
;
284 if (!generic_subclass_hash
)
285 generic_subclass_hash
= g_hash_table_new (mono_aligned_addr_hash
, NULL
);
287 subclass
= g_hash_table_lookup (generic_subclass_hash
, parent
);
288 rgctx_template
->next_subclass
= subclass
;
289 g_hash_table_insert (generic_subclass_hash
, parent
, class);
293 move_subclasses_not_in_image_foreach_func (MonoClass
*class, MonoClass
*subclass
, MonoImage
*image
)
297 if (class->image
== image
) {
298 /* The parent class itself is in the image, so all the
299 subclasses must be in the image, too. If not,
300 we're removing an image containing a class which
301 still has a subclass in another image. */
304 g_assert (subclass
->image
== image
);
305 subclass
= class_lookup_rgctx_template (subclass
)->next_subclass
;
313 MonoRuntimeGenericContextTemplate
*subclass_template
= class_lookup_rgctx_template (subclass
);
314 MonoClass
*next
= subclass_template
->next_subclass
;
316 if (subclass
->image
!= image
) {
317 subclass_template
->next_subclass
= new_list
;
325 g_hash_table_insert (generic_subclass_hash
, class, new_list
);
329 * mono_class_unregister_image_generic_subclasses:
332 * Removes all classes of the image from the generic subclass hash.
333 * Must be called when an image is unloaded.
336 mono_class_unregister_image_generic_subclasses (MonoImage
*image
)
338 GHashTable
*old_hash
;
340 //g_print ("unregistering image %s\n", image->name);
342 if (!generic_subclass_hash
)
347 old_hash
= generic_subclass_hash
;
348 generic_subclass_hash
= g_hash_table_new (mono_aligned_addr_hash
, NULL
);
350 g_hash_table_foreach (old_hash
, (GHFunc
)move_subclasses_not_in_image_foreach_func
, image
);
354 g_hash_table_destroy (old_hash
);
357 static MonoRuntimeGenericContextTemplate
*
358 alloc_template (MonoClass
*class)
360 static gboolean inited
= FALSE
;
361 static int num_allocted
= 0;
362 static int num_bytes
= 0;
364 int size
= sizeof (MonoRuntimeGenericContextTemplate
);
367 mono_counters_register ("RGCTX template num allocted", MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &num_allocted
);
368 mono_counters_register ("RGCTX template bytes allocted", MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &num_bytes
);
375 return mono_image_alloc0 (class->image
, size
);
378 static MonoRuntimeGenericContextOtherInfoTemplate
*
379 alloc_oti (MonoImage
*image
)
381 static gboolean inited
= FALSE
;
382 static int num_allocted
= 0;
383 static int num_bytes
= 0;
385 int size
= sizeof (MonoRuntimeGenericContextOtherInfoTemplate
);
388 mono_counters_register ("RGCTX oti num allocted", MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &num_allocted
);
389 mono_counters_register ("RGCTX oti bytes allocted", MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &num_bytes
);
396 return mono_image_alloc0 (image
, size
);
399 #define MONO_RGCTX_SLOT_USED_MARKER ((gpointer)&mono_defaults.object_class->byval_arg)
402 * LOCKING: templates lock
405 rgctx_template_set_other_slot (MonoImage
*image
, MonoRuntimeGenericContextTemplate
*template, int type_argc
,
406 int slot
, gpointer data
, int info_type
)
408 static gboolean inited
= FALSE
;
409 static int num_markers
= 0;
410 static int num_data
= 0;
413 MonoRuntimeGenericContextOtherInfoTemplate
*list
= get_other_info_templates (template, type_argc
);
414 MonoRuntimeGenericContextOtherInfoTemplate
**oti
= &list
;
417 mono_counters_register ("RGCTX oti num markers", MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &num_markers
);
418 mono_counters_register ("RGCTX oti num data", MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &num_data
);
422 g_assert (slot
>= 0);
430 *oti
= alloc_oti (image
);
434 g_assert (!(*oti
)->data
);
436 (*oti
)->info_type
= info_type
;
438 set_other_info_templates (image
, template, type_argc
, list
);
440 if (data
== MONO_RGCTX_SLOT_USED_MARKER
)
447 * mono_method_get_declaring_generic_method:
448 * @method: an inflated method
450 * Returns an inflated method's declaring method.
453 mono_method_get_declaring_generic_method (MonoMethod
*method
)
455 MonoMethodInflated
*inflated
;
457 g_assert (method
->is_inflated
);
459 inflated
= (MonoMethodInflated
*)method
;
461 return inflated
->declaring
;
465 * mono_class_get_method_generic:
469 * Given a class and a generic method, which has to be of an
470 * instantiation of the same class that klass is an instantiation of,
471 * returns the corresponding method in klass. Example:
473 * klass is Gen<string>
474 * method is Gen<object>.work<int>
476 * returns: Gen<string>.work<int>
479 mono_class_get_method_generic (MonoClass
*klass
, MonoMethod
*method
)
481 MonoMethod
*declaring
, *m
;
484 if (method
->is_inflated
)
485 declaring
= mono_method_get_declaring_generic_method (method
);
490 if (klass
->generic_class
)
491 m
= mono_class_get_inflated_method (klass
, declaring
);
494 mono_class_setup_methods (klass
);
495 for (i
= 0; i
< klass
->method
.count
; ++i
) {
496 m
= klass
->methods
[i
];
499 if (m
->is_inflated
&& mono_method_get_declaring_generic_method (m
) == declaring
)
502 if (i
>= klass
->method
.count
)
506 if (method
!= declaring
) {
507 MonoGenericContext context
;
509 context
.class_inst
= NULL
;
510 context
.method_inst
= mono_method_get_context (method
)->method_inst
;
512 m
= mono_class_inflate_generic_method (m
, &context
);
519 inflate_other_data (gpointer data
, int info_type
, MonoGenericContext
*context
, MonoClass
*class, gboolean temporary
)
523 if (data
== MONO_RGCTX_SLOT_USED_MARKER
)
524 return MONO_RGCTX_SLOT_USED_MARKER
;
528 case MONO_RGCTX_INFO_STATIC_DATA
:
529 case MONO_RGCTX_INFO_KLASS
:
530 case MONO_RGCTX_INFO_VTABLE
:
531 case MONO_RGCTX_INFO_TYPE
:
532 case MONO_RGCTX_INFO_REFLECTION_TYPE
:
533 return mono_class_inflate_generic_type_with_mempool (temporary
? NULL
: class->image
,
536 case MONO_RGCTX_INFO_METHOD
:
537 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE
:
538 case MONO_RGCTX_INFO_METHOD_RGCTX
:
539 case MONO_RGCTX_INFO_METHOD_CONTEXT
: {
540 MonoMethod
*method
= data
;
541 MonoMethod
*inflated_method
;
542 MonoType
*inflated_type
= mono_class_inflate_generic_type (&method
->klass
->byval_arg
, context
);
543 MonoClass
*inflated_class
= mono_class_from_mono_type (inflated_type
);
545 mono_metadata_free_type (inflated_type
);
547 mono_class_init (inflated_class
);
549 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
) {
550 g_assert (info_type
!= MONO_RGCTX_INFO_METHOD_RGCTX
);
551 g_assert (method
->wrapper_type
== MONO_WRAPPER_STATIC_RGCTX_INVOKE
);
553 method
= mono_marshal_method_from_wrapper (method
);
554 method
= mono_class_inflate_generic_method (method
, context
);
555 method
= mono_marshal_get_static_rgctx_invoke (method
);
558 if (inflated_class
->byval_arg
.type
== MONO_TYPE_ARRAY
||
559 inflated_class
->byval_arg
.type
== MONO_TYPE_SZARRAY
) {
560 inflated_method
= mono_method_search_in_array_class (inflated_class
,
561 method
->name
, method
->signature
);
563 inflated_method
= mono_class_inflate_generic_method (method
, context
);
565 mono_class_init (inflated_method
->klass
);
566 g_assert (inflated_method
->klass
== inflated_class
);
567 return inflated_method
;
570 case MONO_RGCTX_INFO_CLASS_FIELD
: {
571 MonoClassField
*field
= data
;
572 MonoType
*inflated_type
= mono_class_inflate_generic_type (&field
->parent
->byval_arg
, context
);
573 MonoClass
*inflated_class
= mono_class_from_mono_type (inflated_type
);
574 int i
= field
- field
->parent
->fields
;
575 gpointer dummy
= NULL
;
577 mono_metadata_free_type (inflated_type
);
579 mono_class_get_fields (inflated_class
, &dummy
);
580 g_assert (inflated_class
->fields
);
582 return &inflated_class
->fields
[i
];
586 g_assert_not_reached ();
591 inflate_other_info (MonoRuntimeGenericContextOtherInfoTemplate
*oti
,
592 MonoGenericContext
*context
, MonoClass
*class, gboolean temporary
)
594 return inflate_other_data (oti
->data
, oti
->info_type
, context
, class, temporary
);
598 free_inflated_info (int info_type
, gpointer info
)
604 case MONO_RGCTX_INFO_STATIC_DATA
:
605 case MONO_RGCTX_INFO_KLASS
:
606 case MONO_RGCTX_INFO_VTABLE
:
607 case MONO_RGCTX_INFO_TYPE
:
608 case MONO_RGCTX_INFO_REFLECTION_TYPE
:
609 mono_metadata_free_type (info
);
616 static MonoRuntimeGenericContextOtherInfoTemplate
617 class_get_rgctx_template_oti (MonoClass
*class, int type_argc
, guint32 slot
, gboolean temporary
, gboolean
*do_free
);
620 * mono_class_get_runtime_generic_context_template:
623 * Looks up or constructs, if necessary, the runtime generic context
626 static MonoRuntimeGenericContextTemplate
*
627 mono_class_get_runtime_generic_context_template (MonoClass
*class)
629 MonoRuntimeGenericContextTemplate
*parent_template
, *template;
630 MonoGenericInst
*inst
;
633 g_assert (!class->generic_class
);
636 template = class_lookup_rgctx_template (class);
642 if (class->generic_container
)
643 inst
= class->generic_container
->context
.class_inst
;
647 template = alloc_template (class);
652 if (class->parent
->generic_class
) {
654 int max_argc
, type_argc
;
656 parent_template
= mono_class_get_runtime_generic_context_template
657 (class->parent
->generic_class
->container_class
);
659 max_argc
= template_get_max_argc (parent_template
);
661 for (type_argc
= 0; type_argc
<= max_argc
; ++type_argc
) {
662 num_entries
= rgctx_template_num_other_infos (parent_template
, type_argc
);
664 /* FIXME: quadratic! */
665 for (i
= 0; i
< num_entries
; ++i
) {
666 MonoRuntimeGenericContextOtherInfoTemplate oti
;
668 oti
= class_get_rgctx_template_oti (class->parent
, type_argc
, i
, FALSE
, NULL
);
669 if (oti
.data
&& oti
.data
!= MONO_RGCTX_SLOT_USED_MARKER
) {
670 rgctx_template_set_other_slot (class->image
, template, type_argc
, i
,
671 oti
.data
, oti
.info_type
);
676 MonoRuntimeGenericContextOtherInfoTemplate
*oti
;
677 int max_argc
, type_argc
;
679 parent_template
= mono_class_get_runtime_generic_context_template (class->parent
);
681 max_argc
= template_get_max_argc (parent_template
);
683 for (type_argc
= 0; type_argc
<= max_argc
; ++type_argc
) {
684 /* FIXME: quadratic! */
685 for (i
= 0, oti
= parent_template
->other_infos
; oti
; ++i
, oti
= oti
->next
) {
686 if (oti
->data
&& oti
->data
!= MONO_RGCTX_SLOT_USED_MARKER
) {
687 rgctx_template_set_other_slot (class->image
, template, type_argc
, i
,
688 oti
->data
, oti
->info_type
);
695 if (class_lookup_rgctx_template (class)) {
696 /* some other thread already set the template */
697 template = class_lookup_rgctx_template (class);
699 class_set_rgctx_template (class, template);
702 register_generic_subclass (class);
711 * temporary signifies whether the inflated info (oti.data) will be
712 * used temporarily, in which case it might be heap-allocated, or
713 * permanently, in which case it will be mempool-allocated. If
714 * temporary is set then *do_free will return whether the returned
715 * data must be freed.
717 static MonoRuntimeGenericContextOtherInfoTemplate
718 class_get_rgctx_template_oti (MonoClass
*class, int type_argc
, guint32 slot
, gboolean temporary
, gboolean
*do_free
)
720 g_assert ((temporary
&& do_free
) || (!temporary
&& !do_free
));
722 if (class->generic_class
) {
723 MonoRuntimeGenericContextOtherInfoTemplate oti
;
724 gboolean tmp_do_free
;
726 oti
= class_get_rgctx_template_oti (class->generic_class
->container_class
,
727 type_argc
, slot
, TRUE
, &tmp_do_free
);
729 gpointer info
= oti
.data
;
730 oti
.data
= inflate_other_info (&oti
, &class->generic_class
->context
, class, temporary
);
732 free_inflated_info (oti
.info_type
, info
);
739 MonoRuntimeGenericContextTemplate
*template;
740 MonoRuntimeGenericContextOtherInfoTemplate
*oti
;
742 template = mono_class_get_runtime_generic_context_template (class);
743 oti
= rgctx_template_get_other_slot (template, type_argc
, slot
);
754 class_uninstantiated (MonoClass
*class)
756 if (class->generic_class
)
757 return class->generic_class
->container_class
;
762 class_type_info (MonoDomain
*domain
, MonoClass
*class, int info_type
)
765 case MONO_RGCTX_INFO_STATIC_DATA
:
766 return mono_class_vtable (domain
, class)->data
;
767 case MONO_RGCTX_INFO_KLASS
:
769 case MONO_RGCTX_INFO_VTABLE
:
770 return mono_class_vtable (domain
, class);
772 g_assert_not_reached ();
777 instantiate_other_info (MonoDomain
*domain
, MonoRuntimeGenericContextOtherInfoTemplate
*oti
,
778 MonoGenericContext
*context
, MonoClass
*class)
786 switch (oti
->info_type
) {
787 case MONO_RGCTX_INFO_STATIC_DATA
:
788 case MONO_RGCTX_INFO_KLASS
:
789 case MONO_RGCTX_INFO_VTABLE
:
796 data
= inflate_other_info (oti
, context
, class, temporary
);
798 switch (oti
->info_type
) {
799 case MONO_RGCTX_INFO_STATIC_DATA
:
800 case MONO_RGCTX_INFO_KLASS
:
801 case MONO_RGCTX_INFO_VTABLE
: {
802 MonoClass
*arg_class
= mono_class_from_mono_type (data
);
804 free_inflated_info (oti
->info_type
, data
);
805 g_assert (arg_class
);
807 return class_type_info (domain
, arg_class
, oti
->info_type
);
809 case MONO_RGCTX_INFO_TYPE
:
811 case MONO_RGCTX_INFO_REFLECTION_TYPE
:
812 return mono_type_get_object (domain
, data
);
813 case MONO_RGCTX_INFO_METHOD
:
815 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE
:
816 return mono_create_ftnptr (mono_domain_get (),
817 mono_runtime_create_jump_trampoline (mono_domain_get (), data
, TRUE
));
818 case MONO_RGCTX_INFO_CLASS_FIELD
:
820 case MONO_RGCTX_INFO_METHOD_RGCTX
: {
821 MonoMethodInflated
*method
= data
;
823 g_assert (method
->method
.method
.is_inflated
);
824 g_assert (method
->context
.method_inst
);
826 return mono_method_lookup_rgctx (mono_class_vtable (domain
, method
->method
.method
.klass
),
827 method
->context
.method_inst
);
829 case MONO_RGCTX_INFO_METHOD_CONTEXT
: {
830 MonoMethodInflated
*method
= data
;
832 g_assert (method
->method
.method
.is_inflated
);
833 g_assert (method
->context
.method_inst
);
835 return method
->context
.method_inst
;
838 g_assert_not_reached ();
843 * LOCKING: templates lock
846 fill_in_rgctx_template_slot (MonoClass
*class, int type_argc
, int index
, gpointer data
, int info_type
)
848 MonoRuntimeGenericContextTemplate
*template = mono_class_get_runtime_generic_context_template (class);
851 g_assert (!class->generic_class
);
853 rgctx_template_set_other_slot (class->image
, template, type_argc
, index
, data
, info_type
);
855 /* Recurse for all subclasses */
856 if (generic_subclass_hash
)
857 subclass
= g_hash_table_lookup (generic_subclass_hash
, class);
862 MonoRuntimeGenericContextOtherInfoTemplate subclass_oti
;
863 MonoRuntimeGenericContextTemplate
*subclass_template
= class_lookup_rgctx_template (subclass
);
865 g_assert (!subclass
->generic_class
);
866 g_assert (subclass_template
);
868 subclass_oti
= class_get_rgctx_template_oti (subclass
->parent
, type_argc
, index
, FALSE
, NULL
);
869 g_assert (subclass_oti
.data
);
871 fill_in_rgctx_template_slot (subclass
, type_argc
, index
, subclass_oti
.data
, info_type
);
873 subclass
= subclass_template
->next_subclass
;
878 * LOCKING: templates lock
881 register_other_info (MonoClass
*class, int type_argc
, gpointer data
, int info_type
)
884 MonoRuntimeGenericContextTemplate
*template = mono_class_get_runtime_generic_context_template (class);
886 MonoRuntimeGenericContextOtherInfoTemplate
*oti
;
888 for (i
= 0, oti
= get_other_info_templates (template, type_argc
); oti
; ++i
, oti
= oti
->next
) {
893 //g_print ("template %s . other_infos [%d] = %s\n", mono_type_get_full_name (class), i, mono_type_get_full_name (other_class));
895 /* Mark the slot as used in all parent classes (until we find
896 a parent class which already has it marked used). */
897 parent
= class->parent
;
898 while (parent
!= NULL
) {
899 MonoRuntimeGenericContextTemplate
*parent_template
;
900 MonoRuntimeGenericContextOtherInfoTemplate
*oti
;
902 if (parent
->generic_class
)
903 parent
= parent
->generic_class
->container_class
;
905 parent_template
= mono_class_get_runtime_generic_context_template (parent
);
906 oti
= rgctx_template_get_other_slot (parent_template
, type_argc
, i
);
908 if (oti
&& oti
->data
)
911 rgctx_template_set_other_slot (parent
->image
, parent_template
, type_argc
, i
,
912 MONO_RGCTX_SLOT_USED_MARKER
, 0);
914 parent
= parent
->parent
;
917 /* Fill in the slot in this class and in all subclasses
919 fill_in_rgctx_template_slot (class, type_argc
, i
, data
, info_type
);
925 other_info_equal (gpointer data1
, gpointer data2
, int info_type
)
928 case MONO_RGCTX_INFO_STATIC_DATA
:
929 case MONO_RGCTX_INFO_KLASS
:
930 case MONO_RGCTX_INFO_VTABLE
:
931 case MONO_RGCTX_INFO_TYPE
:
932 case MONO_RGCTX_INFO_REFLECTION_TYPE
:
933 return mono_class_from_mono_type (data1
) == mono_class_from_mono_type (data2
);
934 case MONO_RGCTX_INFO_METHOD
:
935 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE
:
936 case MONO_RGCTX_INFO_CLASS_FIELD
:
937 case MONO_RGCTX_INFO_METHOD_RGCTX
:
938 case MONO_RGCTX_INFO_METHOD_CONTEXT
:
939 return data1
== data2
;
941 g_assert_not_reached ();
946 lookup_or_register_other_info (MonoClass
*class, int type_argc
, gpointer data
, int info_type
,
947 MonoGenericContext
*generic_context
)
949 static gboolean inited
= FALSE
;
950 static int max_slot
= 0;
952 MonoRuntimeGenericContextTemplate
*rgctx_template
=
953 mono_class_get_runtime_generic_context_template (class);
954 MonoRuntimeGenericContextOtherInfoTemplate
*oti_list
, *oti
, *copy
;
957 g_assert (!class->generic_class
);
958 g_assert (class->generic_container
|| type_argc
);
961 * We must not call inflate_other_info() with the templates
962 * lock held, because it calls metadata functions which might
963 * cause the loader lock to be taken, which must not happen if
964 * the templates lock is held.
966 * Only two things can happen to an oti list: An unused
967 * (data==NULL) node can be filled in and nodes can be
968 * appended at the end of the list.
970 * To solve the lock problem we first count the number of
971 * nodes in the list, then copy all the data into a separate
972 * array. With the templates lock not held we then search for
973 * our info in the array - this is where the calls to
974 * inflate_other_info() happen. If we don't find the info
975 * we're looking for, we take the templates lock again and
976 * check if the oti list has changed since we've copied it.
977 * If it has, we start again. If it hasn't, we register the
984 oti_list
= get_other_info_templates (rgctx_template
, type_argc
);
987 for (oti
= oti_list
; oti
; oti
= oti
->next
)
990 copy
= g_new (MonoRuntimeGenericContextOtherInfoTemplate
, length
);
992 for (oti
= oti_list
, i
= 0; oti
; oti
= oti
->next
, ++i
) {
993 copy
[i
].info_type
= oti
->info_type
;
994 copy
[i
].data
= oti
->data
;
996 g_assert (i
== length
);
1000 /* We've copied the list. Now look for the info. */
1002 for (i
= 0; i
< length
; ++i
) {
1003 gpointer inflated_data
;
1005 if (copy
[i
].info_type
!= info_type
|| !copy
[i
].data
)
1008 inflated_data
= inflate_other_info (©
[i
], generic_context
, class, TRUE
);
1010 if (other_info_equal (data
, inflated_data
, info_type
)) {
1011 free_inflated_info (info_type
, inflated_data
);
1015 free_inflated_info (info_type
, inflated_data
);
1018 /* We haven't found the info, so check if the list is still
1023 /* We need to fetch oti_list again here because the list could
1025 oti_list
= get_other_info_templates (rgctx_template
, type_argc
);
1027 for (oti
= oti_list
, i
= 0; i
< length
; oti
= oti
->next
, ++i
) {
1030 if (copy
[i
].info_type
!= oti
->info_type
|| copy
[i
].data
!= oti
->data
) {
1039 /* The list is still the same - success. */
1041 i
= register_other_info (class, type_argc
, data
, info_type
);
1043 templates_unlock ();
1046 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &max_slot
);
1056 * mono_method_lookup_or_register_other_info:
1058 * @in_mrgctx: whether to put the data into the MRGCTX
1059 * @data: the info data
1060 * @info_type: the type of info to register about data
1061 * @generic_context: a generic context
1063 * Looks up and, if necessary, adds information about other_class in
1064 * method's or method's class runtime generic context. Returns the
1065 * encoded slot number.
1068 mono_method_lookup_or_register_other_info (MonoMethod
*method
, gboolean in_mrgctx
, gpointer data
,
1069 int info_type
, MonoGenericContext
*generic_context
)
1071 MonoClass
*class = method
->klass
;
1072 int type_argc
, index
;
1075 MonoGenericInst
*method_inst
= mono_method_get_context (method
)->method_inst
;
1077 g_assert (method
->is_inflated
&& method_inst
);
1078 type_argc
= method_inst
->type_argc
;
1079 g_assert (type_argc
> 0);
1084 index
= lookup_or_register_other_info (class, type_argc
, data
, info_type
, generic_context
);
1086 //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
1089 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index
);
1091 return MONO_RGCTX_SLOT_MAKE_RGCTX (index
);
1095 * mono_class_rgctx_get_array_size:
1096 * @n: The number of the array
1097 * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
1099 * Returns the number of slots in the n'th array of a (M)RGCTX. That
1100 * number includes the slot for linking and - for MRGCTXs - the two
1101 * slots in the first array for additional information.
1104 mono_class_rgctx_get_array_size (int n
, gboolean mrgctx
)
1106 g_assert (n
>= 0 && n
< 30);
1115 * LOCKING: domain lock
1118 alloc_rgctx_array (MonoDomain
*domain
, int n
, gboolean is_mrgctx
)
1120 static gboolean inited
= FALSE
;
1121 static int rgctx_num_alloced
= 0;
1122 static int rgctx_bytes_alloced
= 0;
1123 static int mrgctx_num_alloced
= 0;
1124 static int mrgctx_bytes_alloced
= 0;
1126 int size
= mono_class_rgctx_get_array_size (n
, is_mrgctx
) * sizeof (gpointer
);
1127 gpointer array
= mono_domain_alloc0 (domain
, size
);
1130 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &rgctx_num_alloced
);
1131 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &rgctx_bytes_alloced
);
1132 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &mrgctx_num_alloced
);
1133 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &mrgctx_bytes_alloced
);
1138 mrgctx_num_alloced
++;
1139 mrgctx_bytes_alloced
+= size
;
1141 rgctx_num_alloced
++;
1142 rgctx_bytes_alloced
+= size
;
1149 fill_runtime_generic_context (MonoVTable
*class_vtable
, MonoRuntimeGenericContext
*rgctx
, guint32 slot
,
1150 MonoGenericInst
*method_inst
)
1153 int i
, first_slot
, size
;
1154 MonoDomain
*domain
= class_vtable
->domain
;
1155 MonoClass
*class = class_vtable
->klass
;
1156 MonoGenericContext
*class_context
= class->generic_class
? &class->generic_class
->context
: NULL
;
1157 MonoRuntimeGenericContextOtherInfoTemplate oti
;
1158 MonoGenericContext context
= { class_context
? class_context
->class_inst
: NULL
, method_inst
};
1164 mono_domain_lock (domain
);
1166 /* First check whether that slot isn't already instantiated.
1167 This might happen because lookup doesn't lock. Allocate
1168 arrays on the way. */
1170 size
= mono_class_rgctx_get_array_size (0, method_inst
!= NULL
);
1172 size
-= sizeof (MonoMethodRuntimeGenericContext
) / sizeof (gpointer
);
1173 for (i
= 0; ; ++i
) {
1176 if (method_inst
&& i
== 0)
1177 offset
= sizeof (MonoMethodRuntimeGenericContext
) / sizeof (gpointer
);
1181 if (slot
< first_slot
+ size
- 1) {
1182 rgctx_index
= slot
- first_slot
+ 1 + offset
;
1183 info
= rgctx
[rgctx_index
];
1185 mono_domain_unlock (domain
);
1190 if (!rgctx
[offset
+ 0])
1191 rgctx
[offset
+ 0] = alloc_rgctx_array (domain
, i
+ 1, method_inst
!= NULL
);
1192 rgctx
= rgctx
[offset
+ 0];
1193 first_slot
+= size
- 1;
1194 size
= mono_class_rgctx_get_array_size (i
+ 1, method_inst
!= NULL
);
1197 g_assert (!rgctx
[rgctx_index
]);
1199 mono_domain_unlock (domain
);
1201 oti
= class_get_rgctx_template_oti (class_uninstantiated (class),
1202 method_inst
? method_inst
->type_argc
: 0, slot
, TRUE
, &do_free
);
1203 /* This might take the loader lock */
1204 info
= instantiate_other_info (domain
, &oti
, &context
, class);
1208 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
1211 /*FIXME We should use CAS here, no need to take a lock.*/
1212 mono_domain_lock (domain
);
1214 /* Check whether the slot hasn't been instantiated in the
1216 if (rgctx
[rgctx_index
])
1217 info
= rgctx
[rgctx_index
];
1219 rgctx
[rgctx_index
] = info
;
1221 mono_domain_unlock (domain
);
1224 free_inflated_info (oti
.info_type
, oti
.data
);
1230 * mono_class_fill_runtime_generic_context:
1231 * @class_vtable: a vtable
1232 * @slot: a slot index to be instantiated
1234 * Instantiates a slot in the RGCTX.
1237 mono_class_fill_runtime_generic_context (MonoVTable
*class_vtable
, guint32 slot
)
1239 static gboolean inited
= FALSE
;
1240 static int num_alloced
= 0;
1242 MonoDomain
*domain
= class_vtable
->domain
;
1243 MonoRuntimeGenericContext
*rgctx
;
1246 mono_domain_lock (domain
);
1249 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &num_alloced
);
1253 rgctx
= class_vtable
->runtime_generic_context
;
1255 rgctx
= alloc_rgctx_array (domain
, 0, FALSE
);
1256 class_vtable
->runtime_generic_context
= rgctx
;
1260 mono_domain_unlock (domain
);
1262 info
= fill_runtime_generic_context (class_vtable
, rgctx
, slot
, 0);
1268 * mono_method_fill_runtime_generic_context:
1269 * @mrgctx: an MRGCTX
1270 * @slot: a slot index to be instantiated
1272 * Instantiates a slot in the MRGCTX.
1275 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext
*mrgctx
, guint32 slot
)
1279 info
= fill_runtime_generic_context (mrgctx
->class_vtable
, (MonoRuntimeGenericContext
*)mrgctx
, slot
,
1280 mrgctx
->method_inst
);
1286 mrgctx_hash_func (gconstpointer key
)
1288 const MonoMethodRuntimeGenericContext
*mrgctx
= key
;
1290 return mono_aligned_addr_hash (mrgctx
->class_vtable
) ^ mono_metadata_generic_inst_hash (mrgctx
->method_inst
);
1294 mrgctx_equal_func (gconstpointer a
, gconstpointer b
)
1296 const MonoMethodRuntimeGenericContext
*mrgctx1
= a
;
1297 const MonoMethodRuntimeGenericContext
*mrgctx2
= b
;
1299 return mrgctx1
->class_vtable
== mrgctx2
->class_vtable
&&
1300 mono_metadata_generic_inst_equal (mrgctx1
->method_inst
, mrgctx2
->method_inst
);
1304 * mono_method_lookup_rgctx:
1305 * @class_vtable: a vtable
1306 * @method_inst: the method inst of a generic method
1308 * Returns the MRGCTX for the generic method(s) with the given
1309 * method_inst of the given class_vtable.
1311 * LOCKING: Take the domain lock.
1313 MonoMethodRuntimeGenericContext
*
1314 mono_method_lookup_rgctx (MonoVTable
*class_vtable
, MonoGenericInst
*method_inst
)
1316 MonoDomain
*domain
= class_vtable
->domain
;
1317 MonoMethodRuntimeGenericContext
*mrgctx
;
1318 MonoMethodRuntimeGenericContext key
;
1320 g_assert (!class_vtable
->klass
->generic_container
);
1321 g_assert (!method_inst
->is_open
);
1323 mono_domain_lock (domain
);
1324 if (!domain
->method_rgctx_hash
)
1325 domain
->method_rgctx_hash
= g_hash_table_new (mrgctx_hash_func
, mrgctx_equal_func
);
1327 key
.class_vtable
= class_vtable
;
1328 key
.method_inst
= method_inst
;
1330 mrgctx
= g_hash_table_lookup (domain
->method_rgctx_hash
, &key
);
1335 mrgctx
= (MonoMethodRuntimeGenericContext
*)alloc_rgctx_array (domain
, 0, TRUE
);
1336 mrgctx
->class_vtable
= class_vtable
;
1337 mrgctx
->method_inst
= method_inst
;
1339 g_hash_table_insert (domain
->method_rgctx_hash
, mrgctx
, mrgctx
);
1342 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
1343 for (i = 0; i < method_inst->type_argc; ++i)
1344 g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
1349 mono_domain_unlock (domain
);
1357 generic_inst_is_sharable (MonoGenericInst
*inst
, gboolean allow_type_vars
)
1361 for (i
= 0; i
< inst
->type_argc
; ++i
) {
1362 MonoType
*type
= inst
->type_argv
[i
];
1365 if (MONO_TYPE_IS_REFERENCE (type
))
1368 type_type
= mono_type_get_type (type
);
1369 if (allow_type_vars
&& (type_type
== MONO_TYPE_VAR
|| type_type
== MONO_TYPE_MVAR
))
1379 * mono_generic_context_is_sharable:
1380 * @context: a generic context
1382 * Returns whether the generic context is sharable. A generic context
1383 * is sharable iff all of its type arguments are reference type.
1386 mono_generic_context_is_sharable (MonoGenericContext
*context
, gboolean allow_type_vars
)
1388 g_assert (context
->class_inst
|| context
->method_inst
);
1390 if (context
->class_inst
&& !generic_inst_is_sharable (context
->class_inst
, allow_type_vars
))
1393 if (context
->method_inst
&& !generic_inst_is_sharable (context
->method_inst
, allow_type_vars
))
1400 * mono_method_is_generic_impl:
1403 * Returns whether the method is either generic or part of a generic
1407 mono_method_is_generic_impl (MonoMethod
*method
)
1409 if (method
->is_inflated
) {
1410 g_assert (method
->wrapper_type
== MONO_WRAPPER_NONE
);
1413 /* We don't treat wrappers as generic code, i.e., we never
1414 apply generic sharing to them. This is especially
1415 important for static rgctx invoke wrappers, which only work
1416 if not compiled with sharing. */
1417 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
1419 if (method
->klass
->generic_container
)
1425 has_constraints (MonoGenericContainer
*container
)
1431 g_assert (container->type_argc > 0);
1432 g_assert (container->type_params);
1434 for (i = 0; i < container->type_argc; ++i)
1435 if (container->type_params [i].constraints)
1442 * mono_method_is_generic_sharable_impl:
1444 * @allow_type_vars: whether to regard type variables as reference types
1446 * Returns TRUE iff the method is inflated or part of an inflated
1447 * class, its context is sharable and it has no constraints on its
1448 * type parameters. Otherwise returns FALSE.
1451 mono_method_is_generic_sharable_impl (MonoMethod
*method
, gboolean allow_type_vars
)
1453 if (!mono_method_is_generic_impl (method
))
1456 if (method
->is_inflated
) {
1457 MonoMethodInflated
*inflated
= (MonoMethodInflated
*)method
;
1458 MonoGenericContext
*context
= &inflated
->context
;
1460 if (!mono_generic_context_is_sharable (context
, allow_type_vars
))
1463 g_assert (inflated
->declaring
);
1465 if (inflated
->declaring
->is_generic
) {
1466 if (has_constraints (mono_method_get_generic_container (inflated
->declaring
)))
1471 if (method
->klass
->generic_class
) {
1472 if (!mono_generic_context_is_sharable (&method
->klass
->generic_class
->context
, allow_type_vars
))
1475 g_assert (method
->klass
->generic_class
->container_class
&&
1476 method
->klass
->generic_class
->container_class
->generic_container
);
1478 if (has_constraints (method
->klass
->generic_class
->container_class
->generic_container
))
1482 if (method
->klass
->generic_container
&& !allow_type_vars
)
1489 mono_method_needs_static_rgctx_invoke (MonoMethod
*method
, gboolean allow_type_vars
)
1491 if (!mono_class_generic_sharing_enabled (method
->klass
))
1494 if (!mono_method_is_generic_sharable_impl (method
, allow_type_vars
))
1497 if (method
->is_inflated
&& mono_method_get_context (method
)->method_inst
)
1500 return ((method
->flags
& METHOD_ATTRIBUTE_STATIC
) ||
1501 method
->klass
->valuetype
) &&
1502 (method
->klass
->generic_class
|| method
->klass
->generic_container
);
1505 static MonoGenericInst
*
1506 get_object_generic_inst (int type_argc
)
1508 MonoType
**type_argv
;
1511 type_argv
= alloca (sizeof (MonoType
*) * type_argc
);
1513 for (i
= 0; i
< type_argc
; ++i
)
1514 type_argv
[i
] = &mono_defaults
.object_class
->byval_arg
;
1516 return mono_metadata_get_generic_inst (type_argc
, type_argv
);
1520 * mono_method_construct_object_context:
1523 * Returns a generic context for method with all type variables for
1524 * class and method instantiated with Object.
1527 mono_method_construct_object_context (MonoMethod
*method
)
1529 MonoGenericContext object_context
;
1531 g_assert (!method
->klass
->generic_class
);
1532 if (method
->klass
->generic_container
) {
1533 int type_argc
= method
->klass
->generic_container
->type_argc
;
1535 object_context
.class_inst
= get_object_generic_inst (type_argc
);
1537 object_context
.class_inst
= NULL
;
1540 if (mono_method_get_context_general (method
, TRUE
)->method_inst
) {
1541 int type_argc
= mono_method_get_context_general (method
, TRUE
)->method_inst
->type_argc
;
1543 object_context
.method_inst
= get_object_generic_inst (type_argc
);
1545 object_context
.method_inst
= NULL
;
1548 g_assert (object_context
.class_inst
|| object_context
.method_inst
);
1550 return object_context
;
1554 * mono_domain_lookup_shared_generic:
1556 * @open_method: an open generic method
1558 * Looks up the jit info for method via the domain's jit code hash.
1561 mono_domain_lookup_shared_generic (MonoDomain
*domain
, MonoMethod
*open_method
)
1563 static gboolean inited
= FALSE
;
1564 static int lookups
= 0;
1565 static int failed_lookups
= 0;
1567 MonoGenericContext object_context
;
1568 MonoMethod
*object_method
;
1571 object_context
= mono_method_construct_object_context (open_method
);
1572 object_method
= mono_class_inflate_generic_method (open_method
, &object_context
);
1574 mono_domain_jit_code_hash_lock (domain
);
1575 ji
= mono_internal_hash_table_lookup (&domain
->jit_code_hash
, object_method
);
1576 if (ji
&& !ji
->has_generic_jit_info
)
1578 mono_domain_jit_code_hash_unlock (domain
);
1581 mono_counters_register ("Shared generic lookups", MONO_COUNTER_INT
|MONO_COUNTER_GENERICS
, &lookups
);
1582 mono_counters_register ("Failed shared generic lookups", MONO_COUNTER_INT
|MONO_COUNTER_GENERICS
, &failed_lookups
);