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