Merge pull request #444 from knocte/xbuild_improvements
[mono.git] / mono / mini / mini-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  * Copyright 2007-2011 Novell, Inc (http://www.novell.com)
8  * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
9  */
10
11 #include <config.h>
12
13 #include <mono/metadata/class.h>
14 #include <mono/utils/mono-counters.h>
15
16 #include "mini.h"
17
18 //#define ALLOW_PARTIAL_SHARING TRUE
19 #define ALLOW_PARTIAL_SHARING FALSE
20  
21 #if 0
22 #define DEBUG(...) __VA_ARGS__
23 #else
24 #define DEBUG(...)
25 #endif
26
27 static void
28 mono_class_unregister_image_generic_subclasses (MonoImage *image, gpointer user_data);
29
30 static int
31 type_check_context_used (MonoType *type, gboolean recursive)
32 {
33         switch (mono_type_get_type (type)) {
34         case MONO_TYPE_VAR:
35                 return MONO_GENERIC_CONTEXT_USED_CLASS;
36         case MONO_TYPE_MVAR:
37                 return MONO_GENERIC_CONTEXT_USED_METHOD;
38         case MONO_TYPE_SZARRAY:
39                 return mono_class_check_context_used (mono_type_get_class (type));
40         case MONO_TYPE_ARRAY:
41                 return mono_class_check_context_used (mono_type_get_array_type (type)->eklass);
42         case MONO_TYPE_CLASS:
43                 if (recursive)
44                         return mono_class_check_context_used (mono_type_get_class (type));
45                 else
46                         return 0;
47         case MONO_TYPE_GENERICINST:
48                 if (recursive) {
49                         MonoGenericClass *gclass = type->data.generic_class;
50
51                         g_assert (gclass->container_class->generic_container);
52                         return mono_generic_context_check_used (&gclass->context);
53                 } else {
54                         return 0;
55                 }
56         default:
57                 return 0;
58         }
59 }
60
61 static int
62 inst_check_context_used (MonoGenericInst *inst)
63 {
64         int context_used = 0;
65         int i;
66
67         if (!inst)
68                 return 0;
69
70         for (i = 0; i < inst->type_argc; ++i)
71                 context_used |= type_check_context_used (inst->type_argv [i], TRUE);
72
73         return context_used;
74 }
75
76 /*
77  * mono_generic_context_check_used:
78  * @context: a generic context
79  *
80  * Checks whether the context uses a type variable.  Returns an int
81  * with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to reflect whether
82  * the context's class instantiation uses type variables.
83  */
84 int
85 mono_generic_context_check_used (MonoGenericContext *context)
86 {
87         int context_used = 0;
88
89         context_used |= inst_check_context_used (context->class_inst);
90         context_used |= inst_check_context_used (context->method_inst);
91
92         return context_used;
93 }
94
95 /*
96  * mono_class_check_context_used:
97  * @class: a class
98  *
99  * Checks whether the class's generic context uses a type variable.
100  * Returns an int with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to
101  * reflect whether the context's class instantiation uses type
102  * variables.
103  */
104 int
105 mono_class_check_context_used (MonoClass *class)
106 {
107         int context_used = 0;
108
109         context_used |= type_check_context_used (&class->this_arg, FALSE);
110         context_used |= type_check_context_used (&class->byval_arg, FALSE);
111
112         if (class->generic_class)
113                 context_used |= mono_generic_context_check_used (&class->generic_class->context);
114         else if (class->generic_container)
115                 context_used |= mono_generic_context_check_used (&class->generic_container->context);
116
117         return context_used;
118 }
119
120 /*
121  * LOCKING: loader lock
122  */
123 static MonoRuntimeGenericContextInfoTemplate*
124 get_info_templates (MonoRuntimeGenericContextTemplate *template, int type_argc)
125 {
126         g_assert (type_argc >= 0);
127         if (type_argc == 0)
128                 return template->infos;
129         return g_slist_nth_data (template->method_templates, type_argc - 1);
130 }
131
132 /*
133  * LOCKING: loader lock
134  */
135 static void
136 set_info_templates (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
137         MonoRuntimeGenericContextInfoTemplate *oti)
138 {
139         g_assert (type_argc >= 0);
140         if (type_argc == 0)
141                 template->infos = oti;
142         else {
143                 int length = g_slist_length (template->method_templates);
144                 GSList *list;
145
146                 /* FIXME: quadratic! */
147                 while (length < type_argc) {
148                         template->method_templates = g_slist_append_image (image, template->method_templates, NULL);
149                         length++;
150                 }
151
152                 list = g_slist_nth (template->method_templates, type_argc - 1);
153                 g_assert (list);
154                 list->data = oti;
155         }
156 }
157
158 /*
159  * LOCKING: loader lock
160  */
161 static int
162 template_get_max_argc (MonoRuntimeGenericContextTemplate *template)
163 {
164         return g_slist_length (template->method_templates);
165 }
166
167 /*
168  * LOCKING: loader lock
169  */
170 static MonoRuntimeGenericContextInfoTemplate*
171 rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate *template, int type_argc, int slot)
172 {
173         int i;
174         MonoRuntimeGenericContextInfoTemplate *oti;
175
176         g_assert (slot >= 0);
177
178         for (oti = get_info_templates (template, type_argc), i = 0; i < slot; oti = oti->next, ++i) {
179                 if (!oti)
180                         return NULL;
181         }
182
183         return oti;
184 }
185
186 /*
187  * LOCKING: loader lock
188  */
189 static int
190 rgctx_template_num_infos (MonoRuntimeGenericContextTemplate *template, int type_argc)
191 {
192         MonoRuntimeGenericContextInfoTemplate *oti;
193         int i;
194
195         for (i = 0, oti = get_info_templates (template, type_argc); oti; ++i, oti = oti->next)
196                 ;
197
198         return i;
199 }
200
201 /* Maps from uninstantiated generic classes to GList's of
202  * uninstantiated generic classes whose parent is the key class or an
203  * instance of the key class.
204  *
205  * LOCKING: loader lock
206  */
207 static GHashTable *generic_subclass_hash;
208
209 /*
210  * LOCKING: templates lock
211  */
212 static void
213 class_set_rgctx_template (MonoClass *class, MonoRuntimeGenericContextTemplate *rgctx_template)
214 {
215         if (!class->image->rgctx_template_hash)
216                 class->image->rgctx_template_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
217
218         g_hash_table_insert (class->image->rgctx_template_hash, class, rgctx_template);
219 }
220
221 /*
222  * LOCKING: loader lock
223  */
224 static MonoRuntimeGenericContextTemplate*
225 class_lookup_rgctx_template (MonoClass *class)
226 {
227         MonoRuntimeGenericContextTemplate *template;
228
229         if (!class->image->rgctx_template_hash)
230                 return NULL;
231
232         template = g_hash_table_lookup (class->image->rgctx_template_hash, class);
233
234         return template;
235 }
236
237 /*
238  * LOCKING: loader lock
239  */
240 static void
241 register_generic_subclass (MonoClass *class)
242 {
243         MonoClass *parent = class->parent;
244         MonoClass *subclass;
245         MonoRuntimeGenericContextTemplate *rgctx_template = class_lookup_rgctx_template (class);
246
247         g_assert (rgctx_template);
248
249         if (parent->generic_class)
250                 parent = parent->generic_class->container_class;
251
252         if (!generic_subclass_hash)
253                 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
254
255         subclass = g_hash_table_lookup (generic_subclass_hash, parent);
256         rgctx_template->next_subclass = subclass;
257         g_hash_table_insert (generic_subclass_hash, parent, class);
258 }
259
260 static void
261 move_subclasses_not_in_image_foreach_func (MonoClass *class, MonoClass *subclass, MonoImage *image)
262 {
263         MonoClass *new_list;
264
265         if (class->image == image) {
266                 /* The parent class itself is in the image, so all the
267                    subclasses must be in the image, too.  If not,
268                    we're removing an image containing a class which
269                    still has a subclass in another image. */
270
271                 while (subclass) {
272                         g_assert (subclass->image == image);
273                         subclass = class_lookup_rgctx_template (subclass)->next_subclass;
274                 }
275
276                 return;
277         }
278
279         new_list = NULL;
280         while (subclass) {
281                 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
282                 MonoClass *next = subclass_template->next_subclass;
283
284                 if (subclass->image != image) {
285                         subclass_template->next_subclass = new_list;
286                         new_list = subclass;
287                 }
288
289                 subclass = next;
290         }
291
292         if (new_list)
293                 g_hash_table_insert (generic_subclass_hash, class, new_list);
294 }
295
296 /*
297  * mono_class_unregister_image_generic_subclasses:
298  * @image: an image
299  *
300  * Removes all classes of the image from the generic subclass hash.
301  * Must be called when an image is unloaded.
302  */
303 static void
304 mono_class_unregister_image_generic_subclasses (MonoImage *image, gpointer user_data)
305 {
306         GHashTable *old_hash;
307
308         //g_print ("unregistering image %s\n", image->name);
309
310         if (!generic_subclass_hash)
311                 return;
312
313         mono_loader_lock ();
314
315         old_hash = generic_subclass_hash;
316         generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
317
318         g_hash_table_foreach (old_hash, (GHFunc)move_subclasses_not_in_image_foreach_func, image);
319
320         mono_loader_unlock ();
321
322         g_hash_table_destroy (old_hash);
323 }
324
325 static MonoRuntimeGenericContextTemplate*
326 alloc_template (MonoClass *class)
327 {
328         static gboolean inited = FALSE;
329         static int num_allocted = 0;
330         static int num_bytes = 0;
331
332         int size = sizeof (MonoRuntimeGenericContextTemplate);
333
334         if (!inited) {
335                 mono_counters_register ("RGCTX template num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_allocted);
336                 mono_counters_register ("RGCTX template bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_bytes);
337                 inited = TRUE;
338         }
339
340         num_allocted++;
341         num_bytes += size;
342
343         return mono_image_alloc0 (class->image, size);
344 }
345
346 static MonoRuntimeGenericContextInfoTemplate*
347 alloc_oti (MonoImage *image)
348 {
349         static gboolean inited = FALSE;
350         static int num_allocted = 0;
351         static int num_bytes = 0;
352
353         int size = sizeof (MonoRuntimeGenericContextInfoTemplate);
354
355         if (!inited) {
356                 mono_counters_register ("RGCTX oti num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_allocted);
357                 mono_counters_register ("RGCTX oti bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_bytes);
358                 inited = TRUE;
359         }
360
361         num_allocted++;
362         num_bytes += size;
363
364         return mono_image_alloc0 (image, size);
365 }
366
367 #define MONO_RGCTX_SLOT_USED_MARKER     ((gpointer)&mono_defaults.object_class->byval_arg)
368
369 /*
370  * Return true if this info type has the notion of identify.
371  *
372  * Some info types expect that each insert results in a new slot been assigned.
373  */
374 static int
375 info_has_identity (MonoRgctxInfoType info_type)
376 {
377         return info_type != MONO_RGCTX_INFO_CAST_CACHE;
378 }
379
380 /*
381  * LOCKING: loader lock
382  */
383 static void
384 rgctx_template_set_slot (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
385         int slot, gpointer data, MonoRgctxInfoType info_type)
386 {
387         static gboolean inited = FALSE;
388         static int num_markers = 0;
389         static int num_data = 0;
390
391         int i;
392         MonoRuntimeGenericContextInfoTemplate *list = get_info_templates (template, type_argc);
393         MonoRuntimeGenericContextInfoTemplate **oti = &list;
394
395         if (!inited) {
396                 mono_counters_register ("RGCTX oti num markers", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_markers);
397                 mono_counters_register ("RGCTX oti num data", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_data);
398                 inited = TRUE;
399         }
400
401         g_assert (slot >= 0);
402         g_assert (data);
403
404         i = 0;
405         while (i <= slot) {
406                 if (i > 0)
407                         oti = &(*oti)->next;
408                 if (!*oti)
409                         *oti = alloc_oti (image);
410                 ++i;
411         }
412
413         g_assert (!(*oti)->data);
414         (*oti)->data = data;
415         (*oti)->info_type = info_type;
416
417         set_info_templates (image, template, type_argc, list);
418
419         if (data == MONO_RGCTX_SLOT_USED_MARKER)
420                 ++num_markers;
421         else
422                 ++num_data;
423 }
424
425 /*
426  * mono_method_get_declaring_generic_method:
427  * @method: an inflated method
428  *
429  * Returns an inflated method's declaring method.
430  */
431 MonoMethod*
432 mono_method_get_declaring_generic_method (MonoMethod *method)
433 {
434         MonoMethodInflated *inflated;
435
436         g_assert (method->is_inflated);
437
438         inflated = (MonoMethodInflated*)method;
439
440         return inflated->declaring;
441 }
442
443 /*
444  * mono_class_get_method_generic:
445  * @klass: a class
446  * @method: a method
447  *
448  * Given a class and a generic method, which has to be of an
449  * instantiation of the same class that klass is an instantiation of,
450  * returns the corresponding method in klass.  Example:
451  *
452  * klass is Gen<string>
453  * method is Gen<object>.work<int>
454  *
455  * returns: Gen<string>.work<int>
456  */
457 MonoMethod*
458 mono_class_get_method_generic (MonoClass *klass, MonoMethod *method)
459 {
460         MonoMethod *declaring, *m;
461         int i;
462
463         if (method->is_inflated)
464                 declaring = mono_method_get_declaring_generic_method (method);
465         else
466                 declaring = method;
467
468         m = NULL;
469         if (klass->generic_class)
470                 m = mono_class_get_inflated_method (klass, declaring);
471
472         if (!m) {
473                 mono_class_setup_methods (klass);
474                 if (klass->exception_type)
475                         return NULL;
476                 for (i = 0; i < klass->method.count; ++i) {
477                         m = klass->methods [i];
478                         if (m == declaring)
479                                 break;
480                         if (m->is_inflated && mono_method_get_declaring_generic_method (m) == declaring)
481                                 break;
482                 }
483                 if (i >= klass->method.count)
484                         return NULL;
485         }
486
487         if (method != declaring) {
488                 MonoGenericContext context;
489
490                 context.class_inst = NULL;
491                 context.method_inst = mono_method_get_context (method)->method_inst;
492
493                 m = mono_class_inflate_generic_method (m, &context);
494         }
495
496         return m;
497 }
498
499 static gpointer
500 inflate_info (MonoRuntimeGenericContextInfoTemplate *oti, MonoGenericContext *context, MonoClass *class, gboolean temporary)
501 {
502         gpointer data = oti->data;
503         MonoRgctxInfoType info_type = oti->info_type;
504         MonoError error;
505
506         g_assert (data);
507
508         if (data == MONO_RGCTX_SLOT_USED_MARKER)
509                 return MONO_RGCTX_SLOT_USED_MARKER;
510
511         switch (info_type)
512         {
513         case MONO_RGCTX_INFO_STATIC_DATA:
514         case MONO_RGCTX_INFO_KLASS:
515         case MONO_RGCTX_INFO_VTABLE:
516         case MONO_RGCTX_INFO_TYPE:
517         case MONO_RGCTX_INFO_REFLECTION_TYPE:
518         case MONO_RGCTX_INFO_CAST_CACHE: {
519                 gpointer result = mono_class_inflate_generic_type_with_mempool (temporary ? NULL : class->image,
520                         data, context, &error);
521                 g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/
522                 return result;
523         }
524
525         case MONO_RGCTX_INFO_METHOD:
526         case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
527         case MONO_RGCTX_INFO_METHOD_RGCTX:
528         case MONO_RGCTX_INFO_METHOD_CONTEXT:
529         case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
530         case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: {
531                 MonoMethod *method = data;
532                 MonoMethod *inflated_method;
533                 MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
534                 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
535
536                 mono_metadata_free_type (inflated_type);
537
538                 mono_class_init (inflated_class);
539
540                 g_assert (!method->wrapper_type);
541
542                 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
543                                 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
544                         inflated_method = mono_method_search_in_array_class (inflated_class,
545                                 method->name, method->signature);
546                 } else {
547                         inflated_method = mono_class_inflate_generic_method (method, context);
548                 }
549                 mono_class_init (inflated_method->klass);
550                 g_assert (inflated_method->klass == inflated_class);
551                 return inflated_method;
552         }
553
554         case MONO_RGCTX_INFO_CLASS_FIELD: {
555                 MonoClassField *field = data;
556                 MonoType *inflated_type = mono_class_inflate_generic_type (&field->parent->byval_arg, context);
557                 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
558                 int i = field - field->parent->fields;
559                 gpointer dummy = NULL;
560
561                 mono_metadata_free_type (inflated_type);
562
563                 mono_class_get_fields (inflated_class, &dummy);
564                 g_assert (inflated_class->fields);
565
566                 return &inflated_class->fields [i];
567         }
568
569         default:
570                 g_assert_not_reached ();
571         }
572         /* Not reached, quiet compiler */
573         return NULL;
574 }
575
576 static void
577 free_inflated_info (MonoRgctxInfoType info_type, gpointer info)
578 {
579         if (!info)
580                 return;
581
582         switch (info_type) {
583         case MONO_RGCTX_INFO_STATIC_DATA:
584         case MONO_RGCTX_INFO_KLASS:
585         case MONO_RGCTX_INFO_VTABLE:
586         case MONO_RGCTX_INFO_TYPE:
587         case MONO_RGCTX_INFO_REFLECTION_TYPE:
588         case MONO_RGCTX_INFO_CAST_CACHE:
589                 mono_metadata_free_type (info);
590                 break;
591         default:
592                 break;
593         }
594 }
595
596 static MonoRuntimeGenericContextInfoTemplate
597 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free);
598  
599 static MonoClass*
600 class_uninstantiated (MonoClass *class)
601 {
602         if (class->generic_class)
603                 return class->generic_class->container_class;
604         return class;
605 }
606
607 static gboolean
608 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars,
609                                                   gboolean allow_partial)
610 {
611         int i;
612
613         for (i = 0; i < inst->type_argc; ++i) {
614                 MonoType *type = inst->type_argv [i];
615
616                 if (MONO_TYPE_IS_REFERENCE (type) || (allow_type_vars && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)))
617                         continue;
618  
619                 /*
620                  * Allow non ref arguments, if there is at least one ref argument
621                  * (partial sharing).
622                  * FIXME: Allow more types
623                  */
624                 if (allow_partial && !type->byref && (((type->type >= MONO_TYPE_BOOLEAN) && (type->type <= MONO_TYPE_R8)) || (type->type == MONO_TYPE_I) || (type->type == MONO_TYPE_U)))
625                         continue;
626
627                 return FALSE;
628         }
629
630         return TRUE;
631 }
632
633 /*
634  * mono_is_partially_sharable_inst:
635  *
636  *   Return TRUE if INST has ref and non-ref type arguments.
637  */
638 gboolean
639 mono_is_partially_sharable_inst (MonoGenericInst *inst)
640 {
641         int i;
642         gboolean has_refs = FALSE, has_non_refs = FALSE;
643
644         for (i = 0; i < inst->type_argc; ++i) {
645                 if (MONO_TYPE_IS_REFERENCE (inst->type_argv [i]) || inst->type_argv [i]->type == MONO_TYPE_VAR || inst->type_argv [i]->type == MONO_TYPE_MVAR)
646                         has_refs = TRUE;
647                 else
648                         has_non_refs = TRUE;
649         }
650
651         return has_refs && has_non_refs;
652 }
653
654 /*
655  * get_shared_class:
656  *
657  *   Return the class used to store information when using generic sharing.
658  * For fully shared classes, it is the generic definition, for partially shared
659  * classes, it is an instance with all ref type arguments replaced by the type parameters
660  * of its generic definition.
661  */
662 static MonoClass*
663 get_shared_class (MonoClass *class)
664 {
665         /*
666          * FIXME: This conflicts with normal instances. Also, some code in this file
667          * like class_get_rgctx_template_oti treats these as normal generic instances
668          * instead of generic classes.
669          */
670         //g_assert_not_reached ();
671
672         if (class->is_inflated) {
673                 MonoGenericContext *context = &class->generic_class->context;
674                 MonoGenericContext *container_context;
675                 MonoGenericContext shared_context;
676                 MonoGenericInst *inst;
677                 MonoType **type_argv;
678                 int i;
679
680                 inst = context->class_inst;
681                 if (mono_is_partially_sharable_inst (inst)) {
682                         container_context = &class->generic_class->container_class->generic_container->context;
683                         type_argv = g_new0 (MonoType*, inst->type_argc);
684                         for (i = 0; i < inst->type_argc; ++i) {
685                                 if (MONO_TYPE_IS_REFERENCE (inst->type_argv [i]) || inst->type_argv [i]->type == MONO_TYPE_VAR || inst->type_argv [i]->type == MONO_TYPE_MVAR)
686                                         type_argv [i] = container_context->class_inst->type_argv [i];
687                                 else
688                                         type_argv [i] = inst->type_argv [i];
689                         }
690
691                         memset (&shared_context, 0, sizeof (MonoGenericContext));
692                         shared_context.class_inst = mono_metadata_get_generic_inst (inst->type_argc, type_argv);
693                         g_free (type_argv);
694
695                         return mono_class_inflate_generic_class (class->generic_class->container_class, &shared_context);
696                 } else if (!generic_inst_is_sharable (inst, TRUE, FALSE)) {
697                         /* Happens for partially shared methods of nono-sharable generic class */
698                         return class;
699                 }
700         }
701
702         return class_uninstantiated (class);
703 }
704
705 /*
706  * mono_class_get_runtime_generic_context_template:
707  * @class: a class
708  *
709  * Looks up or constructs, if necessary, the runtime generic context template for class.
710  * The template is the same for all instantiations of a class.
711  */
712 static MonoRuntimeGenericContextTemplate*
713 mono_class_get_runtime_generic_context_template (MonoClass *class)
714 {
715         MonoRuntimeGenericContextTemplate *parent_template, *template;
716         guint32 i;
717
718         class = get_shared_class (class);
719
720         mono_loader_lock ();
721         template = class_lookup_rgctx_template (class);
722         mono_loader_unlock ();
723
724         if (template)
725                 return template;
726
727         //g_assert (get_shared_class (class) == class);
728
729         template = alloc_template (class);
730
731         mono_loader_lock ();
732
733         if (class->parent) {
734                 guint32 num_entries;
735                 int max_argc, type_argc;
736
737                 parent_template = mono_class_get_runtime_generic_context_template (class->parent);
738                 max_argc = template_get_max_argc (parent_template);
739
740                 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
741                         num_entries = rgctx_template_num_infos (parent_template, type_argc);
742
743                         /* FIXME: quadratic! */
744                         for (i = 0; i < num_entries; ++i) {
745                                 MonoRuntimeGenericContextInfoTemplate oti;
746
747                                 oti = class_get_rgctx_template_oti (class->parent, type_argc, i, FALSE, FALSE, NULL);
748                                 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
749                                         rgctx_template_set_slot (class->image, template, type_argc, i,
750                                                                                          oti.data, oti.info_type);
751                                 }
752                         }
753                 }
754         }
755
756         if (class_lookup_rgctx_template (class)) {
757                 /* some other thread already set the template */
758                 template = class_lookup_rgctx_template (class);
759         } else {
760                 class_set_rgctx_template (class, template);
761
762                 if (class->parent)
763                         register_generic_subclass (class);
764         }
765
766         mono_loader_unlock ();
767
768         return template;
769 }
770
771 /*
772  * class_get_rgctx_template_oti:
773  *
774  *   Return the info template of CLASS numbered TYPE_ARGC/SLOT.
775  * temporary signifies whether the inflated info (oti.data) will be
776  * used temporarily, in which case it might be heap-allocated, or
777  * permanently, in which case it will be mempool-allocated.  If
778  * temporary is set then *do_free will return whether the returned
779  * data must be freed.
780  *
781  * LOCKING: loader lock
782  */
783 static MonoRuntimeGenericContextInfoTemplate
784 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free)
785 {
786         g_assert ((temporary && do_free) || (!temporary && !do_free));
787
788         DEBUG (printf ("get slot: %s %d\n", mono_type_full_name (&class->byval_arg), slot));
789
790         if (class->generic_class && !shared) {
791                 MonoRuntimeGenericContextInfoTemplate oti;
792                 gboolean tmp_do_free;
793
794                 oti = class_get_rgctx_template_oti (class->generic_class->container_class,
795                                                                                         type_argc, slot, TRUE, FALSE, &tmp_do_free);
796                 if (oti.data) {
797                         gpointer info = oti.data;
798                         oti.data = inflate_info (&oti, &class->generic_class->context, class, temporary);
799                         if (tmp_do_free)
800                                 free_inflated_info (oti.info_type, info);
801                 }
802                 if (temporary)
803                         *do_free = TRUE;
804
805                 return oti;
806         } else {
807                 MonoRuntimeGenericContextTemplate *template;
808                 MonoRuntimeGenericContextInfoTemplate *oti;
809
810                 template = mono_class_get_runtime_generic_context_template (class);
811                 oti = rgctx_template_get_other_slot (template, type_argc, slot);
812                 g_assert (oti);
813
814                 if (temporary)
815                         *do_free = FALSE;
816
817                 return *oti;
818         }
819 }
820
821 static gpointer
822 class_type_info (MonoDomain *domain, MonoClass *class, MonoRgctxInfoType info_type)
823 {
824         switch (info_type) {
825         case MONO_RGCTX_INFO_STATIC_DATA: {
826                 MonoVTable *vtable = mono_class_vtable (domain, class);
827                 if (!vtable)
828                         mono_raise_exception (mono_class_get_exception_for_failure (class));
829                 return mono_vtable_get_static_field_data (vtable);
830         }
831         case MONO_RGCTX_INFO_KLASS:
832                 return class;
833         case MONO_RGCTX_INFO_VTABLE: {
834                 MonoVTable *vtable = mono_class_vtable (domain, class);
835                 if (!vtable)
836                         mono_raise_exception (mono_class_get_exception_for_failure (class));
837                 return vtable;
838         }
839         case MONO_RGCTX_INFO_CAST_CACHE: {
840                 /*First slot is the cache itself, the second the vtable.*/
841                 gpointer **cache_data = mono_domain_alloc0 (domain, sizeof (gpointer) * 2);
842                 cache_data [1] = (gpointer)class;
843                 return cache_data;
844         }
845         default:
846                 g_assert_not_reached ();
847         }
848         /* Not reached */
849         return NULL;
850 }
851
852 static gpointer
853 instantiate_info (MonoDomain *domain, MonoRuntimeGenericContextInfoTemplate *oti,
854         MonoGenericContext *context, MonoClass *class)
855 {
856         gpointer data;
857         gboolean temporary;
858
859         if (!oti->data)
860                 return NULL;
861
862         switch (oti->info_type) {
863         case MONO_RGCTX_INFO_STATIC_DATA:
864         case MONO_RGCTX_INFO_KLASS:
865         case MONO_RGCTX_INFO_VTABLE:
866         case MONO_RGCTX_INFO_CAST_CACHE:
867                 temporary = TRUE;
868                 break;
869         default:
870                 temporary = FALSE;
871         }
872
873         data = inflate_info (oti, context, class, temporary);
874
875         switch (oti->info_type) {
876         case MONO_RGCTX_INFO_STATIC_DATA:
877         case MONO_RGCTX_INFO_KLASS:
878         case MONO_RGCTX_INFO_VTABLE:
879         case MONO_RGCTX_INFO_CAST_CACHE: {
880                 MonoClass *arg_class = mono_class_from_mono_type (data);
881
882                 free_inflated_info (oti->info_type, data);
883                 g_assert (arg_class);
884
885                 /* The class might be used as an argument to
886                    mono_value_copy(), which requires that its GC
887                    descriptor has been computed. */
888                 if (oti->info_type == MONO_RGCTX_INFO_KLASS)
889                         mono_class_compute_gc_descriptor (arg_class);
890
891                 return class_type_info (domain, arg_class, oti->info_type);
892         }
893         case MONO_RGCTX_INFO_TYPE:
894                 return data;
895         case MONO_RGCTX_INFO_REFLECTION_TYPE:
896                 return mono_type_get_object (domain, data);
897         case MONO_RGCTX_INFO_METHOD:
898                 return data;
899         case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
900                 /*
901                  * We can't create a jump trampoline here, as it cannot be patched.
902                  */
903                 return mono_compile_method (data);
904         case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
905                 return mono_compile_method (mono_marshal_get_remoting_invoke_with_check (data));
906         case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
907                 return mono_domain_alloc0 (domain, sizeof (gpointer));
908         case MONO_RGCTX_INFO_CLASS_FIELD:
909                 return data;
910         case MONO_RGCTX_INFO_METHOD_RGCTX: {
911                 MonoMethodInflated *method = data;
912                 MonoVTable *vtable;
913
914                 g_assert (method->method.method.is_inflated);
915                 g_assert (method->context.method_inst);
916
917                 vtable = mono_class_vtable (domain, method->method.method.klass);
918                 if (!vtable)
919                         mono_raise_exception (mono_class_get_exception_for_failure (method->method.method.klass));
920
921                 return mono_method_lookup_rgctx (vtable, method->context.method_inst);
922         }
923         case MONO_RGCTX_INFO_METHOD_CONTEXT: {
924                 MonoMethodInflated *method = data;
925
926                 g_assert (method->method.method.is_inflated);
927                 g_assert (method->context.method_inst);
928
929                 return method->context.method_inst;
930         }
931         default:
932                 g_assert_not_reached ();
933         }
934         /* Not reached */
935         return NULL;
936 }
937
938 /*
939  * LOCKING: loader lock
940  */
941 static void
942 fill_in_rgctx_template_slot (MonoClass *class, int type_argc, int index, gpointer data, MonoRgctxInfoType info_type)
943 {
944         MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
945         MonoClass *subclass;
946
947         rgctx_template_set_slot (class->image, template, type_argc, index, data, info_type);
948
949         /* Recurse for all subclasses */
950         if (generic_subclass_hash)
951                 subclass = g_hash_table_lookup (generic_subclass_hash, class);
952         else
953                 subclass = NULL;
954
955         while (subclass) {
956                 MonoRuntimeGenericContextInfoTemplate subclass_oti;
957                 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
958
959                 g_assert (subclass_template);
960
961                 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, FALSE, NULL);
962                 g_assert (subclass_oti.data);
963
964                 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
965
966                 subclass = subclass_template->next_subclass;
967         }
968 }
969
970 G_GNUC_UNUSED static const char*
971 info_type_to_str (MonoRgctxInfoType type)
972 {
973         switch (type) {
974         case MONO_RGCTX_INFO_STATIC_DATA: return "STATIC_DATA";
975         case MONO_RGCTX_INFO_KLASS: return "KLASS";
976         case MONO_RGCTX_INFO_VTABLE: return "VTABLE";
977         case MONO_RGCTX_INFO_TYPE: return "TYPE";
978         case MONO_RGCTX_INFO_REFLECTION_TYPE: return "REFLECTION_TYPE";
979         case MONO_RGCTX_INFO_METHOD: return "METHOD";
980         case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: return "GENERIC_METHOD_CODE";
981         case MONO_RGCTX_INFO_CLASS_FIELD: return "CLASS_FIELD";
982         case MONO_RGCTX_INFO_METHOD_RGCTX: return "METHOD_RGCTX";
983         case MONO_RGCTX_INFO_METHOD_CONTEXT: return "METHOD_CONTEXT";
984         case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: return "REMOTING_INVOKE_WITH_CHECK";
985         case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: return "METHOD_DELEGATE_CODE";
986         case MONO_RGCTX_INFO_CAST_CACHE: return "CAST_CACHE";
987         default:
988                 return "<>";
989         }
990 }
991
992 G_GNUC_UNUSED static char*
993 rgctx_info_to_str (MonoRgctxInfoType info_type, gpointer data)
994 {
995         switch (info_type) {
996         case MONO_RGCTX_INFO_VTABLE:
997                 return mono_type_full_name ((MonoType*)data);
998         default:
999                 return g_strdup_printf ("<%p>", data);
1000         }
1001 }
1002
1003 /*
1004  * LOCKING: loader lock
1005  */
1006 static int
1007 register_info (MonoClass *class, int type_argc, gpointer data, MonoRgctxInfoType info_type)
1008 {
1009         int i;
1010         MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
1011         MonoClass *parent;
1012         MonoRuntimeGenericContextInfoTemplate *oti;
1013
1014         for (i = 0, oti = get_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
1015                 if (!oti->data)
1016                         break;
1017         }
1018
1019         DEBUG (printf ("set slot %s, infos [%d] = %s, %s\n", mono_type_get_full_name (class), i, info_type_to_str (info_type), rgctx_info_to_str (info_type, data)));
1020
1021         /* Mark the slot as used in all parent classes (until we find
1022            a parent class which already has it marked used). */
1023         parent = class->parent;
1024         while (parent != NULL) {
1025                 MonoRuntimeGenericContextTemplate *parent_template;
1026                 MonoRuntimeGenericContextInfoTemplate *oti;
1027
1028                 if (parent->generic_class)
1029                         parent = parent->generic_class->container_class;
1030
1031                 parent_template = mono_class_get_runtime_generic_context_template (parent);
1032                 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
1033
1034                 if (oti && oti->data)
1035                         break;
1036
1037                 rgctx_template_set_slot (parent->image, parent_template, type_argc, i,
1038                                                                  MONO_RGCTX_SLOT_USED_MARKER, 0);
1039
1040                 parent = parent->parent;
1041         }
1042
1043         /* Fill in the slot in this class and in all subclasses
1044            recursively. */
1045         fill_in_rgctx_template_slot (class, type_argc, i, data, info_type);
1046
1047         return i;
1048 }
1049
1050 static gboolean
1051 info_equal (gpointer data1, gpointer data2, MonoRgctxInfoType info_type)
1052 {
1053         switch (info_type) {
1054         case MONO_RGCTX_INFO_STATIC_DATA:
1055         case MONO_RGCTX_INFO_KLASS:
1056         case MONO_RGCTX_INFO_VTABLE:
1057         case MONO_RGCTX_INFO_TYPE:
1058         case MONO_RGCTX_INFO_REFLECTION_TYPE:
1059         case MONO_RGCTX_INFO_CAST_CACHE:
1060                 return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
1061         case MONO_RGCTX_INFO_METHOD:
1062         case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
1063         case MONO_RGCTX_INFO_CLASS_FIELD:
1064         case MONO_RGCTX_INFO_METHOD_RGCTX:
1065         case MONO_RGCTX_INFO_METHOD_CONTEXT:
1066         case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
1067         case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
1068                 return data1 == data2;
1069         default:
1070                 g_assert_not_reached ();
1071         }
1072         /* never reached */
1073         return FALSE;
1074 }
1075
1076 static int
1077 lookup_or_register_info (MonoClass *class, int type_argc, gpointer data, MonoRgctxInfoType info_type,
1078         MonoGenericContext *generic_context)
1079 {
1080         static gboolean inited = FALSE;
1081         static int max_slot = 0;
1082
1083         MonoRuntimeGenericContextTemplate *rgctx_template =
1084                 mono_class_get_runtime_generic_context_template (class);
1085         MonoRuntimeGenericContextInfoTemplate *oti_list, *oti;
1086         int i;
1087
1088         class = get_shared_class (class);
1089
1090         mono_loader_lock ();
1091
1092         if (info_has_identity (info_type)) {
1093                 oti_list = get_info_templates (rgctx_template, type_argc);
1094
1095                 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
1096                         gpointer inflated_data;
1097
1098                         if (oti->info_type != info_type || !oti->data)
1099                                 continue;
1100
1101                         inflated_data = inflate_info (oti, generic_context, class, TRUE);
1102
1103                         if (info_equal (data, inflated_data, info_type)) {
1104                                 free_inflated_info (info_type, inflated_data);
1105                                 mono_loader_unlock ();
1106                                 return i;
1107                         }
1108                         free_inflated_info (info_type, inflated_data);
1109                 }
1110         }
1111
1112         /* We haven't found the info */
1113         i = register_info (class, type_argc, data, info_type);
1114
1115         mono_loader_unlock ();
1116
1117         if (!inited) {
1118                 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
1119                 inited = TRUE;
1120         }
1121         if (i > max_slot)
1122                 max_slot = i;
1123
1124         return i;
1125 }
1126
1127 /*
1128  * mono_method_lookup_or_register_info:
1129  * @method: a method
1130  * @in_mrgctx: whether to put the data into the MRGCTX
1131  * @data: the info data
1132  * @info_type: the type of info to register about data
1133  * @generic_context: a generic context
1134  *
1135  * Looks up and, if necessary, adds information about data/info_type in
1136  * method's or method's class runtime generic context.  Returns the
1137  * encoded slot number.
1138  */
1139 guint32
1140 mono_method_lookup_or_register_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
1141         MonoRgctxInfoType info_type, MonoGenericContext *generic_context)
1142 {
1143         MonoClass *class = method->klass;
1144         int type_argc, index;
1145
1146         if (in_mrgctx) {
1147                 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
1148
1149                 g_assert (method->is_inflated && method_inst);
1150                 type_argc = method_inst->type_argc;
1151                 g_assert (type_argc > 0);
1152         } else {
1153                 type_argc = 0;
1154         }
1155
1156         index = lookup_or_register_info (class, type_argc, data, info_type, generic_context);
1157
1158         //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
1159
1160         if (in_mrgctx)
1161                 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
1162         else
1163                 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
1164 }
1165
1166 /*
1167  * mono_class_rgctx_get_array_size:
1168  * @n: The number of the array
1169  * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
1170  *
1171  * Returns the number of slots in the n'th array of a (M)RGCTX.  That
1172  * number includes the slot for linking and - for MRGCTXs - the two
1173  * slots in the first array for additional information.
1174  */
1175 int
1176 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
1177 {
1178         g_assert (n >= 0 && n < 30);
1179
1180         if (mrgctx)
1181                 return 6 << n;
1182         else
1183                 return 4 << n;
1184 }
1185
1186 /*
1187  * LOCKING: domain lock
1188  */
1189 static gpointer*
1190 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
1191 {
1192         static gboolean inited = FALSE;
1193         static int rgctx_num_alloced = 0;
1194         static int rgctx_bytes_alloced = 0;
1195         static int mrgctx_num_alloced = 0;
1196         static int mrgctx_bytes_alloced = 0;
1197
1198         int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
1199         gpointer array = mono_domain_alloc0 (domain, size);
1200
1201         if (!inited) {
1202                 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
1203                 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
1204                 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
1205                 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
1206                 inited = TRUE;
1207         }
1208
1209         if (is_mrgctx) {
1210                 mrgctx_num_alloced++;
1211                 mrgctx_bytes_alloced += size;
1212         } else {
1213                 rgctx_num_alloced++;
1214                 rgctx_bytes_alloced += size;
1215         }
1216
1217         return array;
1218 }
1219
1220 static gpointer
1221 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
1222                 MonoGenericInst *method_inst)
1223 {
1224         gpointer info;
1225         int i, first_slot, size;
1226         MonoDomain *domain = class_vtable->domain;
1227         MonoClass *class = class_vtable->klass;
1228         MonoGenericContext *class_context = class->generic_class ? &class->generic_class->context : NULL;
1229         MonoRuntimeGenericContextInfoTemplate oti;
1230         MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
1231         int rgctx_index;
1232         gboolean do_free;
1233
1234         g_assert (rgctx);
1235
1236         mono_domain_lock (domain);
1237
1238         /* First check whether that slot isn't already instantiated.
1239            This might happen because lookup doesn't lock.  Allocate
1240            arrays on the way. */
1241         first_slot = 0;
1242         size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
1243         if (method_inst)
1244                 size -= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
1245         for (i = 0; ; ++i) {
1246                 int offset;
1247
1248                 if (method_inst && i == 0)
1249                         offset = MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
1250                 else
1251                         offset = 0;
1252
1253                 if (slot < first_slot + size - 1) {
1254                         rgctx_index = slot - first_slot + 1 + offset;
1255                         info = rgctx [rgctx_index];
1256                         if (info) {
1257                                 mono_domain_unlock (domain);
1258                                 return info;
1259                         }
1260                         break;
1261                 }
1262                 if (!rgctx [offset + 0])
1263                         rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
1264                 rgctx = rgctx [offset + 0];
1265                 first_slot += size - 1;
1266                 size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
1267         }
1268
1269         g_assert (!rgctx [rgctx_index]);
1270
1271         mono_domain_unlock (domain);
1272
1273         oti = class_get_rgctx_template_oti (get_shared_class (class),
1274                                                                                 method_inst ? method_inst->type_argc : 0, slot, TRUE, TRUE, &do_free);
1275         /* This might take the loader lock */
1276         info = instantiate_info (domain, &oti, &context, class);
1277
1278         /*
1279         if (method_inst)
1280                 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
1281         */
1282
1283         /*FIXME We should use CAS here, no need to take a lock.*/
1284         mono_domain_lock (domain);
1285
1286         /* Check whether the slot hasn't been instantiated in the
1287            meantime. */
1288         if (rgctx [rgctx_index])
1289                 info = rgctx [rgctx_index];
1290         else
1291                 rgctx [rgctx_index] = info;
1292
1293         mono_domain_unlock (domain);
1294
1295         if (do_free)
1296                 free_inflated_info (oti.info_type, oti.data);
1297
1298         return info;
1299 }
1300
1301 /*
1302  * mono_class_fill_runtime_generic_context:
1303  * @class_vtable: a vtable
1304  * @slot: a slot index to be instantiated
1305  *
1306  * Instantiates a slot in the RGCTX, returning its value.
1307  */
1308 gpointer
1309 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot)
1310 {
1311         static gboolean inited = FALSE;
1312         static int num_alloced = 0;
1313
1314         MonoDomain *domain = class_vtable->domain;
1315         MonoRuntimeGenericContext *rgctx;
1316         gpointer info;
1317
1318         mono_domain_lock (domain);
1319
1320         if (!inited) {
1321                 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
1322                 inited = TRUE;
1323         }
1324
1325         rgctx = class_vtable->runtime_generic_context;
1326         if (!rgctx) {
1327                 rgctx = alloc_rgctx_array (domain, 0, FALSE);
1328                 class_vtable->runtime_generic_context = rgctx;
1329                 num_alloced++;
1330         }
1331
1332         mono_domain_unlock (domain);
1333
1334         info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
1335
1336         DEBUG (printf ("get rgctx slot: %s %d -> %p\n", mono_type_full_name (&class_vtable->klass->byval_arg), slot, info));
1337
1338         return info;
1339 }
1340
1341 /*
1342  * mono_method_fill_runtime_generic_context:
1343  * @mrgctx: an MRGCTX
1344  * @slot: a slot index to be instantiated
1345  *
1346  * Instantiates a slot in the MRGCTX.
1347  */
1348 gpointer
1349 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot)
1350 {
1351         gpointer info;
1352
1353         info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot,
1354                 mrgctx->method_inst);
1355
1356         return info;
1357 }
1358
1359 static guint
1360 mrgctx_hash_func (gconstpointer key)
1361 {
1362         const MonoMethodRuntimeGenericContext *mrgctx = key;
1363
1364         return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
1365 }
1366
1367 static gboolean
1368 mrgctx_equal_func (gconstpointer a, gconstpointer b)
1369 {
1370         const MonoMethodRuntimeGenericContext *mrgctx1 = a;
1371         const MonoMethodRuntimeGenericContext *mrgctx2 = b;
1372
1373         return mrgctx1->class_vtable == mrgctx2->class_vtable &&
1374                 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
1375 }
1376
1377 /*
1378  * mono_method_lookup_rgctx:
1379  * @class_vtable: a vtable
1380  * @method_inst: the method inst of a generic method
1381  *
1382  * Returns the MRGCTX for the generic method(s) with the given
1383  * method_inst of the given class_vtable.
1384  *
1385  * LOCKING: Take the domain lock.
1386  */
1387 MonoMethodRuntimeGenericContext*
1388 mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
1389 {
1390         MonoDomain *domain = class_vtable->domain;
1391         MonoMethodRuntimeGenericContext *mrgctx;
1392         MonoMethodRuntimeGenericContext key;
1393
1394         g_assert (!class_vtable->klass->generic_container);
1395         g_assert (!method_inst->is_open);
1396
1397         mono_domain_lock (domain);
1398         if (!domain->method_rgctx_hash)
1399                 domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
1400
1401         key.class_vtable = class_vtable;
1402         key.method_inst = method_inst;
1403
1404         mrgctx = g_hash_table_lookup (domain->method_rgctx_hash, &key);
1405
1406         if (!mrgctx) {
1407                 //int i;
1408
1409                 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
1410                 mrgctx->class_vtable = class_vtable;
1411                 mrgctx->method_inst = method_inst;
1412
1413                 g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
1414
1415                 /*
1416                 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
1417                 for (i = 0; i < method_inst->type_argc; ++i)
1418                         g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
1419                 g_print (">\n");
1420                 */
1421         }
1422
1423         mono_domain_unlock (domain);
1424
1425         g_assert (mrgctx);
1426
1427         return mrgctx;
1428 }
1429
1430 /*
1431  * mono_generic_context_is_sharable_full:
1432  * @context: a generic context
1433  *
1434  * Returns whether the generic context is sharable.  A generic context
1435  * is sharable iff all of its type arguments are reference type, or some of them have a
1436  * reference type, and ALLOW_PARTIAL is TRUE.
1437  */
1438 gboolean
1439 mono_generic_context_is_sharable_full (MonoGenericContext *context,
1440                                                                            gboolean allow_type_vars,
1441                                                                            gboolean allow_partial)
1442 {
1443         g_assert (context->class_inst || context->method_inst);
1444
1445         if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars, allow_partial))
1446                 return FALSE;
1447
1448         if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars, allow_partial))
1449                 return FALSE;
1450
1451         return TRUE;
1452 }
1453
1454 gboolean
1455 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
1456 {
1457         return mono_generic_context_is_sharable_full (context, allow_type_vars, ALLOW_PARTIAL_SHARING);
1458 }
1459
1460 /*
1461  * mono_method_is_generic_impl:
1462  * @method: a method
1463  *
1464  * Returns whether the method is either generic or part of a generic
1465  * class.
1466  */
1467 gboolean
1468 mono_method_is_generic_impl (MonoMethod *method)
1469 {
1470         if (method->is_inflated) {
1471                 g_assert (method->wrapper_type == MONO_WRAPPER_NONE);
1472                 return TRUE;
1473         }
1474         /* We don't treat wrappers as generic code, i.e., we never
1475            apply generic sharing to them.  This is especially
1476            important for static rgctx invoke wrappers, which only work
1477            if not compiled with sharing. */
1478         if (method->wrapper_type != MONO_WRAPPER_NONE)
1479                 return FALSE;
1480         if (method->klass->generic_container)
1481                 return TRUE;
1482         return FALSE;
1483 }
1484
1485 static gboolean
1486 has_constraints (MonoGenericContainer *container)
1487 {
1488         //int i;
1489
1490         return FALSE;
1491         /*
1492         g_assert (container->type_argc > 0);
1493         g_assert (container->type_params);
1494
1495         for (i = 0; i < container->type_argc; ++i)
1496                 if (container->type_params [i].constraints)
1497                         return TRUE;
1498         return FALSE;
1499         */
1500 }
1501
1502 /*
1503  * mono_method_is_generic_sharable_impl_full:
1504  * @method: a method
1505  * @allow_type_vars: whether to regard type variables as reference types
1506  * @alloc_partial: whether to allow partial sharing
1507  *
1508  * Returns TRUE iff the method is inflated or part of an inflated
1509  * class, its context is sharable and it has no constraints on its
1510  * type parameters.  Otherwise returns FALSE.
1511  */
1512 gboolean
1513 mono_method_is_generic_sharable_impl_full (MonoMethod *method, gboolean allow_type_vars,
1514                                                                                    gboolean allow_partial)
1515 {
1516         if (!mono_method_is_generic_impl (method))
1517                 return FALSE;
1518
1519         if (method->is_inflated) {
1520                 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
1521                 MonoGenericContext *context = &inflated->context;
1522
1523                 if (!mono_generic_context_is_sharable_full (context, allow_type_vars, allow_partial))
1524                         return FALSE;
1525
1526                 g_assert (inflated->declaring);
1527
1528                 if (inflated->declaring->is_generic) {
1529                         if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
1530                                 return FALSE;
1531                 }
1532         }
1533
1534         if (method->klass->generic_class) {
1535                 if (!mono_generic_context_is_sharable_full (&method->klass->generic_class->context, allow_type_vars, allow_partial))
1536                         return FALSE;
1537
1538                 g_assert (method->klass->generic_class->container_class &&
1539                                 method->klass->generic_class->container_class->generic_container);
1540
1541                 if (has_constraints (method->klass->generic_class->container_class->generic_container))
1542                         return FALSE;
1543         }
1544
1545         if (method->klass->generic_container && !allow_type_vars)
1546                 return FALSE;
1547
1548         return TRUE;
1549 }
1550
1551 gboolean
1552 mono_method_is_generic_sharable_impl (MonoMethod *method, gboolean allow_type_vars)
1553 {
1554         return mono_method_is_generic_sharable_impl_full (method, allow_type_vars, ALLOW_PARTIAL_SHARING);
1555 }
1556
1557 gboolean
1558 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
1559 {
1560         if (!mono_class_generic_sharing_enabled (method->klass))
1561                 return FALSE;
1562
1563         if (!mono_method_is_generic_sharable_impl (method, allow_type_vars))
1564                 return FALSE;
1565
1566         if (method->is_inflated && mono_method_get_context (method)->method_inst)
1567                 return TRUE;
1568
1569         return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
1570                         method->klass->valuetype) &&
1571                 (method->klass->generic_class || method->klass->generic_container);
1572 }
1573
1574 static MonoGenericInst*
1575 get_object_generic_inst (int type_argc)
1576 {
1577         MonoType **type_argv;
1578         int i;
1579
1580         type_argv = alloca (sizeof (MonoType*) * type_argc);
1581
1582         for (i = 0; i < type_argc; ++i)
1583                 type_argv [i] = &mono_defaults.object_class->byval_arg;
1584
1585         return mono_metadata_get_generic_inst (type_argc, type_argv);
1586 }
1587
1588 /*
1589  * mono_method_construct_object_context:
1590  * @method: a method
1591  *
1592  * Returns a generic context for method with all type variables for
1593  * class and method instantiated with Object.
1594  */
1595 MonoGenericContext
1596 mono_method_construct_object_context (MonoMethod *method)
1597 {
1598         MonoGenericContext object_context;
1599
1600         g_assert (!method->klass->generic_class);
1601         if (method->klass->generic_container) {
1602                 int type_argc = method->klass->generic_container->type_argc;
1603
1604                 object_context.class_inst = get_object_generic_inst (type_argc);
1605         } else {
1606                 object_context.class_inst = NULL;
1607         }
1608
1609         if (mono_method_get_context_general (method, TRUE)->method_inst) {
1610                 int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
1611
1612                 object_context.method_inst = get_object_generic_inst (type_argc);
1613         } else {
1614                 object_context.method_inst = NULL;
1615         }
1616
1617         g_assert (object_context.class_inst || object_context.method_inst);
1618
1619         return object_context;
1620 }
1621
1622 static gboolean gshared_supported;
1623
1624 void
1625 mono_set_generic_sharing_supported (gboolean supported)
1626 {
1627         gshared_supported = supported;
1628 }
1629
1630 /*
1631  * mono_class_generic_sharing_enabled:
1632  * @class: a class
1633  *
1634  * Returns whether generic sharing is enabled for class.
1635  *
1636  * This is a stop-gap measure to slowly introduce generic sharing
1637  * until we have all the issues sorted out, at which time this
1638  * function will disappear and generic sharing will always be enabled.
1639  */
1640 gboolean
1641 mono_class_generic_sharing_enabled (MonoClass *class)
1642 {
1643         static int generic_sharing = MONO_GENERIC_SHARING_NONE;
1644         static gboolean inited = FALSE;
1645
1646         if (!inited) {
1647                 const char *option;
1648
1649                 if (gshared_supported)
1650                         generic_sharing = MONO_GENERIC_SHARING_ALL;
1651                 else
1652                         generic_sharing = MONO_GENERIC_SHARING_NONE;
1653
1654                 if ((option = g_getenv ("MONO_GENERIC_SHARING"))) {
1655                         if (strcmp (option, "corlib") == 0)
1656                                 generic_sharing = MONO_GENERIC_SHARING_CORLIB;
1657                         else if (strcmp (option, "collections") == 0)
1658                                 generic_sharing = MONO_GENERIC_SHARING_COLLECTIONS;
1659                         else if (strcmp (option, "all") == 0)
1660                                 generic_sharing = MONO_GENERIC_SHARING_ALL;
1661                         else if (strcmp (option, "none") == 0)
1662                                 generic_sharing = MONO_GENERIC_SHARING_NONE;
1663                         else
1664                                 g_warning ("Unknown generic sharing option `%s'.", option);
1665                 }
1666
1667                 if (!gshared_supported)
1668                         generic_sharing = MONO_GENERIC_SHARING_NONE;
1669
1670                 inited = TRUE;
1671         }
1672
1673         switch (generic_sharing) {
1674         case MONO_GENERIC_SHARING_NONE:
1675                 return FALSE;
1676         case MONO_GENERIC_SHARING_ALL:
1677                 return TRUE;
1678         case MONO_GENERIC_SHARING_CORLIB :
1679                 return class->image == mono_defaults.corlib;
1680         case MONO_GENERIC_SHARING_COLLECTIONS:
1681                 if (class->image != mono_defaults.corlib)
1682                         return FALSE;
1683                 while (class->nested_in)
1684                         class = class->nested_in;
1685                 return g_str_has_prefix (class->name_space, "System.Collections.Generic");
1686         default:
1687                 g_assert_not_reached ();
1688         }
1689         return FALSE;
1690 }
1691
1692 /*
1693  * mono_get_generic_context_from_code:
1694  *
1695  *   Return the runtime generic context belonging to the method whose native code
1696  * contains CODE.
1697  */
1698 MonoGenericSharingContext*
1699 mono_get_generic_context_from_code (guint8 *code)
1700 {
1701         MonoJitInfo *jit_info = mini_jit_info_table_find (mono_domain_get (), (char*)code, NULL);
1702
1703         g_assert (jit_info);
1704
1705         return mono_jit_info_get_generic_sharing_context (jit_info);
1706 }
1707
1708 MonoGenericContext*
1709 mini_method_get_context (MonoMethod *method)
1710 {
1711         return mono_method_get_context_general (method, TRUE);
1712 }
1713
1714 /*
1715  * mono_method_check_context_used:
1716  * @method: a method
1717  *
1718  * Checks whether the method's generic context uses a type variable.
1719  * Returns an int with the bits MONO_GENERIC_CONTEXT_USED_CLASS and
1720  * MONO_GENERIC_CONTEXT_USED_METHOD set to reflect whether the
1721  * context's class or method instantiation uses type variables.
1722  */
1723 int
1724 mono_method_check_context_used (MonoMethod *method)
1725 {
1726         MonoGenericContext *method_context = mini_method_get_context (method);
1727         int context_used = 0;
1728
1729         if (!method_context) {
1730                 /* It might be a method of an array of an open generic type */
1731                 if (method->klass->rank)
1732                         context_used = mono_class_check_context_used (method->klass);
1733         } else {
1734                 context_used = mono_generic_context_check_used (method_context);
1735                 context_used |= mono_class_check_context_used (method->klass);
1736         }
1737
1738         return context_used;
1739 }
1740
1741 static gboolean
1742 generic_inst_equal (MonoGenericInst *inst1, MonoGenericInst *inst2)
1743 {
1744         int i;
1745
1746         if (!inst1) {
1747                 g_assert (!inst2);
1748                 return TRUE;
1749         }
1750
1751         g_assert (inst2);
1752
1753         if (inst1->type_argc != inst2->type_argc)
1754                 return FALSE;
1755
1756         for (i = 0; i < inst1->type_argc; ++i)
1757                 if (!mono_metadata_type_equal (inst1->type_argv [i], inst2->type_argv [i]))
1758                         return FALSE;
1759
1760         return TRUE;
1761 }
1762
1763 /*
1764  * mono_generic_context_equal_deep:
1765  * @context1: a generic context
1766  * @context2: a generic context
1767  *
1768  * Returns whether context1's type arguments are equal to context2's
1769  * type arguments.
1770  */
1771 gboolean
1772 mono_generic_context_equal_deep (MonoGenericContext *context1, MonoGenericContext *context2)
1773 {
1774         return generic_inst_equal (context1->class_inst, context2->class_inst) &&
1775                 generic_inst_equal (context1->method_inst, context2->method_inst);
1776 }
1777
1778 /*
1779  * mini_class_get_container_class:
1780  * @class: a generic class
1781  *
1782  * Returns the class's container class, which is the class itself if
1783  * it doesn't have generic_class set.
1784  */
1785 MonoClass*
1786 mini_class_get_container_class (MonoClass *class)
1787 {
1788         if (class->generic_class)
1789                 return class->generic_class->container_class;
1790
1791         g_assert (class->generic_container);
1792         return class;
1793 }
1794
1795 /*
1796  * mini_class_get_context:
1797  * @class: a generic class
1798  *
1799  * Returns the class's generic context.
1800  */
1801 MonoGenericContext*
1802 mini_class_get_context (MonoClass *class)
1803 {
1804         if (class->generic_class)
1805                 return &class->generic_class->context;
1806
1807         g_assert (class->generic_container);
1808         return &class->generic_container->context;
1809 }
1810
1811 /*
1812  * mini_get_basic_type_from_generic:
1813  * @gsctx: a generic sharing context
1814  * @type: a type
1815  *
1816  * Returns a closed type corresponding to the possibly open type
1817  * passed to it.
1818  */
1819 MonoType*
1820 mini_get_basic_type_from_generic (MonoGenericSharingContext *gsctx, MonoType *type)
1821 {
1822         if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR))
1823                 g_assert (gsctx);
1824
1825         return mono_type_get_basic_type_from_generic (type);
1826 }
1827
1828 /*
1829  * mini_type_get_underlying_type:
1830  *
1831  *   Return the underlying type of TYPE, taking into account enums, byref and generic
1832  * sharing.
1833  */
1834 MonoType*
1835 mini_type_get_underlying_type (MonoGenericSharingContext *gsctx, MonoType *type)
1836 {
1837         if (type->byref)
1838                 return &mono_defaults.int_class->byval_arg;
1839         return mono_type_get_basic_type_from_generic (mono_type_get_underlying_type (type));
1840 }
1841
1842 /*
1843  * mini_type_stack_size:
1844  * @gsctx: a generic sharing context
1845  * @t: a type
1846  * @align: Pointer to an int for returning the alignment
1847  *
1848  * Returns the type's stack size and the alignment in *align.  The
1849  * type is allowed to be open.
1850  */
1851 int
1852 mini_type_stack_size (MonoGenericSharingContext *gsctx, MonoType *t, int *align)
1853 {
1854         gboolean allow_open = TRUE;
1855
1856         // FIXME: Some callers might not pass in a gsctx
1857         //allow_open = gsctx != NULL;
1858         return mono_type_stack_size_internal (t, align, allow_open);
1859 }
1860
1861 /*
1862  * mini_type_stack_size_full:
1863  *
1864  *   Same as mini_type_stack_size, but handle pinvoke data types as well.
1865  */
1866 int
1867 mini_type_stack_size_full (MonoGenericSharingContext *gsctx, MonoType *t, guint32 *align, gboolean pinvoke)
1868 {
1869         int size;
1870
1871         if (pinvoke) {
1872                 size = mono_type_native_stack_size (t, align);
1873         } else {
1874                 int ialign;
1875
1876                 if (align) {
1877                         size = mini_type_stack_size (gsctx, t, &ialign);
1878                         *align = ialign;
1879                 } else {
1880                         size = mini_type_stack_size (gsctx, t, NULL);
1881                 }
1882         }
1883         
1884         return size;
1885 }
1886
1887 /*
1888  * mono_generic_sharing_init:
1889  *
1890  * Register the generic sharing counters.
1891  */
1892 void
1893 mono_generic_sharing_init (void)
1894 {
1895         mono_install_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
1896 }
1897
1898 void
1899 mono_generic_sharing_cleanup (void)
1900 {
1901         mono_remove_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
1902
1903         if (generic_subclass_hash)
1904                 g_hash_table_destroy (generic_subclass_hash);
1905 }
1906
1907 gboolean
1908 mini_type_is_reference (MonoCompile *cfg, MonoType *type)
1909 {
1910         if (mono_type_is_reference (type))
1911                 return TRUE;
1912         if (!cfg->generic_sharing_context)
1913                 return FALSE;
1914         /*FIXME the probably needs better handle under partial sharing*/
1915         return type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR;
1916 }