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