2008-08-22 Robert Jordan <robertj@gmx.net>
[mono.git] / mono / metadata / generic-sharing.c
1 /*
2  * generic-sharing.c: Support functions for generic sharing.
3  *
4  * Author:
5  *   Mark Probst (mark.probst@gmail.com)
6  *
7  * (C) 2007-2008 Novell, Inc.
8  */
9
10 #include <config.h>
11 #include <string.h>
12
13 #ifdef _MSC_VER
14 #include <glib.h>
15 #endif
16 #include <mono/utils/mono-membar.h>
17 #include <mono/utils/mono-counters.h>
18
19 #include "metadata-internals.h"
20 #include "class.h"
21 #include "class-internals.h"
22 #include "marshal.h"
23 #include "debug-helpers.h"
24
25 static int
26 type_check_context_used (MonoType *type, gboolean recursive)
27 {
28         switch (mono_type_get_type (type)) {
29         case MONO_TYPE_VAR:
30                 return MONO_GENERIC_CONTEXT_USED_CLASS;
31         case MONO_TYPE_MVAR:
32                 return MONO_GENERIC_CONTEXT_USED_METHOD;
33         case MONO_TYPE_SZARRAY:
34                 return mono_class_check_context_used (mono_type_get_class (type));
35         case MONO_TYPE_ARRAY:
36                 return mono_class_check_context_used (mono_type_get_array_type (type)->eklass);
37         case MONO_TYPE_CLASS:
38                 if (recursive)
39                         return mono_class_check_context_used (mono_type_get_class (type));
40                 else
41                         return 0;
42         case MONO_TYPE_GENERICINST:
43                 if (recursive) {
44                         MonoGenericClass *gclass = type->data.generic_class;
45
46                         g_assert (gclass->container_class->generic_container);
47                         return mono_generic_context_check_used (&gclass->context);
48                 } else {
49                         return 0;
50                 }
51         default:
52                 return 0;
53         }
54 }
55
56 static int
57 inst_check_context_used (MonoGenericInst *inst)
58 {
59         int context_used = 0;
60         int i;
61
62         if (!inst)
63                 return 0;
64
65         for (i = 0; i < inst->type_argc; ++i)
66                 context_used |= type_check_context_used (inst->type_argv [i], TRUE);
67
68         return context_used;
69 }
70
71 /*
72  * mono_generic_context_check_used:
73  * @context: a generic context
74  *
75  * Checks whether the context uses a type variable.  Returns an int
76  * with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to reflect whether
77  * the context's class instantiation uses type variables.
78  */
79 int
80 mono_generic_context_check_used (MonoGenericContext *context)
81 {
82         int context_used = 0;
83
84         context_used |= inst_check_context_used (context->class_inst);
85         context_used |= inst_check_context_used (context->method_inst);
86
87         return context_used;
88 }
89
90 /*
91  * mono_class_check_context_used:
92  * @class: a class
93  *
94  * Checks whether the class's generic context uses a type variable.
95  * Returns an int with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to
96  * reflect whether the context's class instantiation uses type
97  * variables.
98  */
99 int
100 mono_class_check_context_used (MonoClass *class)
101 {
102         int context_used = 0;
103
104         context_used |= type_check_context_used (&class->this_arg, FALSE);
105         context_used |= type_check_context_used (&class->byval_arg, FALSE);
106
107         if (class->generic_class)
108                 context_used |= mono_generic_context_check_used (&class->generic_class->context);
109         else if (class->generic_container)
110                 context_used |= mono_generic_context_check_used (&class->generic_container->context);
111
112         return context_used;
113 }
114
115 /*
116  * Guards the two global rgctx (template) hash tables and all rgctx
117  * templates.
118  *
119  * Ordering: The loader lock can be taken while the templates lock is
120  * held.
121  */
122 static CRITICAL_SECTION templates_mutex;
123
124 static void
125 templates_lock (void)
126 {
127         static gboolean inited = FALSE;
128
129         if (!inited) {
130                 mono_loader_lock ();
131                 if (!inited) {
132                         InitializeCriticalSection (&templates_mutex);
133                         inited = TRUE;
134                 }
135                 mono_loader_unlock ();
136         }
137
138         EnterCriticalSection (&templates_mutex);
139 }
140
141 static void
142 templates_unlock (void)
143 {
144         LeaveCriticalSection (&templates_mutex);
145 }
146
147 /*
148  * LOCKING: templates lock
149  */
150 static MonoRuntimeGenericContextOtherInfoTemplate*
151 get_other_info_templates (MonoRuntimeGenericContextTemplate *template, int type_argc)
152 {
153         g_assert (type_argc >= 0);
154         if (type_argc == 0)
155                 return template->other_infos;
156         return g_slist_nth_data (template->method_templates, type_argc - 1);
157 }
158
159 /*
160  * LOCKING: templates lock
161  */
162 static void
163 set_other_info_templates (MonoRuntimeGenericContextTemplate *template, int type_argc,
164         MonoRuntimeGenericContextOtherInfoTemplate *oti)
165 {
166         g_assert (type_argc >= 0);
167         if (type_argc == 0)
168                 template->other_infos = oti;
169         else {
170                 int length = g_slist_length (template->method_templates);
171                 GSList *list;
172
173                 /* FIXME: quadratic! */
174                 while (length < type_argc) {
175                         template->method_templates = g_slist_append (template->method_templates, NULL);
176                         length++;
177                 }
178
179                 list = g_slist_nth (template->method_templates, type_argc - 1);
180                 g_assert (list);
181                 list->data = oti;
182         }
183 }
184
185 /*
186  * LOCKING: templates lock
187  */
188 static int
189 template_get_max_argc (MonoRuntimeGenericContextTemplate *template)
190 {
191         return g_slist_length (template->method_templates);
192 }
193
194 /*
195  * LOCKING: templates lock
196  */
197 static MonoRuntimeGenericContextOtherInfoTemplate*
198 rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate *template, int type_argc, int slot)
199 {
200         int i;
201         MonoRuntimeGenericContextOtherInfoTemplate *oti;
202
203         g_assert (slot >= 0);
204
205         for (oti = get_other_info_templates (template, type_argc), i = 0; i < slot; oti = oti->next, ++i) {
206                 if (!oti)
207                         return NULL;
208         }
209
210         return oti;
211 }
212
213 /*
214  * LOCKING: templates lock
215  */
216 static int
217 rgctx_template_num_other_infos (MonoRuntimeGenericContextTemplate *template, int type_argc)
218 {
219         MonoRuntimeGenericContextOtherInfoTemplate *oti;
220         int i;
221
222         for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next)
223                 ;
224
225         return i;
226 }
227
228 /* Maps from uninstantiated generic classes to GList's of
229  * uninstantiated generic classes whose parent is the key class or an
230  * instance of the key class.
231  *
232  * LOCKING: templates lock
233  */
234 static GHashTable *generic_subclass_hash;
235
236 /*
237  * LOCKING: templates lock
238  */
239 static void
240 class_set_rgctx_template (MonoClass *class, MonoRuntimeGenericContextTemplate *rgctx_template)
241 {
242         if (!class->image->rgctx_template_hash)
243                 class->image->rgctx_template_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
244
245         g_hash_table_insert (class->image->rgctx_template_hash, class, rgctx_template);
246 }
247
248 /*
249  * LOCKING: templates lock
250  */
251 static MonoRuntimeGenericContextTemplate*
252 class_lookup_rgctx_template (MonoClass *class)
253 {
254         MonoRuntimeGenericContextTemplate *template;
255
256         if (!class->image->rgctx_template_hash)
257                 return NULL;
258
259         template = g_hash_table_lookup (class->image->rgctx_template_hash, class);
260
261         return template;
262 }
263
264 /*
265  * LOCKING: templates lock
266  */
267 static void
268 register_generic_subclass (MonoClass *class)
269 {
270         MonoClass *parent = class->parent;
271         MonoClass *subclass;
272         MonoRuntimeGenericContextTemplate *rgctx_template = class_lookup_rgctx_template (class);
273
274         g_assert (rgctx_template);
275
276         if (parent->generic_class)
277                 parent = parent->generic_class->container_class;
278
279         if (!generic_subclass_hash)
280                 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
281
282         subclass = g_hash_table_lookup (generic_subclass_hash, parent);
283         rgctx_template->next_subclass = subclass;
284         g_hash_table_insert (generic_subclass_hash, parent, class);
285 }
286
287 static void
288 move_subclasses_not_in_image_foreach_func (MonoClass *class, MonoClass *subclass, MonoImage *image)
289 {
290         MonoClass *new_list;
291
292         if (class->image == image) {
293                 /* The parent class itself is in the image, so all the
294                    subclasses must be in the image, too.  If not,
295                    we're removing an image containing a class which
296                    still has a subclass in another image. */
297
298                 while (subclass) {
299                         g_assert (subclass->image == image);
300                         subclass = class_lookup_rgctx_template (subclass)->next_subclass;
301                 }
302
303                 return;
304         }
305
306         new_list = NULL;
307         while (subclass) {
308                 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
309                 MonoClass *next = subclass_template->next_subclass;
310
311                 if (subclass->image != image) {
312                         subclass_template->next_subclass = new_list;
313                         new_list = subclass;
314                 }
315
316                 subclass = next;
317         }
318
319         if (new_list)
320                 g_hash_table_insert (generic_subclass_hash, class, new_list);
321 }
322
323 /*
324  * mono_class_unregister_image_generic_subclasses:
325  * @image: an image
326  *
327  * Removes all classes of the image from the generic subclass hash.
328  * Must be called when an image is unloaded.
329  */
330 void
331 mono_class_unregister_image_generic_subclasses (MonoImage *image)
332 {
333         GHashTable *old_hash;
334
335         //g_print ("unregistering image %s\n", image->name);
336
337         if (!generic_subclass_hash)
338                 return;
339
340         templates_lock ();
341
342         old_hash = generic_subclass_hash;
343         generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
344
345         g_hash_table_foreach (old_hash, (GHFunc)move_subclasses_not_in_image_foreach_func, image);
346
347         templates_unlock ();
348
349         g_hash_table_destroy (old_hash);
350 }
351
352 /*
353  * LOCKING: loader lock
354  */
355 static MonoRuntimeGenericContextTemplate*
356 alloc_template (MonoClass *class)
357 {
358         static gboolean inited = FALSE;
359         static int num_allocted = 0;
360         static int num_bytes = 0;
361
362         int size = sizeof (MonoRuntimeGenericContextTemplate);
363
364         if (!inited) {
365                 mono_counters_register ("RGCTX template num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_allocted);
366                 mono_counters_register ("RGCTX template bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_bytes);
367                 inited = TRUE;
368         }
369
370         num_allocted++;
371         num_bytes += size;
372
373         return mono_mempool_alloc0 (class->image->mempool, size);
374 }
375
376 /*
377  * LOCKING: loader lock
378  */
379 static MonoRuntimeGenericContextOtherInfoTemplate*
380 alloc_oti (MonoImage *image)
381 {
382         static gboolean inited = FALSE;
383         static int num_allocted = 0;
384         static int num_bytes = 0;
385
386         int size = sizeof (MonoRuntimeGenericContextOtherInfoTemplate);
387
388         if (!inited) {
389                 mono_counters_register ("RGCTX oti num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_allocted);
390                 mono_counters_register ("RGCTX oti bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_bytes);
391                 inited = TRUE;
392         }
393
394         num_allocted++;
395         num_bytes += size;
396
397         return mono_mempool_alloc0 (image->mempool, size);
398 }
399
400 #define MONO_RGCTX_SLOT_USED_MARKER     ((gpointer)&mono_defaults.object_class->byval_arg)
401
402 /*
403  * LOCKING: templates lock
404  */
405 static void
406 rgctx_template_set_other_slot (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
407         int slot, gpointer data, int info_type)
408 {
409         static gboolean inited = FALSE;
410         static int num_markers = 0;
411         static int num_data = 0;
412
413         int i;
414         MonoRuntimeGenericContextOtherInfoTemplate *list = get_other_info_templates (template, type_argc);
415         MonoRuntimeGenericContextOtherInfoTemplate **oti = &list;
416
417         if (!inited) {
418                 mono_counters_register ("RGCTX oti num markers", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_markers);
419                 mono_counters_register ("RGCTX oti num data", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_data);
420                 inited = TRUE;
421         }
422
423         g_assert (slot >= 0);
424         g_assert (data);
425
426         mono_loader_lock ();
427
428         i = 0;
429         while (i <= slot) {
430                 if (i > 0)
431                         oti = &(*oti)->next;
432                 if (!*oti)
433                         *oti = alloc_oti (image);
434                 ++i;
435         }
436
437         mono_loader_unlock ();
438
439         g_assert (!(*oti)->data);
440         (*oti)->data = data;
441         (*oti)->info_type = info_type;
442
443         set_other_info_templates (template, type_argc, list);
444
445         if (data == MONO_RGCTX_SLOT_USED_MARKER)
446                 ++num_markers;
447         else
448                 ++num_data;
449 }
450
451 /*
452  * mono_method_get_declaring_generic_method:
453  * @method: an inflated method
454  *
455  * Returns an inflated method's declaring method.
456  */
457 MonoMethod*
458 mono_method_get_declaring_generic_method (MonoMethod *method)
459 {
460         MonoMethodInflated *inflated;
461
462         g_assert (method->is_inflated);
463
464         inflated = (MonoMethodInflated*)method;
465
466         return inflated->declaring;
467 }
468
469 /*
470  * mono_class_get_method_generic:
471  * @klass: a class
472  * @method: a method
473  *
474  * Given a class and a generic method, which has to be of an
475  * instantiation of the same class that klass is an instantiation of,
476  * returns the corresponding method in klass.  Example:
477  *
478  * klass is Gen<string>
479  * method is Gen<object>.work<int>
480  *
481  * returns: Gen<string>.work<int>
482  */
483 MonoMethod*
484 mono_class_get_method_generic (MonoClass *klass, MonoMethod *method)
485 {
486         MonoMethod *declaring, *m;
487         int i;
488
489         if (method->is_inflated)
490                 declaring = mono_method_get_declaring_generic_method (method);
491         else
492                 declaring = method;
493
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;
501         }
502         if (i >= klass->method.count)
503                 return NULL;
504
505         if (method != declaring) {
506                 MonoGenericContext context;
507
508                 context.class_inst = NULL;
509                 context.method_inst = mono_method_get_context (method)->method_inst;
510
511                 m = mono_class_inflate_generic_method (m, &context);
512         }
513
514         return m;
515 }
516
517 static gpointer
518 inflate_other_data (gpointer data, int info_type, MonoGenericContext *context, MonoClass *class, gboolean temporary)
519 {
520         g_assert (data);
521
522         if (data == MONO_RGCTX_SLOT_USED_MARKER)
523                 return MONO_RGCTX_SLOT_USED_MARKER;
524
525         switch (info_type)
526         {
527         case MONO_RGCTX_INFO_STATIC_DATA:
528         case MONO_RGCTX_INFO_KLASS:
529         case MONO_RGCTX_INFO_VTABLE:
530         case MONO_RGCTX_INFO_TYPE:
531         case MONO_RGCTX_INFO_REFLECTION_TYPE:
532                 return mono_class_inflate_generic_type_with_mempool (temporary ? NULL : class->image->mempool,
533                         data, context);
534
535         case MONO_RGCTX_INFO_METHOD:
536         case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
537         case MONO_RGCTX_INFO_METHOD_RGCTX: {
538                 MonoMethod *method = data;
539                 MonoMethod *inflated_method;
540                 MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
541                 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
542
543                 mono_metadata_free_type (inflated_type);
544
545                 mono_class_init (inflated_class);
546
547                 if (method->wrapper_type != MONO_WRAPPER_NONE) {
548                         g_assert (info_type != MONO_RGCTX_INFO_METHOD_RGCTX);
549                         g_assert (method->wrapper_type == MONO_WRAPPER_STATIC_RGCTX_INVOKE);
550
551                         method = mono_marshal_method_from_wrapper (method);
552                         method = mono_class_inflate_generic_method (method, context);
553                         method = mono_marshal_get_static_rgctx_invoke (method);
554                 }
555
556                 inflated_method = mono_class_inflate_generic_method (method, context);
557                 mono_class_init (inflated_method->klass);
558                 g_assert (inflated_method->klass == inflated_class);
559                 return inflated_method;
560         }
561
562         case MONO_RGCTX_INFO_CLASS_FIELD: {
563                 MonoClassField *field = data;
564                 MonoType *inflated_type = mono_class_inflate_generic_type (&field->parent->byval_arg, context);
565                 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
566                 int i = field - field->parent->fields;
567                 gpointer dummy = NULL;
568
569                 mono_metadata_free_type (inflated_type);
570
571                 mono_class_get_fields (inflated_class, &dummy);
572                 g_assert (inflated_class->fields);
573
574                 return &inflated_class->fields [i];
575         }
576
577         default:
578                 g_assert_not_reached ();
579         }
580 }
581
582 static gpointer
583 inflate_other_info (MonoRuntimeGenericContextOtherInfoTemplate *oti,
584         MonoGenericContext *context, MonoClass *class, gboolean temporary)
585 {
586         return inflate_other_data (oti->data, oti->info_type, context, class, temporary);
587 }
588
589 static void
590 free_inflated_info (int info_type, gpointer info)
591 {
592         if (!info)
593                 return;
594
595         switch (info_type) {
596         case MONO_RGCTX_INFO_STATIC_DATA:
597         case MONO_RGCTX_INFO_KLASS:
598         case MONO_RGCTX_INFO_VTABLE:
599         case MONO_RGCTX_INFO_TYPE:
600         case MONO_RGCTX_INFO_REFLECTION_TYPE:
601                 mono_metadata_free_type (info);
602                 break;
603         default:
604                 break;
605         }
606 }
607
608 static MonoRuntimeGenericContextOtherInfoTemplate
609 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free);
610
611 /*
612  * mono_class_get_runtime_generic_context_template:
613  * @class: a class
614  *
615  * Looks up or constructs, if necessary, the runtime generic context
616  * for class.
617  */
618 static MonoRuntimeGenericContextTemplate*
619 mono_class_get_runtime_generic_context_template (MonoClass *class)
620 {
621         MonoRuntimeGenericContextTemplate *parent_template, *template;
622         MonoGenericInst *inst;
623         guint32 i;
624
625         g_assert (!class->generic_class);
626
627         templates_lock ();
628         template = class_lookup_rgctx_template (class);
629         templates_unlock ();
630
631         if (template)
632                 return template;
633
634         if (class->generic_container)
635                 inst = class->generic_container->context.class_inst;
636         else
637                 inst = NULL;
638
639         mono_loader_lock ();
640         template = alloc_template (class);
641         mono_loader_unlock ();
642
643         templates_lock ();
644
645         if (class->parent) {
646                 if (class->parent->generic_class) {
647                         guint32 num_entries;
648                         int max_argc, type_argc;
649
650                         parent_template = mono_class_get_runtime_generic_context_template
651                                 (class->parent->generic_class->container_class);
652
653                         max_argc = template_get_max_argc (parent_template);
654
655                         for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
656                                 num_entries = rgctx_template_num_other_infos (parent_template, type_argc);
657
658                                 /* FIXME: quadratic! */
659                                 for (i = 0; i < num_entries; ++i) {
660                                         MonoRuntimeGenericContextOtherInfoTemplate oti;
661
662                                         oti = class_get_rgctx_template_oti (class->parent, type_argc, i, FALSE, NULL);
663                                         if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
664                                                 rgctx_template_set_other_slot (class->image, template, type_argc, i,
665                                                         oti.data, oti.info_type);
666                                         }
667                                 }
668                         }
669                 } else {
670                         MonoRuntimeGenericContextOtherInfoTemplate *oti;
671                         int max_argc, type_argc;
672
673                         parent_template = mono_class_get_runtime_generic_context_template (class->parent);
674
675                         max_argc = template_get_max_argc (parent_template);
676
677                         for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
678                                 /* FIXME: quadratic! */
679                                 for (i = 0, oti = parent_template->other_infos; oti; ++i, oti = oti->next) {
680                                         if (oti->data && oti->data != MONO_RGCTX_SLOT_USED_MARKER) {
681                                                 rgctx_template_set_other_slot (class->image, template, type_argc, i,
682                                                         oti->data, oti->info_type);
683                                         }
684                                 }
685                         }
686                 }
687         }
688
689         if (class_lookup_rgctx_template (class)) {
690                 /* some other thread already set the template */
691                 template = class_lookup_rgctx_template (class);
692         } else {
693                 class_set_rgctx_template (class, template);
694
695                 if (class->parent)
696                         register_generic_subclass (class);
697         }
698
699         templates_unlock ();
700
701         return template;
702 }
703
704 /*
705  * temporary signifies whether the inflated info (oti.data) will be
706  * used temporarily, in which case it might be heap-allocated, or
707  * permanently, in which case it will be mempool-allocated.  If
708  * temporary is set then *do_free will return whether the returned
709  * data must be freed.
710  */
711 static MonoRuntimeGenericContextOtherInfoTemplate
712 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free)
713 {
714         g_assert ((temporary && do_free) || (!temporary && !do_free));
715
716         if (class->generic_class) {
717                 MonoRuntimeGenericContextOtherInfoTemplate oti;
718                 gboolean tmp_do_free;
719
720                 oti = class_get_rgctx_template_oti (class->generic_class->container_class,
721                         type_argc, slot, TRUE, &tmp_do_free);
722                 if (oti.data) {
723                         gpointer info = oti.data;
724                         oti.data = inflate_other_info (&oti, &class->generic_class->context, class, temporary);
725                         if (tmp_do_free)
726                                 free_inflated_info (oti.info_type, info);
727                 }
728                 if (temporary)
729                         *do_free = TRUE;
730
731                 return oti;
732         } else {
733                 MonoRuntimeGenericContextTemplate *template;
734                 MonoRuntimeGenericContextOtherInfoTemplate *oti;
735
736                 template = mono_class_get_runtime_generic_context_template (class);
737                 oti = rgctx_template_get_other_slot (template, type_argc, slot);
738                 g_assert (oti);
739
740                 if (temporary)
741                         *do_free = FALSE;
742
743                 return *oti;
744         }
745 }
746
747 static MonoClass*
748 class_uninstantiated (MonoClass *class)
749 {
750         if (class->generic_class)
751                 return class->generic_class->container_class;
752         return class;
753 }
754
755 static gpointer
756 class_type_info (MonoDomain *domain, MonoClass *class, int info_type)
757 {
758         switch (info_type) {
759         case MONO_RGCTX_INFO_STATIC_DATA:
760                 return mono_class_vtable (domain, class)->data;
761         case MONO_RGCTX_INFO_KLASS:
762                 return class;
763         case MONO_RGCTX_INFO_VTABLE:
764                 return mono_class_vtable (domain, class);
765         default:
766                 g_assert_not_reached ();
767         }
768 }
769
770 static gpointer
771 instantiate_other_info (MonoDomain *domain, MonoRuntimeGenericContextOtherInfoTemplate *oti,
772         MonoGenericContext *context, MonoClass *class)
773 {
774         gpointer data;
775         gboolean temporary;
776
777         if (!oti->data)
778                 return NULL;
779
780         switch (oti->info_type) {
781         case MONO_RGCTX_INFO_STATIC_DATA:
782         case MONO_RGCTX_INFO_KLASS:
783         case MONO_RGCTX_INFO_VTABLE:
784                 temporary = TRUE;
785                 break;
786         default:
787                 temporary = FALSE;
788         }
789
790         data = inflate_other_info (oti, context, class, temporary);
791
792         switch (oti->info_type) {
793         case MONO_RGCTX_INFO_STATIC_DATA:
794         case MONO_RGCTX_INFO_KLASS:
795         case MONO_RGCTX_INFO_VTABLE: {
796                 MonoClass *arg_class = mono_class_from_mono_type (data);
797
798                 free_inflated_info (oti->info_type, data);
799                 g_assert (arg_class);
800
801                 return class_type_info (domain, arg_class, oti->info_type);
802         }
803         case MONO_RGCTX_INFO_TYPE:
804                 return data;
805         case MONO_RGCTX_INFO_REFLECTION_TYPE:
806                 return mono_type_get_object (domain, data);
807         case MONO_RGCTX_INFO_METHOD:
808                 return data;
809         case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
810                 return mono_compile_method (data);
811         case MONO_RGCTX_INFO_CLASS_FIELD:
812                 return data;
813         case MONO_RGCTX_INFO_METHOD_RGCTX: {
814                 MonoMethodInflated *method = data;
815
816                 g_assert (method->method.method.is_inflated);
817                 g_assert (method->context.method_inst);
818
819                 return mono_method_lookup_rgctx (mono_class_vtable (domain, method->method.method.klass),
820                         method->context.method_inst);
821         }
822         default:
823                 g_assert_not_reached ();
824         }
825 }
826
827 /*
828  * LOCKING: templates lock
829  */
830 static void
831 fill_in_rgctx_template_slot (MonoClass *class, int type_argc, int index, gpointer data, int info_type)
832 {
833         MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
834         MonoClass *subclass;
835
836         g_assert (!class->generic_class);
837
838         rgctx_template_set_other_slot (class->image, template, type_argc, index, data, info_type);
839
840         /* Recurse for all subclasses */
841         if (generic_subclass_hash)
842                 subclass = g_hash_table_lookup (generic_subclass_hash, class);
843         else
844                 subclass = NULL;
845
846         while (subclass) {
847                 MonoRuntimeGenericContextOtherInfoTemplate subclass_oti;
848                 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
849
850                 g_assert (!subclass->generic_class);
851                 g_assert (subclass_template);
852
853                 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, NULL);
854                 g_assert (subclass_oti.data);
855
856                 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
857
858                 subclass = subclass_template->next_subclass;
859         }
860 }
861
862 /*
863  * LOCKING: templates lock
864  */
865 static int
866 register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type)
867 {
868         int i;
869         MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
870         MonoClass *parent;
871         MonoRuntimeGenericContextOtherInfoTemplate *oti;
872
873         for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
874                 if (!oti->data)
875                         break;
876         }
877
878         //g_print ("template %s . other_infos [%d] = %s\n", mono_type_get_full_name (class), i, mono_type_get_full_name (other_class));
879
880         /* Mark the slot as used in all parent classes (until we find
881            a parent class which already has it marked used). */
882         parent = class->parent;
883         while (parent != NULL) {
884                 MonoRuntimeGenericContextTemplate *parent_template;
885                 MonoRuntimeGenericContextOtherInfoTemplate *oti;
886
887                 if (parent->generic_class)
888                         parent = parent->generic_class->container_class;
889
890                 parent_template = mono_class_get_runtime_generic_context_template (parent);
891                 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
892
893                 if (oti && oti->data)
894                         break;
895
896                 rgctx_template_set_other_slot (parent->image, parent_template, type_argc, i,
897                                 MONO_RGCTX_SLOT_USED_MARKER, 0);
898
899                 parent = parent->parent;
900         }
901
902         /* Fill in the slot in this class and in all subclasses
903            recursively. */
904         fill_in_rgctx_template_slot (class, type_argc, i, data, info_type);
905
906         return i;
907 }
908
909 static gboolean
910 other_info_equal (gpointer data1, gpointer data2, int info_type)
911 {
912         switch (info_type) {
913         case MONO_RGCTX_INFO_STATIC_DATA:
914         case MONO_RGCTX_INFO_KLASS:
915         case MONO_RGCTX_INFO_VTABLE:
916         case MONO_RGCTX_INFO_TYPE:
917         case MONO_RGCTX_INFO_REFLECTION_TYPE:
918                 return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
919         case MONO_RGCTX_INFO_METHOD:
920         case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
921         case MONO_RGCTX_INFO_CLASS_FIELD:
922         case MONO_RGCTX_INFO_METHOD_RGCTX:
923                 return data1 == data2;
924         default:
925                 g_assert_not_reached ();
926         }
927 }
928
929 static int
930 lookup_or_register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type,
931         MonoGenericContext *generic_context)
932 {
933         static gboolean inited = FALSE;
934         static int max_slot = 0;
935
936         MonoRuntimeGenericContextTemplate *rgctx_template =
937                 mono_class_get_runtime_generic_context_template (class);
938         MonoRuntimeGenericContextOtherInfoTemplate *oti;
939         int i;
940
941         g_assert (!class->generic_class);
942         g_assert (class->generic_container || type_argc);
943
944         templates_lock ();
945
946         for (oti = get_other_info_templates (rgctx_template, type_argc), i = 0; oti; oti = oti->next, ++i) {
947                 gpointer inflated_data;
948
949                 if (!oti || oti->info_type != info_type || !oti->data)
950                         continue;
951
952                 inflated_data = inflate_other_info (oti, generic_context, class, TRUE);
953
954                 if (other_info_equal (data, inflated_data, info_type)) {
955                         templates_unlock ();
956                         free_inflated_info (oti->info_type, inflated_data);
957                         return i;
958                 }
959                 free_inflated_info (info_type, inflated_data);
960         }
961
962         i = register_other_info (class, type_argc, data, info_type);
963
964         templates_unlock ();
965
966         if (!inited) {
967                 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
968                 inited = TRUE;
969         }
970         if (i > max_slot)
971                 max_slot = i;
972
973         return i;
974 }
975
976 /*
977  * mono_method_lookup_or_register_other_info:
978  * @method: a method
979  * @in_mrgctx: whether to put the data into the MRGCTX
980  * @data: the info data
981  * @info_type: the type of info to register about data
982  * @generic_context: a generic context
983  *
984  * Looks up and, if necessary, adds information about other_class in
985  * method's or method's class runtime generic context.  Returns the
986  * encoded slot number.
987  */
988 guint32
989 mono_method_lookup_or_register_other_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
990         int info_type, MonoGenericContext *generic_context)
991 {
992         MonoClass *class = method->klass;
993         int type_argc, index;
994
995         if (in_mrgctx) {
996                 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
997
998                 g_assert (method->is_inflated && method_inst);
999                 type_argc = method_inst->type_argc;
1000                 g_assert (type_argc > 0);
1001         } else {
1002                 type_argc = 0;
1003         }
1004
1005         index = lookup_or_register_other_info (class, type_argc, data, info_type, generic_context);
1006
1007         //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
1008
1009         if (in_mrgctx)
1010                 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
1011         else
1012                 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
1013 }
1014
1015 /*
1016  * mono_class_rgctx_get_array_size:
1017  * @n: The number of the array
1018  * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
1019  *
1020  * Returns the number of slots in the n'th array of a (M)RGCTX.  That
1021  * number includes the slot for linking and - for MRGCTXs - the two
1022  * slots in the first array for additional information.
1023  */
1024 int
1025 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
1026 {
1027         g_assert (n >= 0 && n < 30);
1028
1029         if (mrgctx)
1030                 return 6 << n;
1031         else
1032                 return 4 << n;
1033 }
1034
1035 /*
1036  * LOCKING: domain lock
1037  */
1038 static gpointer*
1039 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
1040 {
1041         static gboolean inited = FALSE;
1042         static int rgctx_num_alloced = 0;
1043         static int rgctx_bytes_alloced = 0;
1044         static int mrgctx_num_alloced = 0;
1045         static int mrgctx_bytes_alloced = 0;
1046
1047         int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
1048         gpointer array = mono_mempool_alloc0 (domain->mp, size);
1049
1050         if (!inited) {
1051                 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
1052                 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
1053                 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
1054                 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
1055                 inited = TRUE;
1056         }
1057
1058         if (is_mrgctx) {
1059                 mrgctx_num_alloced++;
1060                 mrgctx_bytes_alloced += size;
1061         } else {
1062                 rgctx_num_alloced++;
1063                 rgctx_bytes_alloced += size;
1064         }
1065
1066         return array;
1067 }
1068
1069 /*
1070  * LOCKING: domain lock
1071  */
1072 static gpointer
1073 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
1074                 MonoGenericInst *method_inst)
1075 {
1076         gpointer info;
1077         int i, first_slot, size;
1078         MonoDomain *domain = class_vtable->domain;
1079         MonoClass *class = class_vtable->klass;
1080         MonoGenericContext *class_context = class->generic_class ? &class->generic_class->context : NULL;
1081         MonoRuntimeGenericContextOtherInfoTemplate oti;
1082         MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
1083         int rgctx_index;
1084         gboolean do_free;
1085
1086         g_assert (rgctx);
1087
1088         /* First check whether that slot isn't already instantiated.
1089            This might happen because lookup doesn't lock.  Allocate
1090            arrays on the way. */
1091         first_slot = 0;
1092         size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
1093         if (method_inst)
1094                 size -= sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
1095         for (i = 0; ; ++i) {
1096                 int offset;
1097
1098                 if (method_inst && i == 0)
1099                         offset = sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
1100                 else
1101                         offset = 0;
1102
1103                 if (slot < first_slot + size - 1) {
1104                         rgctx_index = slot - first_slot + 1 + offset;
1105                         info = rgctx [rgctx_index];
1106                         if (info)
1107                                 return info;
1108                         break;
1109                 }
1110                 if (!rgctx [offset + 0])
1111                         rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
1112                 rgctx = rgctx [offset + 0];
1113                 first_slot += size - 1;
1114                 size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
1115         }
1116
1117         g_assert (!rgctx [rgctx_index]);
1118
1119         oti = class_get_rgctx_template_oti (class_uninstantiated (class),
1120                         method_inst ? method_inst->type_argc : 0, slot, TRUE, &do_free);
1121
1122         /*
1123         if (method_inst)
1124                 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
1125         */
1126
1127         info = rgctx [rgctx_index] = instantiate_other_info (domain, &oti, &context, class);
1128
1129         if (do_free)
1130                 free_inflated_info (oti.info_type, oti.data);
1131
1132         return info;
1133 }
1134
1135 /*
1136  * mono_class_fill_runtime_generic_context:
1137  * @class_vtable: a vtable
1138  * @slot: a slot index to be instantiated
1139  *
1140  * Instantiates a slot in the RGCTX.
1141  */
1142 gpointer
1143 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot)
1144 {
1145         static gboolean inited = FALSE;
1146         static int num_alloced = 0;
1147
1148         MonoDomain *domain = class_vtable->domain;
1149         MonoRuntimeGenericContext *rgctx;
1150         gpointer info;
1151
1152         mono_domain_lock (domain);
1153
1154         if (!inited) {
1155                 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
1156                 inited = TRUE;
1157         }
1158
1159         rgctx = class_vtable->runtime_generic_context;
1160         if (!rgctx) {
1161                 rgctx = alloc_rgctx_array (domain, 0, FALSE);
1162                 class_vtable->runtime_generic_context = rgctx;
1163                 num_alloced++;
1164         }
1165
1166         info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
1167
1168         mono_domain_unlock (domain);
1169
1170         return info;
1171 }
1172
1173 /*
1174  * mono_method_fill_runtime_generic_context:
1175  * @mrgctx: an MRGCTX
1176  * @slot: a slot index to be instantiated
1177  *
1178  * Instantiates a slot in the MRGCTX.
1179  */
1180 gpointer
1181 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot)
1182 {
1183         MonoDomain *domain = mrgctx->class_vtable->domain;
1184         gpointer info;
1185
1186         mono_domain_lock (domain);
1187
1188         info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot,
1189                 mrgctx->method_inst);
1190
1191         mono_domain_unlock (domain);
1192
1193         return info;
1194 }
1195
1196 static guint
1197 mrgctx_hash_func (gconstpointer key)
1198 {
1199         const MonoMethodRuntimeGenericContext *mrgctx = key;
1200
1201         return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
1202 }
1203
1204 static gboolean
1205 mrgctx_equal_func (gconstpointer a, gconstpointer b)
1206 {
1207         const MonoMethodRuntimeGenericContext *mrgctx1 = a;
1208         const MonoMethodRuntimeGenericContext *mrgctx2 = b;
1209
1210         return mrgctx1->class_vtable == mrgctx2->class_vtable &&
1211                 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
1212 }
1213
1214 /*
1215  * mono_method_lookup_rgctx:
1216  * @class_vtable: a vtable
1217  * @method_inst: the method inst of a generic method
1218  *
1219  * Returns the MRGCTX for the generic method(s) with the given
1220  * method_inst of the given class_vtable.
1221  */
1222 MonoMethodRuntimeGenericContext*
1223 mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
1224 {
1225         MonoDomain *domain = class_vtable->domain;
1226         MonoMethodRuntimeGenericContext *mrgctx;
1227         MonoMethodRuntimeGenericContext key;
1228
1229         g_assert (!class_vtable->klass->generic_container);
1230         g_assert (!method_inst->is_open);
1231
1232         mono_domain_lock (domain);
1233         if (!domain->method_rgctx_hash)
1234                 domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
1235
1236         key.class_vtable = class_vtable;
1237         key.method_inst = method_inst;
1238
1239         mrgctx = g_hash_table_lookup (domain->method_rgctx_hash, &key);
1240
1241         if (!mrgctx) {
1242                 //int i;
1243
1244                 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
1245                 mrgctx->class_vtable = class_vtable;
1246                 mrgctx->method_inst = method_inst;
1247
1248                 g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
1249
1250                 /*
1251                 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
1252                 for (i = 0; i < method_inst->type_argc; ++i)
1253                         g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
1254                 g_print (">\n");
1255                 */
1256         }
1257
1258         mono_domain_unlock (domain);
1259
1260         g_assert (mrgctx);
1261
1262         return mrgctx;
1263 }