2009-03-11 Zoltan Varga <vargaz@gmail.com>
[mono-debugger.git] / mono / metadata / generic-sharing.c
blob2dad1c9b25065813b94e13f540bcb75936fd2e2d
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 ();
590 static gpointer
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);
597 static void
598 free_inflated_info (int info_type, gpointer info)
600 if (!info)
601 return;
603 switch (info_type) {
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);
610 break;
611 default:
612 break;
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:
621 * @class: a class
623 * Looks up or constructs, if necessary, the runtime generic context
624 * for class.
626 static MonoRuntimeGenericContextTemplate*
627 mono_class_get_runtime_generic_context_template (MonoClass *class)
629 MonoRuntimeGenericContextTemplate *parent_template, *template;
630 MonoGenericInst *inst;
631 guint32 i;
633 g_assert (!class->generic_class);
635 templates_lock ();
636 template = class_lookup_rgctx_template (class);
637 templates_unlock ();
639 if (template)
640 return template;
642 if (class->generic_container)
643 inst = class->generic_container->context.class_inst;
644 else
645 inst = NULL;
647 template = alloc_template (class);
649 templates_lock ();
651 if (class->parent) {
652 if (class->parent->generic_class) {
653 guint32 num_entries;
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);
675 } else {
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);
698 } else {
699 class_set_rgctx_template (class, template);
701 if (class->parent)
702 register_generic_subclass (class);
705 templates_unlock ();
707 return template;
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);
728 if (oti.data) {
729 gpointer info = oti.data;
730 oti.data = inflate_other_info (&oti, &class->generic_class->context, class, temporary);
731 if (tmp_do_free)
732 free_inflated_info (oti.info_type, info);
734 if (temporary)
735 *do_free = TRUE;
737 return oti;
738 } else {
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);
744 g_assert (oti);
746 if (temporary)
747 *do_free = FALSE;
749 return *oti;
753 static MonoClass*
754 class_uninstantiated (MonoClass *class)
756 if (class->generic_class)
757 return class->generic_class->container_class;
758 return class;
761 static gpointer
762 class_type_info (MonoDomain *domain, MonoClass *class, int info_type)
764 switch (info_type) {
765 case MONO_RGCTX_INFO_STATIC_DATA:
766 return mono_class_vtable (domain, class)->data;
767 case MONO_RGCTX_INFO_KLASS:
768 return class;
769 case MONO_RGCTX_INFO_VTABLE:
770 return mono_class_vtable (domain, class);
771 default:
772 g_assert_not_reached ();
776 static gpointer
777 instantiate_other_info (MonoDomain *domain, MonoRuntimeGenericContextOtherInfoTemplate *oti,
778 MonoGenericContext *context, MonoClass *class)
780 gpointer data;
781 gboolean temporary;
783 if (!oti->data)
784 return NULL;
786 switch (oti->info_type) {
787 case MONO_RGCTX_INFO_STATIC_DATA:
788 case MONO_RGCTX_INFO_KLASS:
789 case MONO_RGCTX_INFO_VTABLE:
790 temporary = TRUE;
791 break;
792 default:
793 temporary = FALSE;
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:
810 return data;
811 case MONO_RGCTX_INFO_REFLECTION_TYPE:
812 return mono_type_get_object (domain, data);
813 case MONO_RGCTX_INFO_METHOD:
814 return data;
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:
819 return data;
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;
837 default:
838 g_assert_not_reached ();
843 * LOCKING: templates lock
845 static void
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);
849 MonoClass *subclass;
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);
858 else
859 subclass = NULL;
861 while (subclass) {
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
880 static int
881 register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type)
883 int i;
884 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
885 MonoClass *parent;
886 MonoRuntimeGenericContextOtherInfoTemplate *oti;
888 for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
889 if (!oti->data)
890 break;
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)
909 break;
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
918 recursively. */
919 fill_in_rgctx_template_slot (class, type_argc, i, data, info_type);
921 return i;
924 static gboolean
925 other_info_equal (gpointer data1, gpointer data2, int info_type)
927 switch (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;
940 default:
941 g_assert_not_reached ();
945 static int
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;
955 int i, length;
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
978 * info.
981 templates_lock ();
983 restart:
984 oti_list = get_other_info_templates (rgctx_template, type_argc);
986 length = 0;
987 for (oti = oti_list; oti; oti = oti->next)
988 ++length;
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);
998 templates_unlock ();
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)
1006 continue;
1008 inflated_data = inflate_other_info (&copy [i], generic_context, class, TRUE);
1010 if (other_info_equal (data, inflated_data, info_type)) {
1011 free_inflated_info (info_type, inflated_data);
1012 g_free (copy);
1013 return i;
1015 free_inflated_info (info_type, inflated_data);
1018 /* We haven't found the info, so check if the list is still
1019 the same. */
1021 templates_lock ();
1023 /* We need to fetch oti_list again here because the list could
1024 have been empty. */
1025 oti_list = get_other_info_templates (rgctx_template, type_argc);
1027 for (oti = oti_list, i = 0; i < length; oti = oti->next, ++i) {
1028 g_assert (oti);
1030 if (copy [i].info_type != oti->info_type || copy [i].data != oti->data) {
1031 g_free (copy);
1032 goto restart;
1035 g_free (copy);
1036 if (oti)
1037 goto restart;
1039 /* The list is still the same - success. */
1041 i = register_other_info (class, type_argc, data, info_type);
1043 templates_unlock ();
1045 if (!inited) {
1046 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
1047 inited = TRUE;
1049 if (i > max_slot)
1050 max_slot = i;
1052 return i;
1056 * mono_method_lookup_or_register_other_info:
1057 * @method: a method
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.
1067 guint32
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;
1074 if (in_mrgctx) {
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);
1080 } else {
1081 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);
1088 if (in_mrgctx)
1089 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
1090 else
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);
1108 if (mrgctx)
1109 return 6 << n;
1110 else
1111 return 4 << n;
1115 * LOCKING: domain lock
1117 static gpointer*
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);
1129 if (!inited) {
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);
1134 inited = TRUE;
1137 if (is_mrgctx) {
1138 mrgctx_num_alloced++;
1139 mrgctx_bytes_alloced += size;
1140 } else {
1141 rgctx_num_alloced++;
1142 rgctx_bytes_alloced += size;
1145 return array;
1148 static gpointer
1149 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
1150 MonoGenericInst *method_inst)
1152 gpointer info;
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 };
1159 int rgctx_index;
1160 gboolean do_free;
1162 g_assert (rgctx);
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. */
1169 first_slot = 0;
1170 size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
1171 if (method_inst)
1172 size -= sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
1173 for (i = 0; ; ++i) {
1174 int offset;
1176 if (method_inst && i == 0)
1177 offset = sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
1178 else
1179 offset = 0;
1181 if (slot < first_slot + size - 1) {
1182 rgctx_index = slot - first_slot + 1 + offset;
1183 info = rgctx [rgctx_index];
1184 if (info) {
1185 mono_domain_unlock (domain);
1186 return info;
1188 break;
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);
1207 if (method_inst)
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
1215 meantime. */
1216 if (rgctx [rgctx_index])
1217 info = rgctx [rgctx_index];
1218 else
1219 rgctx [rgctx_index] = info;
1221 mono_domain_unlock (domain);
1223 if (do_free)
1224 free_inflated_info (oti.info_type, oti.data);
1226 return info;
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.
1236 gpointer
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;
1244 gpointer info;
1246 mono_domain_lock (domain);
1248 if (!inited) {
1249 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
1250 inited = TRUE;
1253 rgctx = class_vtable->runtime_generic_context;
1254 if (!rgctx) {
1255 rgctx = alloc_rgctx_array (domain, 0, FALSE);
1256 class_vtable->runtime_generic_context = rgctx;
1257 num_alloced++;
1260 mono_domain_unlock (domain);
1262 info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
1264 return info;
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.
1274 gpointer
1275 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot)
1277 gpointer info;
1279 info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot,
1280 mrgctx->method_inst);
1282 return info;
1285 static guint
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);
1293 static gboolean
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);
1332 if (!mrgctx) {
1333 //int i;
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]));
1345 g_print (">\n");
1349 mono_domain_unlock (domain);
1351 g_assert (mrgctx);
1353 return mrgctx;
1356 static gboolean
1357 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars)
1359 int i;
1361 for (i = 0; i < inst->type_argc; ++i) {
1362 MonoType *type = inst->type_argv [i];
1363 int type_type;
1365 if (MONO_TYPE_IS_REFERENCE (type))
1366 continue;
1368 type_type = mono_type_get_type (type);
1369 if (allow_type_vars && (type_type == MONO_TYPE_VAR || type_type == MONO_TYPE_MVAR))
1370 continue;
1372 return FALSE;
1375 return TRUE;
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.
1385 gboolean
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))
1391 return FALSE;
1393 if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars))
1394 return FALSE;
1396 return TRUE;
1400 * mono_method_is_generic_impl:
1401 * @method: a method
1403 * Returns whether the method is either generic or part of a generic
1404 * class.
1406 gboolean
1407 mono_method_is_generic_impl (MonoMethod *method)
1409 if (method->is_inflated) {
1410 g_assert (method->wrapper_type == MONO_WRAPPER_NONE);
1411 return TRUE;
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)
1418 return FALSE;
1419 if (method->klass->generic_container)
1420 return TRUE;
1421 return FALSE;
1424 static gboolean
1425 has_constraints (MonoGenericContainer *container)
1427 //int i;
1429 return FALSE;
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)
1436 return TRUE;
1437 return FALSE;
1442 * mono_method_is_generic_sharable_impl:
1443 * @method: a method
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.
1450 gboolean
1451 mono_method_is_generic_sharable_impl (MonoMethod *method, gboolean allow_type_vars)
1453 if (!mono_method_is_generic_impl (method))
1454 return FALSE;
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))
1461 return FALSE;
1463 g_assert (inflated->declaring);
1465 if (inflated->declaring->is_generic) {
1466 if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
1467 return FALSE;
1471 if (method->klass->generic_class) {
1472 if (!mono_generic_context_is_sharable (&method->klass->generic_class->context, allow_type_vars))
1473 return FALSE;
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))
1479 return FALSE;
1482 if (method->klass->generic_container && !allow_type_vars)
1483 return FALSE;
1485 return TRUE;
1488 gboolean
1489 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
1491 if (!mono_class_generic_sharing_enabled (method->klass))
1492 return FALSE;
1494 if (!mono_method_is_generic_sharable_impl (method, allow_type_vars))
1495 return FALSE;
1497 if (method->is_inflated && mono_method_get_context (method)->method_inst)
1498 return TRUE;
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;
1509 int i;
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:
1521 * @method: a method
1523 * Returns a generic context for method with all type variables for
1524 * class and method instantiated with Object.
1526 MonoGenericContext
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);
1536 } else {
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);
1544 } else {
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:
1555 * @domain: a domain
1556 * @open_method: an open generic method
1558 * Looks up the jit info for method via the domain's jit code hash.
1560 MonoJitInfo*
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;
1569 MonoJitInfo *ji;
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)
1577 ji = NULL;
1578 mono_domain_jit_code_hash_unlock (domain);
1580 if (!inited) {
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);
1583 inited = TRUE;
1586 ++lookups;
1587 if (!ji)
1588 ++failed_lookups;
1590 return ji;