2008-06-13 Mark Probst <mark.probst@gmail.com>
[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 static gpointer
470 inflate_other_data (gpointer data, int info_type, MonoGenericContext *context)
471 {
472         g_assert (data);
473
474         if (data == MONO_RGCTX_SLOT_USED_MARKER)
475                 return MONO_RGCTX_SLOT_USED_MARKER;
476
477         switch (info_type)
478         {
479         case MONO_RGCTX_INFO_STATIC_DATA:
480         case MONO_RGCTX_INFO_KLASS:
481         case MONO_RGCTX_INFO_VTABLE:
482         case MONO_RGCTX_INFO_TYPE:
483         case MONO_RGCTX_INFO_REFLECTION_TYPE:
484                 return mono_class_inflate_generic_type (data, context);
485
486         case MONO_RGCTX_INFO_METHOD:
487         case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
488         case MONO_RGCTX_INFO_METHOD_RGCTX: {
489                 MonoMethod *method = data;
490                 MonoMethod *inflated_method;
491                 MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
492                 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
493
494                 mono_class_init (inflated_class);
495
496                 if (method->wrapper_type != MONO_WRAPPER_NONE) {
497                         g_assert (info_type != MONO_RGCTX_INFO_METHOD_RGCTX);
498                         g_assert (method->wrapper_type == MONO_WRAPPER_STATIC_RGCTX_INVOKE);
499
500                         method = mono_marshal_method_from_wrapper (method);
501                         method = mono_class_inflate_generic_method (method, context);
502                         method = mono_marshal_get_static_rgctx_invoke (method);
503                 }
504
505                 inflated_method = mono_class_inflate_generic_method (method, context);
506                 mono_class_init (inflated_method->klass);
507                 g_assert (inflated_method->klass == inflated_class);
508                 return inflated_method;
509         }
510
511         case MONO_RGCTX_INFO_CLASS_FIELD: {
512                 MonoClassField *field = data;
513                 MonoType *inflated_type = mono_class_inflate_generic_type (&field->parent->byval_arg, context);
514                 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
515                 int i = field - field->parent->fields;
516                 gpointer dummy;
517
518                 mono_class_get_fields (inflated_class, &dummy);
519                 g_assert (inflated_class->fields);
520
521                 return &inflated_class->fields [i];
522         }
523
524         default:
525                 g_assert_not_reached ();
526         }
527 }
528
529 static gpointer
530 inflate_other_info (MonoRuntimeGenericContextOtherInfoTemplate *oti, MonoGenericContext *context)
531 {
532         return inflate_other_data (oti->data, oti->info_type, context);
533 }
534
535 static MonoRuntimeGenericContextOtherInfoTemplate
536 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot);
537
538 /*
539  * mono_class_get_runtime_generic_context_template:
540  * @class: a class
541  *
542  * Looks up or constructs, if necessary, the runtime generic context
543  * for class.
544  */
545 static MonoRuntimeGenericContextTemplate*
546 mono_class_get_runtime_generic_context_template (MonoClass *class)
547 {
548         MonoRuntimeGenericContextTemplate *parent_template, *template;
549         MonoGenericInst *inst;
550         guint32 i;
551
552         g_assert (!class->generic_class);
553
554         templates_lock ();
555         template = class_lookup_rgctx_template (class);
556         templates_unlock ();
557
558         if (template)
559                 return template;
560
561         if (class->generic_container)
562                 inst = class->generic_container->context.class_inst;
563         else
564                 inst = NULL;
565
566         mono_loader_lock ();
567         template = alloc_template (class);
568         mono_loader_unlock ();
569
570         templates_lock ();
571
572         if (class->parent) {
573                 if (class->parent->generic_class) {
574                         guint32 num_entries;
575                         int max_argc, type_argc;
576
577                         parent_template = mono_class_get_runtime_generic_context_template
578                                 (class->parent->generic_class->container_class);
579
580                         max_argc = template_get_max_argc (parent_template);
581
582                         for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
583                                 num_entries = rgctx_template_num_other_infos (parent_template, type_argc);
584
585                                 /* FIXME: quadratic! */
586                                 for (i = 0; i < num_entries; ++i) {
587                                         MonoRuntimeGenericContextOtherInfoTemplate oti;
588
589                                         oti = class_get_rgctx_template_oti (class->parent, type_argc, i);
590                                         if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
591                                                 rgctx_template_set_other_slot (class->image, template, type_argc, i,
592                                                         oti.data, oti.info_type);
593                                         }
594                                 }
595                         }
596                 } else {
597                         MonoRuntimeGenericContextOtherInfoTemplate *oti;
598                         int max_argc, type_argc;
599
600                         parent_template = mono_class_get_runtime_generic_context_template (class->parent);
601
602                         max_argc = template_get_max_argc (parent_template);
603
604                         for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
605                                 /* FIXME: quadratic! */
606                                 for (i = 0, oti = parent_template->other_infos; oti; ++i, oti = oti->next) {
607                                         if (oti->data && oti->data != MONO_RGCTX_SLOT_USED_MARKER) {
608                                                 rgctx_template_set_other_slot (class->image, template, type_argc, i,
609                                                         oti->data, oti->info_type);
610                                         }
611                                 }
612                         }
613                 }
614         }
615
616         if (class_lookup_rgctx_template (class)) {
617                 /* some other thread already set the template */
618                 template = class_lookup_rgctx_template (class);
619         } else {
620                 class_set_rgctx_template (class, template);
621
622                 if (class->parent)
623                         register_generic_subclass (class);
624         }
625
626         templates_unlock ();
627
628         return template;
629 }
630
631 static MonoRuntimeGenericContextOtherInfoTemplate
632 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot)
633 {
634         if (class->generic_class) {
635                 MonoRuntimeGenericContextOtherInfoTemplate oti;
636
637                 oti = class_get_rgctx_template_oti (class->generic_class->container_class, type_argc, slot);
638                 if (oti.data)
639                         oti.data = inflate_other_info (&oti, &class->generic_class->context);
640
641                 return oti;
642         } else {
643                 MonoRuntimeGenericContextTemplate *template;
644                 MonoRuntimeGenericContextOtherInfoTemplate *oti;
645
646                 template = mono_class_get_runtime_generic_context_template (class);
647                 oti = rgctx_template_get_other_slot (template, type_argc, slot);
648                 g_assert (oti);
649
650                 return *oti;
651         }
652 }
653
654 static MonoClass*
655 class_uninstantiated (MonoClass *class)
656 {
657         if (class->generic_class)
658                 return class->generic_class->container_class;
659         return class;
660 }
661
662 static gpointer
663 class_type_info (MonoDomain *domain, MonoClass *class, int info_type)
664 {
665         switch (info_type) {
666         case MONO_RGCTX_INFO_STATIC_DATA:
667                 return mono_class_vtable (domain, class)->data;
668         case MONO_RGCTX_INFO_KLASS:
669                 return class;
670         case MONO_RGCTX_INFO_VTABLE:
671                 return mono_class_vtable (domain, class);
672         default:
673                 g_assert_not_reached ();
674         }
675 }
676
677 static gpointer
678 instantiate_other_info (MonoDomain *domain, MonoRuntimeGenericContextOtherInfoTemplate *oti, MonoGenericContext *context)
679 {
680         gpointer data;
681
682         if (!oti->data)
683                 return NULL;
684
685         data = inflate_other_info (oti, context);
686
687         switch (oti->info_type) {
688         case MONO_RGCTX_INFO_STATIC_DATA:
689         case MONO_RGCTX_INFO_KLASS:
690         case MONO_RGCTX_INFO_VTABLE: {
691                 MonoClass *arg_class = mono_class_from_mono_type (data);
692
693                 g_assert (arg_class);
694
695                 return class_type_info (domain, arg_class, oti->info_type);
696         }
697         case MONO_RGCTX_INFO_TYPE:
698                 return data;
699         case MONO_RGCTX_INFO_REFLECTION_TYPE:
700                 return mono_type_get_object (domain, data);
701         case MONO_RGCTX_INFO_METHOD:
702                 return data;
703         case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
704                 return mono_compile_method (data);
705         case MONO_RGCTX_INFO_CLASS_FIELD:
706                 return data;
707         case MONO_RGCTX_INFO_METHOD_RGCTX: {
708                 MonoMethodInflated *method = data;
709
710                 g_assert (method->method.method.is_inflated);
711                 g_assert (method->context.method_inst);
712
713                 return mono_method_lookup_rgctx (mono_class_vtable (domain, method->method.method.klass),
714                         method->context.method_inst);
715         }
716         default:
717                 g_assert_not_reached ();
718         }
719 }
720
721 /*
722  * LOCKING: templates lock
723  */
724 static void
725 fill_in_rgctx_template_slot (MonoClass *class, int type_argc, int index, gpointer data, int info_type)
726 {
727         MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
728         MonoClass *subclass;
729         int old_length, new_length;
730         int old_instances_length = -1;
731
732         g_assert (!class->generic_class);
733
734         old_length = rgctx_template_num_other_infos (template, type_argc);
735         rgctx_template_set_other_slot (class->image, template, type_argc, index, data, info_type);
736         new_length = rgctx_template_num_other_infos (template, type_argc);
737
738         if (old_instances_length < 0)
739                 old_instances_length = old_length;
740
741         /* The reason why the instance's other_infos list can be
742          * shorter than the uninstanted class's is that when we mark
743          * slots as used in superclasses we only do that in the
744          * uninstantiated classes, not in the instances.
745          */
746         g_assert (old_instances_length <= old_length);
747
748         /* Recurse for all subclasses */
749         if (generic_subclass_hash)
750                 subclass = g_hash_table_lookup (generic_subclass_hash, class);
751         else
752                 subclass = NULL;
753
754         while (subclass) {
755                 MonoRuntimeGenericContextOtherInfoTemplate subclass_oti;
756                 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
757
758                 g_assert (!subclass->generic_class);
759                 g_assert (subclass_template);
760
761                 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index);
762                 g_assert (subclass_oti.data);
763
764                 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
765
766                 subclass = subclass_template->next_subclass;
767         }
768 }
769
770 /*
771  * LOCKING: templates lock
772  */
773 static int
774 register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type)
775 {
776         int i;
777         MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
778         MonoClass *parent;
779         MonoRuntimeGenericContextOtherInfoTemplate *oti;
780
781         for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
782                 if (!oti->data)
783                         break;
784         }
785
786         //g_print ("template %s . other_infos [%d] = %s\n", mono_type_get_full_name (class), i, mono_type_get_full_name (other_class));
787
788         /* Mark the slot as used in all parent classes (until we find
789            a parent class which already has it marked used). */
790         parent = class->parent;
791         while (parent != NULL) {
792                 MonoRuntimeGenericContextTemplate *parent_template;
793                 MonoRuntimeGenericContextOtherInfoTemplate *oti;
794
795                 if (parent->generic_class)
796                         parent = parent->generic_class->container_class;
797
798                 parent_template = mono_class_get_runtime_generic_context_template (parent);
799                 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
800
801                 if (oti && oti->data)
802                         break;
803
804                 rgctx_template_set_other_slot (parent->image, parent_template, type_argc, i,
805                                 MONO_RGCTX_SLOT_USED_MARKER, 0);
806
807                 parent = parent->parent;
808         }
809
810         /* Fill in the slot in this class and in all subclasses
811            recursively. */
812         fill_in_rgctx_template_slot (class, type_argc, i, data, info_type);
813
814         return i;
815 }
816
817 static gboolean
818 other_info_equal (gpointer data1, gpointer data2, int info_type)
819 {
820         switch (info_type) {
821         case MONO_RGCTX_INFO_STATIC_DATA:
822         case MONO_RGCTX_INFO_KLASS:
823         case MONO_RGCTX_INFO_VTABLE:
824         case MONO_RGCTX_INFO_TYPE:
825         case MONO_RGCTX_INFO_REFLECTION_TYPE:
826                 return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
827         case MONO_RGCTX_INFO_METHOD:
828         case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
829         case MONO_RGCTX_INFO_CLASS_FIELD:
830         case MONO_RGCTX_INFO_METHOD_RGCTX:
831                 return data1 == data2;
832         default:
833                 g_assert_not_reached ();
834         }
835 }
836
837 static int
838 lookup_or_register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type,
839         MonoGenericContext *generic_context)
840 {
841         static gboolean inited = FALSE;
842         static int max_slot = 0;
843
844         MonoRuntimeGenericContextTemplate *rgctx_template =
845                 mono_class_get_runtime_generic_context_template (class);
846         MonoRuntimeGenericContextOtherInfoTemplate *oti;
847         int i;
848
849         g_assert (!class->generic_class);
850         g_assert (class->generic_container || type_argc);
851
852         templates_lock ();
853
854         for (oti = get_other_info_templates (rgctx_template, type_argc), i = 0; oti; oti = oti->next, ++i) {
855                 gpointer inflated_data;
856
857                 if (!oti || oti->info_type != info_type || !oti->data)
858                         continue;
859
860                 inflated_data = inflate_other_info (oti, generic_context);
861
862                 if (other_info_equal (data, inflated_data, info_type)) {
863                         templates_unlock ();
864                         return i;
865                 }
866         }
867
868         i = register_other_info (class, type_argc, data, info_type);
869
870         templates_unlock ();
871
872         if (!inited) {
873                 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
874                 inited = TRUE;
875         }
876         if (i > max_slot)
877                 max_slot = i;
878
879         return i;
880 }
881
882 /*
883  * mono_method_lookup_or_register_other_info:
884  * @method: a method
885  * @in_mrgctx: whether to put the data into the MRGCTX
886  * @data: the info data
887  * @info_type: the type of info to register about data
888  * @generic_context: a generic context
889  *
890  * Looks up and, if necessary, adds information about other_class in
891  * method's or method's class runtime generic context.  Returns the
892  * encoded slot number.
893  */
894 guint32
895 mono_method_lookup_or_register_other_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
896         int info_type, MonoGenericContext *generic_context)
897 {
898         MonoClass *class = method->klass;
899         int type_argc, index;
900
901         if (in_mrgctx) {
902                 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
903
904                 g_assert (method->is_inflated && method_inst);
905                 type_argc = method_inst->type_argc;
906                 g_assert (type_argc > 0);
907         } else {
908                 type_argc = 0;
909         }
910
911         index = lookup_or_register_other_info (class, type_argc, data, info_type, generic_context);
912
913         //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
914
915         if (in_mrgctx)
916                 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
917         else
918                 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
919 }
920
921 int
922 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
923 {
924         g_assert (n >= 0 && n < 30);
925
926         if (mrgctx)
927                 return 6 << n;
928         else
929                 return 4 << n;
930 }
931
932 /*
933  * LOCKING: domain lock
934  */
935 static gpointer*
936 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
937 {
938         static gboolean inited = FALSE;
939         static int rgctx_num_alloced = 0;
940         static int rgctx_bytes_alloced = 0;
941         static int mrgctx_num_alloced = 0;
942         static int mrgctx_bytes_alloced = 0;
943
944         int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
945         gpointer array = mono_mempool_alloc0 (domain->mp, size);
946
947         if (!inited) {
948                 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
949                 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
950                 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
951                 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
952                 inited = TRUE;
953         }
954
955         if (is_mrgctx) {
956                 mrgctx_num_alloced++;
957                 mrgctx_bytes_alloced += size;
958         } else {
959                 rgctx_num_alloced++;
960                 rgctx_bytes_alloced += size;
961         }
962
963         return array;
964 }
965
966 /*
967  * LOCKING: domain lock
968  */
969 static gpointer
970 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
971                 MonoGenericInst *method_inst)
972 {
973         gpointer info;
974         int i, first_slot, size;
975         MonoDomain *domain = class_vtable->domain;
976         MonoClass *class = class_vtable->klass;
977         MonoGenericContext *class_context = class->generic_class ? &class->generic_class->context : NULL;
978         MonoRuntimeGenericContextOtherInfoTemplate oti;
979         MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
980         int rgctx_index;
981
982         g_assert (rgctx);
983
984         /* First check whether that slot isn't already instantiated.
985            This might happen because lookup doesn't lock.  Allocate
986            arrays on the way. */
987         first_slot = 0;
988         size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
989         if (method_inst)
990                 size -= sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
991         for (i = 0; ; ++i) {
992                 int offset;
993
994                 if (method_inst && i == 0)
995                         offset = sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
996                 else
997                         offset = 0;
998
999                 if (slot < first_slot + size - 1) {
1000                         rgctx_index = slot - first_slot + 1 + offset;
1001                         info = rgctx [rgctx_index];
1002                         if (info)
1003                                 return info;
1004                         break;
1005                 }
1006                 if (!rgctx [offset + 0])
1007                         rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
1008                 rgctx = rgctx [offset + 0];
1009                 first_slot += size - 1;
1010                 size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
1011         }
1012
1013         g_assert (!rgctx [rgctx_index]);
1014
1015         oti = class_get_rgctx_template_oti (class_uninstantiated (class),
1016                         method_inst ? method_inst->type_argc : 0, slot);
1017
1018         /*
1019         if (method_inst)
1020                 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
1021         */
1022
1023         info = rgctx [rgctx_index] = instantiate_other_info (domain, &oti, &context);
1024
1025         return info;
1026 }
1027
1028 /*
1029  * mono_class_fill_runtime_generic_context:
1030  * @class_vtable: a vtable
1031  * @slot: a slot index to be instantiated
1032  *
1033  * Instantiates a slot in the RGCTX.
1034  */
1035 gpointer
1036 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot)
1037 {
1038         static gboolean inited = FALSE;
1039         static int num_alloced = 0;
1040
1041         MonoDomain *domain = class_vtable->domain;
1042         MonoRuntimeGenericContext *rgctx;
1043         gpointer info;
1044
1045         mono_domain_lock (domain);
1046
1047         if (!inited) {
1048                 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
1049                 inited = TRUE;
1050         }
1051
1052         rgctx = class_vtable->runtime_generic_context;
1053         if (!rgctx) {
1054                 rgctx = alloc_rgctx_array (domain, 0, FALSE);
1055                 class_vtable->runtime_generic_context = rgctx;
1056                 num_alloced++;
1057         }
1058
1059         info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
1060
1061         mono_domain_unlock (domain);
1062
1063         return info;
1064 }
1065
1066 gpointer
1067 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot)
1068 {
1069         MonoDomain *domain = mrgctx->class_vtable->domain;
1070         gpointer info;
1071
1072         mono_domain_lock (domain);
1073
1074         info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot,
1075                 mrgctx->method_inst);
1076
1077         mono_domain_unlock (domain);
1078
1079         return info;
1080 }
1081
1082 static guint
1083 mrgctx_hash_func (gconstpointer key)
1084 {
1085         const MonoMethodRuntimeGenericContext *mrgctx = key;
1086
1087         return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
1088 }
1089
1090 static gboolean
1091 mrgctx_equal_func (gconstpointer a, gconstpointer b)
1092 {
1093         const MonoMethodRuntimeGenericContext *mrgctx1 = a;
1094         const MonoMethodRuntimeGenericContext *mrgctx2 = b;
1095
1096         return mrgctx1->class_vtable == mrgctx2->class_vtable &&
1097                 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
1098 }
1099
1100 MonoMethodRuntimeGenericContext*
1101 mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
1102 {
1103         MonoDomain *domain = class_vtable->domain;
1104         MonoMethodRuntimeGenericContext *mrgctx;
1105         MonoMethodRuntimeGenericContext key;
1106
1107         g_assert (!class_vtable->klass->generic_container);
1108         g_assert (!method_inst->is_open);
1109
1110         mono_domain_lock (domain);
1111         if (!domain->method_rgctx_hash)
1112                 domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
1113
1114         key.class_vtable = class_vtable;
1115         key.method_inst = method_inst;
1116
1117         mrgctx = g_hash_table_lookup (domain->method_rgctx_hash, &key);
1118
1119         if (!mrgctx) {
1120                 int i;
1121
1122                 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
1123                 mrgctx->class_vtable = class_vtable;
1124                 mrgctx->method_inst = method_inst;
1125
1126                 g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
1127
1128                 /*
1129                 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
1130                 for (i = 0; i < method_inst->type_argc; ++i)
1131                         g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
1132                 g_print (">\n");
1133                 */
1134         }
1135
1136         mono_domain_unlock (domain);
1137
1138         g_assert (mrgctx);
1139
1140         return mrgctx;
1141 }