2009-05-22 Miguel de Icaza <miguel@novell.com>
[mono/afaerber.git] / mono / metadata / generic-sharing.c
blobb54eadb3b6ca7446d235636862fd718b7e949c30
1 /*
2 * generic-sharing.c: Support functions for generic sharing.
4 * Author:
5 * Mark Probst (mark.probst@gmail.com)
7 * Copyright 2007-2009 Novell, Inc (http://www.novell.com)
8 */
10 #include <config.h>
11 #include <string.h>
12 #ifdef HAVE_ALLOCA_H
13 #include <alloca.h>
14 #endif
16 #ifdef _MSC_VER
17 #include <glib.h>
18 #endif
19 #include <mono/utils/mono-membar.h>
20 #include <mono/utils/mono-counters.h>
22 #include "metadata-internals.h"
23 #include "class.h"
24 #include "class-internals.h"
25 #include "marshal.h"
26 #include "debug-helpers.h"
27 #include "tabledefs.h"
28 #include "mempool-internals.h"
30 static int
31 type_check_context_used (MonoType *type, gboolean recursive)
33 switch (mono_type_get_type (type)) {
34 case MONO_TYPE_VAR:
35 return MONO_GENERIC_CONTEXT_USED_CLASS;
36 case MONO_TYPE_MVAR:
37 return MONO_GENERIC_CONTEXT_USED_METHOD;
38 case MONO_TYPE_SZARRAY:
39 return mono_class_check_context_used (mono_type_get_class (type));
40 case MONO_TYPE_ARRAY:
41 return mono_class_check_context_used (mono_type_get_array_type (type)->eklass);
42 case MONO_TYPE_CLASS:
43 if (recursive)
44 return mono_class_check_context_used (mono_type_get_class (type));
45 else
46 return 0;
47 case MONO_TYPE_GENERICINST:
48 if (recursive) {
49 MonoGenericClass *gclass = type->data.generic_class;
51 g_assert (gclass->container_class->generic_container);
52 return mono_generic_context_check_used (&gclass->context);
53 } else {
54 return 0;
56 default:
57 return 0;
61 static int
62 inst_check_context_used (MonoGenericInst *inst)
64 int context_used = 0;
65 int i;
67 if (!inst)
68 return 0;
70 for (i = 0; i < inst->type_argc; ++i)
71 context_used |= type_check_context_used (inst->type_argv [i], TRUE);
73 return context_used;
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.
84 int
85 mono_generic_context_check_used (MonoGenericContext *context)
87 int context_used = 0;
89 context_used |= inst_check_context_used (context->class_inst);
90 context_used |= inst_check_context_used (context->method_inst);
92 return context_used;
96 * mono_class_check_context_used:
97 * @class: a class
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
102 * variables.
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);
117 return context_used;
121 * Guards the two global rgctx (template) hash tables and all rgctx
122 * templates.
124 * Ordering: The templates lock can be taken while the loader lock is
125 * held.
127 static CRITICAL_SECTION templates_mutex;
129 static void
130 templates_lock (void)
132 static gboolean inited = FALSE;
134 if (!inited) {
135 mono_loader_lock ();
136 if (!inited) {
137 InitializeCriticalSection (&templates_mutex);
138 inited = TRUE;
140 mono_loader_unlock ();
143 EnterCriticalSection (&templates_mutex);
146 static void
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);
159 if (type_argc == 0)
160 return template->other_infos;
161 return g_slist_nth_data (template->method_templates, type_argc - 1);
165 * LOCKING: templates lock
167 static void
168 set_other_info_templates (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
169 MonoRuntimeGenericContextOtherInfoTemplate *oti)
171 g_assert (type_argc >= 0);
172 if (type_argc == 0)
173 template->other_infos = oti;
174 else {
175 int length = g_slist_length (template->method_templates);
176 GSList *list;
178 /* FIXME: quadratic! */
179 while (length < type_argc) {
180 template->method_templates = g_slist_append_image (image, template->method_templates, NULL);
181 length++;
184 list = g_slist_nth (template->method_templates, type_argc - 1);
185 g_assert (list);
186 list->data = oti;
191 * LOCKING: templates lock
193 static int
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)
205 int i;
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) {
211 if (!oti)
212 return NULL;
215 return oti;
219 * LOCKING: templates lock
221 static int
222 rgctx_template_num_other_infos (MonoRuntimeGenericContextTemplate *template, int type_argc)
224 MonoRuntimeGenericContextOtherInfoTemplate *oti;
225 int i;
227 for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next)
230 return i;
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
244 static void
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)
262 return NULL;
264 template = g_hash_table_lookup (class->image->rgctx_template_hash, class);
266 return template;
270 * LOCKING: templates lock
272 static void
273 register_generic_subclass (MonoClass *class)
275 MonoClass *parent = class->parent;
276 MonoClass *subclass;
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);
292 static void
293 move_subclasses_not_in_image_foreach_func (MonoClass *class, MonoClass *subclass, MonoImage *image)
295 MonoClass *new_list;
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. */
303 while (subclass) {
304 g_assert (subclass->image == image);
305 subclass = class_lookup_rgctx_template (subclass)->next_subclass;
308 return;
311 new_list = NULL;
312 while (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;
318 new_list = subclass;
321 subclass = next;
324 if (new_list)
325 g_hash_table_insert (generic_subclass_hash, class, new_list);
329 * mono_class_unregister_image_generic_subclasses:
330 * @image: an image
332 * Removes all classes of the image from the generic subclass hash.
333 * Must be called when an image is unloaded.
335 void
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)
343 return;
345 templates_lock ();
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);
352 templates_unlock ();
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);
366 if (!inited) {
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);
369 inited = TRUE;
372 num_allocted++;
373 num_bytes += size;
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);
387 if (!inited) {
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);
390 inited = TRUE;
393 num_allocted++;
394 num_bytes += size;
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
404 static void
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;
412 int i;
413 MonoRuntimeGenericContextOtherInfoTemplate *list = get_other_info_templates (template, type_argc);
414 MonoRuntimeGenericContextOtherInfoTemplate **oti = &list;
416 if (!inited) {
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);
419 inited = TRUE;
422 g_assert (slot >= 0);
423 g_assert (data);
425 i = 0;
426 while (i <= slot) {
427 if (i > 0)
428 oti = &(*oti)->next;
429 if (!*oti)
430 *oti = alloc_oti (image);
431 ++i;
434 g_assert (!(*oti)->data);
435 (*oti)->data = 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)
441 ++num_markers;
442 else
443 ++num_data;
447 * mono_method_get_declaring_generic_method:
448 * @method: an inflated method
450 * Returns an inflated method's declaring method.
452 MonoMethod*
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:
466 * @klass: a class
467 * @method: a method
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>
478 MonoMethod*
479 mono_class_get_method_generic (MonoClass *klass, MonoMethod *method)
481 MonoMethod *declaring, *m;
482 int i;
484 if (method->is_inflated)
485 declaring = mono_method_get_declaring_generic_method (method);
486 else
487 declaring = method;
489 m = NULL;
490 if (klass->generic_class)
491 m = mono_class_get_inflated_method (klass, declaring);
493 if (!m) {
494 mono_class_setup_methods (klass);
495 for (i = 0; i < klass->method.count; ++i) {
496 m = klass->methods [i];
497 if (m == declaring)
498 break;
499 if (m->is_inflated && mono_method_get_declaring_generic_method (m) == declaring)
500 break;
502 if (i >= klass->method.count)
503 return NULL;
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);
515 return m;
518 static gpointer
519 inflate_other_data (gpointer data, int info_type, MonoGenericContext *context, MonoClass *class, gboolean temporary)
521 g_assert (data);
523 if (data == MONO_RGCTX_SLOT_USED_MARKER)
524 return MONO_RGCTX_SLOT_USED_MARKER;
526 switch (info_type)
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,
534 data, context);
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);
562 } else {
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];
585 default:
586 g_assert_not_reached ();
588 /* Not reached, quiet compiler */
589 return NULL;
592 static gpointer
593 inflate_other_info (MonoRuntimeGenericContextOtherInfoTemplate *oti,
594 MonoGenericContext *context, MonoClass *class, gboolean temporary)
596 return inflate_other_data (oti->data, oti->info_type, context, class, temporary);
599 static void
600 free_inflated_info (int info_type, gpointer info)
602 if (!info)
603 return;
605 switch (info_type) {
606 case MONO_RGCTX_INFO_STATIC_DATA:
607 case MONO_RGCTX_INFO_KLASS:
608 case MONO_RGCTX_INFO_VTABLE:
609 case MONO_RGCTX_INFO_TYPE:
610 case MONO_RGCTX_INFO_REFLECTION_TYPE:
611 mono_metadata_free_type (info);
612 break;
613 default:
614 break;
618 static MonoRuntimeGenericContextOtherInfoTemplate
619 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free);
622 * mono_class_get_runtime_generic_context_template:
623 * @class: a class
625 * Looks up or constructs, if necessary, the runtime generic context
626 * for class.
628 static MonoRuntimeGenericContextTemplate*
629 mono_class_get_runtime_generic_context_template (MonoClass *class)
631 MonoRuntimeGenericContextTemplate *parent_template, *template;
632 MonoGenericInst *inst;
633 guint32 i;
635 g_assert (!class->generic_class);
637 templates_lock ();
638 template = class_lookup_rgctx_template (class);
639 templates_unlock ();
641 if (template)
642 return template;
644 if (class->generic_container)
645 inst = class->generic_container->context.class_inst;
646 else
647 inst = NULL;
649 template = alloc_template (class);
651 templates_lock ();
653 if (class->parent) {
654 if (class->parent->generic_class) {
655 guint32 num_entries;
656 int max_argc, type_argc;
658 parent_template = mono_class_get_runtime_generic_context_template
659 (class->parent->generic_class->container_class);
661 max_argc = template_get_max_argc (parent_template);
663 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
664 num_entries = rgctx_template_num_other_infos (parent_template, type_argc);
666 /* FIXME: quadratic! */
667 for (i = 0; i < num_entries; ++i) {
668 MonoRuntimeGenericContextOtherInfoTemplate oti;
670 oti = class_get_rgctx_template_oti (class->parent, type_argc, i, FALSE, NULL);
671 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
672 rgctx_template_set_other_slot (class->image, template, type_argc, i,
673 oti.data, oti.info_type);
677 } else {
678 MonoRuntimeGenericContextOtherInfoTemplate *oti;
679 int max_argc, type_argc;
681 parent_template = mono_class_get_runtime_generic_context_template (class->parent);
683 max_argc = template_get_max_argc (parent_template);
685 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
686 /* FIXME: quadratic! */
687 for (i = 0, oti = parent_template->other_infos; oti; ++i, oti = oti->next) {
688 if (oti->data && oti->data != MONO_RGCTX_SLOT_USED_MARKER) {
689 rgctx_template_set_other_slot (class->image, template, type_argc, i,
690 oti->data, oti->info_type);
697 if (class_lookup_rgctx_template (class)) {
698 /* some other thread already set the template */
699 template = class_lookup_rgctx_template (class);
700 } else {
701 class_set_rgctx_template (class, template);
703 if (class->parent)
704 register_generic_subclass (class);
707 templates_unlock ();
709 return template;
713 * temporary signifies whether the inflated info (oti.data) will be
714 * used temporarily, in which case it might be heap-allocated, or
715 * permanently, in which case it will be mempool-allocated. If
716 * temporary is set then *do_free will return whether the returned
717 * data must be freed.
719 static MonoRuntimeGenericContextOtherInfoTemplate
720 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free)
722 g_assert ((temporary && do_free) || (!temporary && !do_free));
724 if (class->generic_class) {
725 MonoRuntimeGenericContextOtherInfoTemplate oti;
726 gboolean tmp_do_free;
728 oti = class_get_rgctx_template_oti (class->generic_class->container_class,
729 type_argc, slot, TRUE, &tmp_do_free);
730 if (oti.data) {
731 gpointer info = oti.data;
732 oti.data = inflate_other_info (&oti, &class->generic_class->context, class, temporary);
733 if (tmp_do_free)
734 free_inflated_info (oti.info_type, info);
736 if (temporary)
737 *do_free = TRUE;
739 return oti;
740 } else {
741 MonoRuntimeGenericContextTemplate *template;
742 MonoRuntimeGenericContextOtherInfoTemplate *oti;
744 template = mono_class_get_runtime_generic_context_template (class);
745 oti = rgctx_template_get_other_slot (template, type_argc, slot);
746 g_assert (oti);
748 if (temporary)
749 *do_free = FALSE;
751 return *oti;
755 static MonoClass*
756 class_uninstantiated (MonoClass *class)
758 if (class->generic_class)
759 return class->generic_class->container_class;
760 return class;
763 static gpointer
764 class_type_info (MonoDomain *domain, MonoClass *class, int info_type)
766 switch (info_type) {
767 case MONO_RGCTX_INFO_STATIC_DATA:
768 return mono_class_vtable (domain, class)->data;
769 case MONO_RGCTX_INFO_KLASS:
770 return class;
771 case MONO_RGCTX_INFO_VTABLE:
772 return mono_class_vtable (domain, class);
773 default:
774 g_assert_not_reached ();
776 /* Not reached */
777 return NULL;
780 static gpointer
781 instantiate_other_info (MonoDomain *domain, MonoRuntimeGenericContextOtherInfoTemplate *oti,
782 MonoGenericContext *context, MonoClass *class)
784 gpointer data;
785 gboolean temporary;
787 if (!oti->data)
788 return NULL;
790 switch (oti->info_type) {
791 case MONO_RGCTX_INFO_STATIC_DATA:
792 case MONO_RGCTX_INFO_KLASS:
793 case MONO_RGCTX_INFO_VTABLE:
794 temporary = TRUE;
795 break;
796 default:
797 temporary = FALSE;
800 data = inflate_other_info (oti, context, class, temporary);
802 switch (oti->info_type) {
803 case MONO_RGCTX_INFO_STATIC_DATA:
804 case MONO_RGCTX_INFO_KLASS:
805 case MONO_RGCTX_INFO_VTABLE: {
806 MonoClass *arg_class = mono_class_from_mono_type (data);
808 free_inflated_info (oti->info_type, data);
809 g_assert (arg_class);
811 return class_type_info (domain, arg_class, oti->info_type);
813 case MONO_RGCTX_INFO_TYPE:
814 return data;
815 case MONO_RGCTX_INFO_REFLECTION_TYPE:
816 return mono_type_get_object (domain, data);
817 case MONO_RGCTX_INFO_METHOD:
818 return data;
819 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
820 return mono_create_ftnptr (mono_domain_get (),
821 mono_runtime_create_jump_trampoline (mono_domain_get (), data, TRUE));
822 case MONO_RGCTX_INFO_CLASS_FIELD:
823 return data;
824 case MONO_RGCTX_INFO_METHOD_RGCTX: {
825 MonoMethodInflated *method = data;
827 g_assert (method->method.method.is_inflated);
828 g_assert (method->context.method_inst);
830 return mono_method_lookup_rgctx (mono_class_vtable (domain, method->method.method.klass),
831 method->context.method_inst);
833 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
834 MonoMethodInflated *method = data;
836 g_assert (method->method.method.is_inflated);
837 g_assert (method->context.method_inst);
839 return method->context.method_inst;
841 default:
842 g_assert_not_reached ();
844 /* Not reached */
845 return NULL;
849 * LOCKING: templates lock
851 static void
852 fill_in_rgctx_template_slot (MonoClass *class, int type_argc, int index, gpointer data, int info_type)
854 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
855 MonoClass *subclass;
857 g_assert (!class->generic_class);
859 rgctx_template_set_other_slot (class->image, template, type_argc, index, data, info_type);
861 /* Recurse for all subclasses */
862 if (generic_subclass_hash)
863 subclass = g_hash_table_lookup (generic_subclass_hash, class);
864 else
865 subclass = NULL;
867 while (subclass) {
868 MonoRuntimeGenericContextOtherInfoTemplate subclass_oti;
869 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
871 g_assert (!subclass->generic_class);
872 g_assert (subclass_template);
874 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, NULL);
875 g_assert (subclass_oti.data);
877 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
879 subclass = subclass_template->next_subclass;
884 * LOCKING: templates lock
886 static int
887 register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type)
889 int i;
890 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
891 MonoClass *parent;
892 MonoRuntimeGenericContextOtherInfoTemplate *oti;
894 for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
895 if (!oti->data)
896 break;
899 //g_print ("template %s . other_infos [%d] = %s\n", mono_type_get_full_name (class), i, mono_type_get_full_name (other_class));
901 /* Mark the slot as used in all parent classes (until we find
902 a parent class which already has it marked used). */
903 parent = class->parent;
904 while (parent != NULL) {
905 MonoRuntimeGenericContextTemplate *parent_template;
906 MonoRuntimeGenericContextOtherInfoTemplate *oti;
908 if (parent->generic_class)
909 parent = parent->generic_class->container_class;
911 parent_template = mono_class_get_runtime_generic_context_template (parent);
912 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
914 if (oti && oti->data)
915 break;
917 rgctx_template_set_other_slot (parent->image, parent_template, type_argc, i,
918 MONO_RGCTX_SLOT_USED_MARKER, 0);
920 parent = parent->parent;
923 /* Fill in the slot in this class and in all subclasses
924 recursively. */
925 fill_in_rgctx_template_slot (class, type_argc, i, data, info_type);
927 return i;
930 static gboolean
931 other_info_equal (gpointer data1, gpointer data2, int info_type)
933 switch (info_type) {
934 case MONO_RGCTX_INFO_STATIC_DATA:
935 case MONO_RGCTX_INFO_KLASS:
936 case MONO_RGCTX_INFO_VTABLE:
937 case MONO_RGCTX_INFO_TYPE:
938 case MONO_RGCTX_INFO_REFLECTION_TYPE:
939 return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
940 case MONO_RGCTX_INFO_METHOD:
941 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
942 case MONO_RGCTX_INFO_CLASS_FIELD:
943 case MONO_RGCTX_INFO_METHOD_RGCTX:
944 case MONO_RGCTX_INFO_METHOD_CONTEXT:
945 return data1 == data2;
946 default:
947 g_assert_not_reached ();
949 /* never reached */
950 return NULL;
953 static int
954 lookup_or_register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type,
955 MonoGenericContext *generic_context)
957 static gboolean inited = FALSE;
958 static int max_slot = 0;
960 MonoRuntimeGenericContextTemplate *rgctx_template =
961 mono_class_get_runtime_generic_context_template (class);
962 MonoRuntimeGenericContextOtherInfoTemplate *oti_list, *oti, *copy;
963 int i, length;
965 g_assert (!class->generic_class);
966 g_assert (class->generic_container || type_argc);
969 * We must not call inflate_other_info() with the templates
970 * lock held, because it calls metadata functions which might
971 * cause the loader lock to be taken, which must not happen if
972 * the templates lock is held.
974 * Only two things can happen to an oti list: An unused
975 * (data==NULL) node can be filled in and nodes can be
976 * appended at the end of the list.
978 * To solve the lock problem we first count the number of
979 * nodes in the list, then copy all the data into a separate
980 * array. With the templates lock not held we then search for
981 * our info in the array - this is where the calls to
982 * inflate_other_info() happen. If we don't find the info
983 * we're looking for, we take the templates lock again and
984 * check if the oti list has changed since we've copied it.
985 * If it has, we start again. If it hasn't, we register the
986 * info.
989 templates_lock ();
991 restart:
992 oti_list = get_other_info_templates (rgctx_template, type_argc);
994 length = 0;
995 for (oti = oti_list; oti; oti = oti->next)
996 ++length;
998 copy = g_new (MonoRuntimeGenericContextOtherInfoTemplate, length);
1000 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
1001 copy [i].info_type = oti->info_type;
1002 copy [i].data = oti->data;
1004 g_assert (i == length);
1006 templates_unlock ();
1008 /* We've copied the list. Now look for the info. */
1010 for (i = 0; i < length; ++i) {
1011 gpointer inflated_data;
1013 if (copy [i].info_type != info_type || !copy [i].data)
1014 continue;
1016 inflated_data = inflate_other_info (&copy [i], generic_context, class, TRUE);
1018 if (other_info_equal (data, inflated_data, info_type)) {
1019 free_inflated_info (info_type, inflated_data);
1020 g_free (copy);
1021 return i;
1023 free_inflated_info (info_type, inflated_data);
1026 /* We haven't found the info, so check if the list is still
1027 the same. */
1029 templates_lock ();
1031 /* We need to fetch oti_list again here because the list could
1032 have been empty. */
1033 oti_list = get_other_info_templates (rgctx_template, type_argc);
1035 for (oti = oti_list, i = 0; i < length; oti = oti->next, ++i) {
1036 g_assert (oti);
1038 if (copy [i].info_type != oti->info_type || copy [i].data != oti->data) {
1039 g_free (copy);
1040 goto restart;
1043 g_free (copy);
1044 if (oti)
1045 goto restart;
1047 /* The list is still the same - success. */
1049 i = register_other_info (class, type_argc, data, info_type);
1051 templates_unlock ();
1053 if (!inited) {
1054 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
1055 inited = TRUE;
1057 if (i > max_slot)
1058 max_slot = i;
1060 return i;
1064 * mono_method_lookup_or_register_other_info:
1065 * @method: a method
1066 * @in_mrgctx: whether to put the data into the MRGCTX
1067 * @data: the info data
1068 * @info_type: the type of info to register about data
1069 * @generic_context: a generic context
1071 * Looks up and, if necessary, adds information about other_class in
1072 * method's or method's class runtime generic context. Returns the
1073 * encoded slot number.
1075 guint32
1076 mono_method_lookup_or_register_other_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
1077 int info_type, MonoGenericContext *generic_context)
1079 MonoClass *class = method->klass;
1080 int type_argc, index;
1082 if (in_mrgctx) {
1083 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
1085 g_assert (method->is_inflated && method_inst);
1086 type_argc = method_inst->type_argc;
1087 g_assert (type_argc > 0);
1088 } else {
1089 type_argc = 0;
1092 index = lookup_or_register_other_info (class, type_argc, data, info_type, generic_context);
1094 //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
1096 if (in_mrgctx)
1097 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
1098 else
1099 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
1103 * mono_class_rgctx_get_array_size:
1104 * @n: The number of the array
1105 * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
1107 * Returns the number of slots in the n'th array of a (M)RGCTX. That
1108 * number includes the slot for linking and - for MRGCTXs - the two
1109 * slots in the first array for additional information.
1112 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
1114 g_assert (n >= 0 && n < 30);
1116 if (mrgctx)
1117 return 6 << n;
1118 else
1119 return 4 << n;
1123 * LOCKING: domain lock
1125 static gpointer*
1126 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
1128 static gboolean inited = FALSE;
1129 static int rgctx_num_alloced = 0;
1130 static int rgctx_bytes_alloced = 0;
1131 static int mrgctx_num_alloced = 0;
1132 static int mrgctx_bytes_alloced = 0;
1134 int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
1135 gpointer array = mono_domain_alloc0 (domain, size);
1137 if (!inited) {
1138 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
1139 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
1140 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
1141 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
1142 inited = TRUE;
1145 if (is_mrgctx) {
1146 mrgctx_num_alloced++;
1147 mrgctx_bytes_alloced += size;
1148 } else {
1149 rgctx_num_alloced++;
1150 rgctx_bytes_alloced += size;
1153 return array;
1156 static gpointer
1157 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
1158 MonoGenericInst *method_inst)
1160 gpointer info;
1161 int i, first_slot, size;
1162 MonoDomain *domain = class_vtable->domain;
1163 MonoClass *class = class_vtable->klass;
1164 MonoGenericContext *class_context = class->generic_class ? &class->generic_class->context : NULL;
1165 MonoRuntimeGenericContextOtherInfoTemplate oti;
1166 MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
1167 int rgctx_index;
1168 gboolean do_free;
1170 g_assert (rgctx);
1172 mono_domain_lock (domain);
1174 /* First check whether that slot isn't already instantiated.
1175 This might happen because lookup doesn't lock. Allocate
1176 arrays on the way. */
1177 first_slot = 0;
1178 size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
1179 if (method_inst)
1180 size -= sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
1181 for (i = 0; ; ++i) {
1182 int offset;
1184 if (method_inst && i == 0)
1185 offset = sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
1186 else
1187 offset = 0;
1189 if (slot < first_slot + size - 1) {
1190 rgctx_index = slot - first_slot + 1 + offset;
1191 info = rgctx [rgctx_index];
1192 if (info) {
1193 mono_domain_unlock (domain);
1194 return info;
1196 break;
1198 if (!rgctx [offset + 0])
1199 rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
1200 rgctx = rgctx [offset + 0];
1201 first_slot += size - 1;
1202 size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
1205 g_assert (!rgctx [rgctx_index]);
1207 mono_domain_unlock (domain);
1209 oti = class_get_rgctx_template_oti (class_uninstantiated (class),
1210 method_inst ? method_inst->type_argc : 0, slot, TRUE, &do_free);
1211 /* This might take the loader lock */
1212 info = instantiate_other_info (domain, &oti, &context, class);
1215 if (method_inst)
1216 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
1219 /*FIXME We should use CAS here, no need to take a lock.*/
1220 mono_domain_lock (domain);
1222 /* Check whether the slot hasn't been instantiated in the
1223 meantime. */
1224 if (rgctx [rgctx_index])
1225 info = rgctx [rgctx_index];
1226 else
1227 rgctx [rgctx_index] = info;
1229 mono_domain_unlock (domain);
1231 if (do_free)
1232 free_inflated_info (oti.info_type, oti.data);
1234 return info;
1238 * mono_class_fill_runtime_generic_context:
1239 * @class_vtable: a vtable
1240 * @slot: a slot index to be instantiated
1242 * Instantiates a slot in the RGCTX.
1244 gpointer
1245 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot)
1247 static gboolean inited = FALSE;
1248 static int num_alloced = 0;
1250 MonoDomain *domain = class_vtable->domain;
1251 MonoRuntimeGenericContext *rgctx;
1252 gpointer info;
1254 mono_domain_lock (domain);
1256 if (!inited) {
1257 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
1258 inited = TRUE;
1261 rgctx = class_vtable->runtime_generic_context;
1262 if (!rgctx) {
1263 rgctx = alloc_rgctx_array (domain, 0, FALSE);
1264 class_vtable->runtime_generic_context = rgctx;
1265 num_alloced++;
1268 mono_domain_unlock (domain);
1270 info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
1272 return info;
1276 * mono_method_fill_runtime_generic_context:
1277 * @mrgctx: an MRGCTX
1278 * @slot: a slot index to be instantiated
1280 * Instantiates a slot in the MRGCTX.
1282 gpointer
1283 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot)
1285 gpointer info;
1287 info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot,
1288 mrgctx->method_inst);
1290 return info;
1293 static guint
1294 mrgctx_hash_func (gconstpointer key)
1296 const MonoMethodRuntimeGenericContext *mrgctx = key;
1298 return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
1301 static gboolean
1302 mrgctx_equal_func (gconstpointer a, gconstpointer b)
1304 const MonoMethodRuntimeGenericContext *mrgctx1 = a;
1305 const MonoMethodRuntimeGenericContext *mrgctx2 = b;
1307 return mrgctx1->class_vtable == mrgctx2->class_vtable &&
1308 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
1312 * mono_method_lookup_rgctx:
1313 * @class_vtable: a vtable
1314 * @method_inst: the method inst of a generic method
1316 * Returns the MRGCTX for the generic method(s) with the given
1317 * method_inst of the given class_vtable.
1319 * LOCKING: Take the domain lock.
1321 MonoMethodRuntimeGenericContext*
1322 mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
1324 MonoDomain *domain = class_vtable->domain;
1325 MonoMethodRuntimeGenericContext *mrgctx;
1326 MonoMethodRuntimeGenericContext key;
1328 g_assert (!class_vtable->klass->generic_container);
1329 g_assert (!method_inst->is_open);
1331 mono_domain_lock (domain);
1332 if (!domain->method_rgctx_hash)
1333 domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
1335 key.class_vtable = class_vtable;
1336 key.method_inst = method_inst;
1338 mrgctx = g_hash_table_lookup (domain->method_rgctx_hash, &key);
1340 if (!mrgctx) {
1341 //int i;
1343 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
1344 mrgctx->class_vtable = class_vtable;
1345 mrgctx->method_inst = method_inst;
1347 g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
1350 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
1351 for (i = 0; i < method_inst->type_argc; ++i)
1352 g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
1353 g_print (">\n");
1357 mono_domain_unlock (domain);
1359 g_assert (mrgctx);
1361 return mrgctx;
1364 static gboolean
1365 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars)
1367 int i;
1369 for (i = 0; i < inst->type_argc; ++i) {
1370 MonoType *type = inst->type_argv [i];
1371 int type_type;
1373 if (MONO_TYPE_IS_REFERENCE (type))
1374 continue;
1376 type_type = mono_type_get_type (type);
1377 if (allow_type_vars && (type_type == MONO_TYPE_VAR || type_type == MONO_TYPE_MVAR))
1378 continue;
1380 return FALSE;
1383 return TRUE;
1387 * mono_generic_context_is_sharable:
1388 * @context: a generic context
1390 * Returns whether the generic context is sharable. A generic context
1391 * is sharable iff all of its type arguments are reference type.
1393 gboolean
1394 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
1396 g_assert (context->class_inst || context->method_inst);
1398 if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars))
1399 return FALSE;
1401 if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars))
1402 return FALSE;
1404 return TRUE;
1408 * mono_method_is_generic_impl:
1409 * @method: a method
1411 * Returns whether the method is either generic or part of a generic
1412 * class.
1414 gboolean
1415 mono_method_is_generic_impl (MonoMethod *method)
1417 if (method->is_inflated) {
1418 g_assert (method->wrapper_type == MONO_WRAPPER_NONE);
1419 return TRUE;
1421 /* We don't treat wrappers as generic code, i.e., we never
1422 apply generic sharing to them. This is especially
1423 important for static rgctx invoke wrappers, which only work
1424 if not compiled with sharing. */
1425 if (method->wrapper_type != MONO_WRAPPER_NONE)
1426 return FALSE;
1427 if (method->klass->generic_container)
1428 return TRUE;
1429 return FALSE;
1432 static gboolean
1433 has_constraints (MonoGenericContainer *container)
1435 //int i;
1437 return FALSE;
1439 g_assert (container->type_argc > 0);
1440 g_assert (container->type_params);
1442 for (i = 0; i < container->type_argc; ++i)
1443 if (container->type_params [i].constraints)
1444 return TRUE;
1445 return FALSE;
1450 * mono_method_is_generic_sharable_impl:
1451 * @method: a method
1452 * @allow_type_vars: whether to regard type variables as reference types
1454 * Returns TRUE iff the method is inflated or part of an inflated
1455 * class, its context is sharable and it has no constraints on its
1456 * type parameters. Otherwise returns FALSE.
1458 gboolean
1459 mono_method_is_generic_sharable_impl (MonoMethod *method, gboolean allow_type_vars)
1461 if (!mono_method_is_generic_impl (method))
1462 return FALSE;
1464 if (method->is_inflated) {
1465 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
1466 MonoGenericContext *context = &inflated->context;
1468 if (!mono_generic_context_is_sharable (context, allow_type_vars))
1469 return FALSE;
1471 g_assert (inflated->declaring);
1473 if (inflated->declaring->is_generic) {
1474 if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
1475 return FALSE;
1479 if (method->klass->generic_class) {
1480 if (!mono_generic_context_is_sharable (&method->klass->generic_class->context, allow_type_vars))
1481 return FALSE;
1483 g_assert (method->klass->generic_class->container_class &&
1484 method->klass->generic_class->container_class->generic_container);
1486 if (has_constraints (method->klass->generic_class->container_class->generic_container))
1487 return FALSE;
1490 if (method->klass->generic_container && !allow_type_vars)
1491 return FALSE;
1493 return TRUE;
1496 gboolean
1497 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
1499 if (!mono_class_generic_sharing_enabled (method->klass))
1500 return FALSE;
1502 if (!mono_method_is_generic_sharable_impl (method, allow_type_vars))
1503 return FALSE;
1505 if (method->is_inflated && mono_method_get_context (method)->method_inst)
1506 return TRUE;
1508 return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
1509 method->klass->valuetype) &&
1510 (method->klass->generic_class || method->klass->generic_container);
1513 static MonoGenericInst*
1514 get_object_generic_inst (int type_argc)
1516 MonoType **type_argv;
1517 int i;
1519 type_argv = alloca (sizeof (MonoType*) * type_argc);
1521 for (i = 0; i < type_argc; ++i)
1522 type_argv [i] = &mono_defaults.object_class->byval_arg;
1524 return mono_metadata_get_generic_inst (type_argc, type_argv);
1528 * mono_method_construct_object_context:
1529 * @method: a method
1531 * Returns a generic context for method with all type variables for
1532 * class and method instantiated with Object.
1534 MonoGenericContext
1535 mono_method_construct_object_context (MonoMethod *method)
1537 MonoGenericContext object_context;
1539 g_assert (!method->klass->generic_class);
1540 if (method->klass->generic_container) {
1541 int type_argc = method->klass->generic_container->type_argc;
1543 object_context.class_inst = get_object_generic_inst (type_argc);
1544 } else {
1545 object_context.class_inst = NULL;
1548 if (mono_method_get_context_general (method, TRUE)->method_inst) {
1549 int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
1551 object_context.method_inst = get_object_generic_inst (type_argc);
1552 } else {
1553 object_context.method_inst = NULL;
1556 g_assert (object_context.class_inst || object_context.method_inst);
1558 return object_context;
1562 * mono_domain_lookup_shared_generic:
1563 * @domain: a domain
1564 * @open_method: an open generic method
1566 * Looks up the jit info for method via the domain's jit code hash.
1568 MonoJitInfo*
1569 mono_domain_lookup_shared_generic (MonoDomain *domain, MonoMethod *open_method)
1571 static gboolean inited = FALSE;
1572 static int lookups = 0;
1573 static int failed_lookups = 0;
1575 MonoGenericContext object_context;
1576 MonoMethod *object_method;
1577 MonoJitInfo *ji;
1579 object_context = mono_method_construct_object_context (open_method);
1580 object_method = mono_class_inflate_generic_method (open_method, &object_context);
1582 mono_domain_jit_code_hash_lock (domain);
1583 ji = mono_internal_hash_table_lookup (&domain->jit_code_hash, object_method);
1584 if (ji && !ji->has_generic_jit_info)
1585 ji = NULL;
1586 mono_domain_jit_code_hash_unlock (domain);
1588 if (!inited) {
1589 mono_counters_register ("Shared generic lookups", MONO_COUNTER_INT|MONO_COUNTER_GENERICS, &lookups);
1590 mono_counters_register ("Failed shared generic lookups", MONO_COUNTER_INT|MONO_COUNTER_GENERICS, &failed_lookups);
1591 inited = TRUE;
1594 ++lookups;
1595 if (!ji)
1596 ++failed_lookups;
1598 return ji;