Merge branch 'cecil-light'
[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         gboolean has_refs;
610         int i;
611
612         has_refs = FALSE;
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                         has_refs = TRUE;
618         }
619  
620         for (i = 0; i < inst->type_argc; ++i) {
621                 MonoType *type = inst->type_argv [i];
622
623                 if (MONO_TYPE_IS_REFERENCE (type) || (allow_type_vars && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)))
624                         continue;
625  
626                 /*
627                  * Allow non ref arguments, if there is at least one ref argument
628                  * (partial sharing).
629                  * FIXME: Allow more types
630                  */
631                 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)))
632                         continue;
633
634                 return FALSE;
635         }
636
637         return TRUE;
638 }
639
640 /*
641  * mono_is_partially_sharable_inst:
642  *
643  *   Return TRUE if INST has ref and non-ref type arguments.
644  */
645 gboolean
646 mono_is_partially_sharable_inst (MonoGenericInst *inst)
647 {
648         int i;
649         gboolean has_refs = FALSE, has_non_refs = FALSE;
650
651         for (i = 0; i < inst->type_argc; ++i) {
652                 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)
653                         has_refs = TRUE;
654                 else
655                         has_non_refs = TRUE;
656         }
657
658         return has_refs && has_non_refs;
659 }
660
661 /*
662  * get_shared_class:
663  *
664  *   Return the class used to store information when using generic sharing.
665  * For fully shared classes, it is the generic definition, for partially shared
666  * classes, it is an instance with all ref type arguments replaced by the type parameters
667  * of its generic definition.
668  */
669 static MonoClass*
670 get_shared_class (MonoClass *class)
671 {
672         /*
673          * FIXME: This conflicts with normal instances. Also, some code in this file
674          * like class_get_rgctx_template_oti treats these as normal generic instances
675          * instead of generic classes.
676          */
677         //g_assert_not_reached ();
678
679         if (class->is_inflated) {
680                 MonoGenericContext *context = &class->generic_class->context;
681                 MonoGenericContext *container_context;
682                 MonoGenericContext shared_context;
683                 MonoGenericInst *inst;
684                 MonoType **type_argv;
685                 int i;
686
687                 inst = context->class_inst;
688                 if (mono_is_partially_sharable_inst (inst)) {
689                         container_context = &class->generic_class->container_class->generic_container->context;
690                         type_argv = g_new0 (MonoType*, inst->type_argc);
691                         for (i = 0; i < inst->type_argc; ++i) {
692                                 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)
693                                         type_argv [i] = container_context->class_inst->type_argv [i];
694                                 else
695                                         type_argv [i] = inst->type_argv [i];
696                         }
697
698                         memset (&shared_context, 0, sizeof (MonoGenericContext));
699                         shared_context.class_inst = mono_metadata_get_generic_inst (inst->type_argc, type_argv);
700                         g_free (type_argv);
701
702                         return mono_class_inflate_generic_class (class->generic_class->container_class, &shared_context);
703                 } else if (!generic_inst_is_sharable (inst, TRUE, FALSE)) {
704                         /* Happens for partially shared methods of nono-sharable generic class */
705                         return class;
706                 }
707         }
708
709         return class_uninstantiated (class);
710 }
711
712 /*
713  * mono_class_get_runtime_generic_context_template:
714  * @class: a class
715  *
716  * Looks up or constructs, if necessary, the runtime generic context
717  * for class.
718  */
719 static MonoRuntimeGenericContextTemplate*
720 mono_class_get_runtime_generic_context_template (MonoClass *class)
721 {
722         MonoRuntimeGenericContextTemplate *parent_template, *template;
723         guint32 i;
724
725         mono_loader_lock ();
726         template = class_lookup_rgctx_template (class);
727         mono_loader_unlock ();
728
729         if (template)
730                 return template;
731
732         //g_assert (get_shared_class (class) == class);
733
734         template = alloc_template (class);
735
736         mono_loader_lock ();
737
738         if (class->parent) {
739                 if (class->parent->generic_class) {
740                         guint32 num_entries;
741                         int max_argc, type_argc;
742
743                         parent_template = mono_class_get_runtime_generic_context_template
744                                 (class->parent->generic_class->container_class);
745
746                         max_argc = template_get_max_argc (parent_template);
747
748                         for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
749                                 num_entries = rgctx_template_num_other_infos (parent_template, type_argc);
750
751                                 /* FIXME: quadratic! */
752                                 for (i = 0; i < num_entries; ++i) {
753                                         MonoRuntimeGenericContextOtherInfoTemplate oti;
754
755                                         oti = class_get_rgctx_template_oti (class->parent, type_argc, i, FALSE, FALSE, NULL);
756                                         if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
757                                                 rgctx_template_set_other_slot (class->image, template, type_argc, i,
758                                                         oti.data, oti.info_type);
759                                         }
760                                 }
761                         }
762                 } else {
763                         MonoRuntimeGenericContextOtherInfoTemplate *oti;
764                         int max_argc, type_argc;
765
766                         parent_template = mono_class_get_runtime_generic_context_template (class->parent);
767
768                         max_argc = template_get_max_argc (parent_template);
769
770                         for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
771                                 /* FIXME: quadratic! */
772                                 for (i = 0, oti = parent_template->other_infos; oti; ++i, oti = oti->next) {
773                                         if (oti->data && oti->data != MONO_RGCTX_SLOT_USED_MARKER) {
774                                                 rgctx_template_set_other_slot (class->image, template, type_argc, i,
775                                                         oti->data, oti->info_type);
776                                         }
777                                 }
778                         }
779                 }
780         }
781
782         if (class_lookup_rgctx_template (class)) {
783                 /* some other thread already set the template */
784                 template = class_lookup_rgctx_template (class);
785         } else {
786                 class_set_rgctx_template (class, template);
787
788                 if (class->parent)
789                         register_generic_subclass (class);
790         }
791
792         mono_loader_unlock ();
793
794         return template;
795 }
796
797 /*
798  * temporary signifies whether the inflated info (oti.data) will be
799  * used temporarily, in which case it might be heap-allocated, or
800  * permanently, in which case it will be mempool-allocated.  If
801  * temporary is set then *do_free will return whether the returned
802  * data must be freed.
803  *
804  * LOCKING: loader lock
805  */
806 static MonoRuntimeGenericContextOtherInfoTemplate
807 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free)
808 {
809         g_assert ((temporary && do_free) || (!temporary && !do_free));
810
811         if (class->generic_class && !shared) {
812                 MonoRuntimeGenericContextOtherInfoTemplate oti;
813                 gboolean tmp_do_free;
814
815                 oti = class_get_rgctx_template_oti (class->generic_class->container_class,
816                                                                                         type_argc, slot, TRUE, FALSE, &tmp_do_free);
817                 if (oti.data) {
818                         gpointer info = oti.data;
819                         oti.data = inflate_other_info (&oti, &class->generic_class->context, class, temporary);
820                         if (tmp_do_free)
821                                 free_inflated_info (oti.info_type, info);
822                 }
823                 if (temporary)
824                         *do_free = TRUE;
825
826                 return oti;
827         } else {
828                 MonoRuntimeGenericContextTemplate *template;
829                 MonoRuntimeGenericContextOtherInfoTemplate *oti;
830
831                 template = mono_class_get_runtime_generic_context_template (class);
832                 oti = rgctx_template_get_other_slot (template, type_argc, slot);
833                 g_assert (oti);
834
835                 if (temporary)
836                         *do_free = FALSE;
837
838                 return *oti;
839         }
840 }
841
842 static gpointer
843 class_type_info (MonoDomain *domain, MonoClass *class, int info_type)
844 {
845         switch (info_type) {
846         case MONO_RGCTX_INFO_STATIC_DATA: {
847                 MonoVTable *vtable = mono_class_vtable (domain, class);
848                 if (!vtable)
849                         mono_raise_exception (mono_class_get_exception_for_failure (class));
850                 return vtable->data;
851         }
852         case MONO_RGCTX_INFO_KLASS:
853                 return class;
854         case MONO_RGCTX_INFO_VTABLE: {
855                 MonoVTable *vtable = mono_class_vtable (domain, class);
856                 if (!vtable)
857                         mono_raise_exception (mono_class_get_exception_for_failure (class));
858                 return vtable;
859         }
860         case MONO_RGCTX_INFO_CAST_CACHE: {
861                 /*First slot is the cache itself, the second the vtable.*/
862                 gpointer **cache_data = mono_domain_alloc0 (domain, sizeof (gpointer) * 2);
863                 cache_data [1] = (gpointer)class;
864                 return cache_data;
865         }
866         default:
867                 g_assert_not_reached ();
868         }
869         /* Not reached */
870         return NULL;
871 }
872
873 static gpointer
874 instantiate_other_info (MonoDomain *domain, MonoRuntimeGenericContextOtherInfoTemplate *oti,
875         MonoGenericContext *context, MonoClass *class)
876 {
877         gpointer data;
878         gboolean temporary;
879
880         if (!oti->data)
881                 return NULL;
882
883         switch (oti->info_type) {
884         case MONO_RGCTX_INFO_STATIC_DATA:
885         case MONO_RGCTX_INFO_KLASS:
886         case MONO_RGCTX_INFO_VTABLE:
887         case MONO_RGCTX_INFO_CAST_CACHE:
888                 temporary = TRUE;
889                 break;
890         default:
891                 temporary = FALSE;
892         }
893
894         data = inflate_other_info (oti, context, class, temporary);
895
896         switch (oti->info_type) {
897         case MONO_RGCTX_INFO_STATIC_DATA:
898         case MONO_RGCTX_INFO_KLASS:
899         case MONO_RGCTX_INFO_VTABLE:
900         case MONO_RGCTX_INFO_CAST_CACHE: {
901                 MonoClass *arg_class = mono_class_from_mono_type (data);
902
903                 free_inflated_info (oti->info_type, data);
904                 g_assert (arg_class);
905
906                 /* The class might be used as an argument to
907                    mono_value_copy(), which requires that its GC
908                    descriptor has been computed. */
909                 if (oti->info_type == MONO_RGCTX_INFO_KLASS)
910                         mono_class_compute_gc_descriptor (arg_class);
911
912                 return class_type_info (domain, arg_class, oti->info_type);
913         }
914         case MONO_RGCTX_INFO_TYPE:
915                 return data;
916         case MONO_RGCTX_INFO_REFLECTION_TYPE:
917                 return mono_type_get_object (domain, data);
918         case MONO_RGCTX_INFO_METHOD:
919                 return data;
920         case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
921                 /*
922                  * We can't create a jump trampoline here, as it cannot be patched.
923                  */
924                 return mono_compile_method (data);
925         case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
926                 return mono_compile_method (mono_marshal_get_remoting_invoke_with_check (data));
927         case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
928                 return mono_domain_alloc0 (domain, sizeof (gpointer));
929         case MONO_RGCTX_INFO_CLASS_FIELD:
930                 return data;
931         case MONO_RGCTX_INFO_METHOD_RGCTX: {
932                 MonoMethodInflated *method = data;
933                 MonoVTable *vtable;
934
935                 g_assert (method->method.method.is_inflated);
936                 g_assert (method->context.method_inst);
937
938                 vtable = mono_class_vtable (domain, method->method.method.klass);
939                 if (!vtable)
940                         mono_raise_exception (mono_class_get_exception_for_failure (method->method.method.klass));
941
942                 return mono_method_lookup_rgctx (vtable, method->context.method_inst);
943         }
944         case MONO_RGCTX_INFO_METHOD_CONTEXT: {
945                 MonoMethodInflated *method = data;
946
947                 g_assert (method->method.method.is_inflated);
948                 g_assert (method->context.method_inst);
949
950                 return method->context.method_inst;
951         }
952         default:
953                 g_assert_not_reached ();
954         }
955         /* Not reached */
956         return NULL;
957 }
958
959 /*
960  * LOCKING: loader lock
961  */
962 static void
963 fill_in_rgctx_template_slot (MonoClass *class, int type_argc, int index, gpointer data, int info_type)
964 {
965         MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
966         MonoClass *subclass;
967
968         rgctx_template_set_other_slot (class->image, template, type_argc, index, data, info_type);
969
970         /* Recurse for all subclasses */
971         if (generic_subclass_hash)
972                 subclass = g_hash_table_lookup (generic_subclass_hash, class);
973         else
974                 subclass = NULL;
975
976         while (subclass) {
977                 MonoRuntimeGenericContextOtherInfoTemplate subclass_oti;
978                 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
979
980                 g_assert (subclass_template);
981
982                 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, FALSE, NULL);
983                 g_assert (subclass_oti.data);
984
985                 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
986
987                 subclass = subclass_template->next_subclass;
988         }
989 }
990
991 /*
992  * LOCKING: loader lock
993  */
994 static int
995 register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type)
996 {
997         int i;
998         MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
999         MonoClass *parent;
1000         MonoRuntimeGenericContextOtherInfoTemplate *oti;
1001
1002         for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
1003                 if (!oti->data)
1004                         break;
1005         }
1006
1007         //g_print ("template %s . other_infos [%d] = %s\n", mono_type_get_full_name (class), i, mono_type_get_full_name (other_class));
1008
1009         /* Mark the slot as used in all parent classes (until we find
1010            a parent class which already has it marked used). */
1011         parent = class->parent;
1012         while (parent != NULL) {
1013                 MonoRuntimeGenericContextTemplate *parent_template;
1014                 MonoRuntimeGenericContextOtherInfoTemplate *oti;
1015
1016                 if (parent->generic_class)
1017                         parent = parent->generic_class->container_class;
1018
1019                 parent_template = mono_class_get_runtime_generic_context_template (parent);
1020                 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
1021
1022                 if (oti && oti->data)
1023                         break;
1024
1025                 rgctx_template_set_other_slot (parent->image, parent_template, type_argc, i,
1026                                 MONO_RGCTX_SLOT_USED_MARKER, 0);
1027
1028                 parent = parent->parent;
1029         }
1030
1031         /* Fill in the slot in this class and in all subclasses
1032            recursively. */
1033         fill_in_rgctx_template_slot (class, type_argc, i, data, info_type);
1034
1035         return i;
1036 }
1037
1038 static gboolean
1039 other_info_equal (gpointer data1, gpointer data2, int info_type)
1040 {
1041         switch (info_type) {
1042         case MONO_RGCTX_INFO_STATIC_DATA:
1043         case MONO_RGCTX_INFO_KLASS:
1044         case MONO_RGCTX_INFO_VTABLE:
1045         case MONO_RGCTX_INFO_TYPE:
1046         case MONO_RGCTX_INFO_REFLECTION_TYPE:
1047         case MONO_RGCTX_INFO_CAST_CACHE:
1048                 return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
1049         case MONO_RGCTX_INFO_METHOD:
1050         case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
1051         case MONO_RGCTX_INFO_CLASS_FIELD:
1052         case MONO_RGCTX_INFO_METHOD_RGCTX:
1053         case MONO_RGCTX_INFO_METHOD_CONTEXT:
1054         case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
1055         case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
1056                 return data1 == data2;
1057         default:
1058                 g_assert_not_reached ();
1059         }
1060         /* never reached */
1061         return FALSE;
1062 }
1063
1064 static int
1065 lookup_or_register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type,
1066         MonoGenericContext *generic_context)
1067 {
1068         static gboolean inited = FALSE;
1069         static int max_slot = 0;
1070
1071         MonoRuntimeGenericContextTemplate *rgctx_template =
1072                 mono_class_get_runtime_generic_context_template (class);
1073         MonoRuntimeGenericContextOtherInfoTemplate *oti_list, *oti;
1074         int i;
1075
1076         mono_loader_lock ();
1077
1078         if (other_info_has_identity (info_type)) {
1079                 oti_list = get_other_info_templates (rgctx_template, type_argc);
1080
1081                 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
1082                         gpointer inflated_data;
1083
1084                         if (oti->info_type != info_type || !oti->data)
1085                                 continue;
1086
1087                         inflated_data = inflate_other_info (oti, generic_context, class, TRUE);
1088
1089                         if (other_info_equal (data, inflated_data, info_type)) {
1090                                 free_inflated_info (info_type, inflated_data);
1091                                 mono_loader_unlock ();
1092                                 return i;
1093                         }
1094                         free_inflated_info (info_type, inflated_data);
1095                 }
1096         }
1097
1098         /* We haven't found the info */
1099         i = register_other_info (class, type_argc, data, info_type);
1100
1101         mono_loader_unlock ();
1102
1103         if (!inited) {
1104                 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
1105                 inited = TRUE;
1106         }
1107         if (i > max_slot)
1108                 max_slot = i;
1109
1110         return i;
1111 }
1112
1113 /*
1114  * mono_method_lookup_or_register_other_info:
1115  * @method: a method
1116  * @in_mrgctx: whether to put the data into the MRGCTX
1117  * @data: the info data
1118  * @info_type: the type of info to register about data
1119  * @generic_context: a generic context
1120  *
1121  * Looks up and, if necessary, adds information about other_class in
1122  * method's or method's class runtime generic context.  Returns the
1123  * encoded slot number.
1124  */
1125 guint32
1126 mono_method_lookup_or_register_other_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
1127         int info_type, MonoGenericContext *generic_context)
1128 {
1129         MonoClass *class = method->klass;
1130         int type_argc, index;
1131
1132         if (in_mrgctx) {
1133                 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
1134
1135                 g_assert (method->is_inflated && method_inst);
1136                 type_argc = method_inst->type_argc;
1137                 g_assert (type_argc > 0);
1138         } else {
1139                 type_argc = 0;
1140         }
1141
1142         index = lookup_or_register_other_info (class, type_argc, data, info_type, generic_context);
1143
1144         //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
1145
1146         if (in_mrgctx)
1147                 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
1148         else
1149                 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
1150 }
1151
1152 /*
1153  * mono_class_rgctx_get_array_size:
1154  * @n: The number of the array
1155  * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
1156  *
1157  * Returns the number of slots in the n'th array of a (M)RGCTX.  That
1158  * number includes the slot for linking and - for MRGCTXs - the two
1159  * slots in the first array for additional information.
1160  */
1161 int
1162 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
1163 {
1164         g_assert (n >= 0 && n < 30);
1165
1166         if (mrgctx)
1167                 return 6 << n;
1168         else
1169                 return 4 << n;
1170 }
1171
1172 /*
1173  * LOCKING: domain lock
1174  */
1175 static gpointer*
1176 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
1177 {
1178         static gboolean inited = FALSE;
1179         static int rgctx_num_alloced = 0;
1180         static int rgctx_bytes_alloced = 0;
1181         static int mrgctx_num_alloced = 0;
1182         static int mrgctx_bytes_alloced = 0;
1183
1184         int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
1185         gpointer array = mono_domain_alloc0 (domain, size);
1186
1187         if (!inited) {
1188                 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
1189                 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
1190                 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
1191                 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
1192                 inited = TRUE;
1193         }
1194
1195         if (is_mrgctx) {
1196                 mrgctx_num_alloced++;
1197                 mrgctx_bytes_alloced += size;
1198         } else {
1199                 rgctx_num_alloced++;
1200                 rgctx_bytes_alloced += size;
1201         }
1202
1203         return array;
1204 }
1205
1206 static gpointer
1207 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
1208                 MonoGenericInst *method_inst)
1209 {
1210         gpointer info;
1211         int i, first_slot, size;
1212         MonoDomain *domain = class_vtable->domain;
1213         MonoClass *class = class_vtable->klass;
1214         MonoGenericContext *class_context = class->generic_class ? &class->generic_class->context : NULL;
1215         MonoRuntimeGenericContextOtherInfoTemplate oti;
1216         MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
1217         int rgctx_index;
1218         gboolean do_free;
1219
1220         g_assert (rgctx);
1221
1222         mono_domain_lock (domain);
1223
1224         /* First check whether that slot isn't already instantiated.
1225            This might happen because lookup doesn't lock.  Allocate
1226            arrays on the way. */
1227         first_slot = 0;
1228         size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
1229         if (method_inst)
1230                 size -= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
1231         for (i = 0; ; ++i) {
1232                 int offset;
1233
1234                 if (method_inst && i == 0)
1235                         offset = MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
1236                 else
1237                         offset = 0;
1238
1239                 if (slot < first_slot + size - 1) {
1240                         rgctx_index = slot - first_slot + 1 + offset;
1241                         info = rgctx [rgctx_index];
1242                         if (info) {
1243                                 mono_domain_unlock (domain);
1244                                 return info;
1245                         }
1246                         break;
1247                 }
1248                 if (!rgctx [offset + 0])
1249                         rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
1250                 rgctx = rgctx [offset + 0];
1251                 first_slot += size - 1;
1252                 size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
1253         }
1254
1255         g_assert (!rgctx [rgctx_index]);
1256
1257         mono_domain_unlock (domain);
1258
1259         oti = class_get_rgctx_template_oti (get_shared_class (class),
1260                                                                                 method_inst ? method_inst->type_argc : 0, slot, TRUE, TRUE, &do_free);
1261         /* This might take the loader lock */
1262         info = instantiate_other_info (domain, &oti, &context, class);
1263
1264         /*
1265         if (method_inst)
1266                 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
1267         */
1268
1269         /*FIXME We should use CAS here, no need to take a lock.*/
1270         mono_domain_lock (domain);
1271
1272         /* Check whether the slot hasn't been instantiated in the
1273            meantime. */
1274         if (rgctx [rgctx_index])
1275                 info = rgctx [rgctx_index];
1276         else
1277                 rgctx [rgctx_index] = info;
1278
1279         mono_domain_unlock (domain);
1280
1281         if (do_free)
1282                 free_inflated_info (oti.info_type, oti.data);
1283
1284         return info;
1285 }
1286
1287 /*
1288  * mono_class_fill_runtime_generic_context:
1289  * @class_vtable: a vtable
1290  * @slot: a slot index to be instantiated
1291  *
1292  * Instantiates a slot in the RGCTX.
1293  */
1294 gpointer
1295 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot)
1296 {
1297         static gboolean inited = FALSE;
1298         static int num_alloced = 0;
1299
1300         MonoDomain *domain = class_vtable->domain;
1301         MonoRuntimeGenericContext *rgctx;
1302         gpointer info;
1303
1304         mono_domain_lock (domain);
1305
1306         if (!inited) {
1307                 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
1308                 inited = TRUE;
1309         }
1310
1311         rgctx = class_vtable->runtime_generic_context;
1312         if (!rgctx) {
1313                 rgctx = alloc_rgctx_array (domain, 0, FALSE);
1314                 class_vtable->runtime_generic_context = rgctx;
1315                 num_alloced++;
1316         }
1317
1318         mono_domain_unlock (domain);
1319
1320         info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
1321
1322         return info;
1323 }
1324
1325 /*
1326  * mono_method_fill_runtime_generic_context:
1327  * @mrgctx: an MRGCTX
1328  * @slot: a slot index to be instantiated
1329  *
1330  * Instantiates a slot in the MRGCTX.
1331  */
1332 gpointer
1333 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot)
1334 {
1335         gpointer info;
1336
1337         info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot,
1338                 mrgctx->method_inst);
1339
1340         return info;
1341 }
1342
1343 static guint
1344 mrgctx_hash_func (gconstpointer key)
1345 {
1346         const MonoMethodRuntimeGenericContext *mrgctx = key;
1347
1348         return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
1349 }
1350
1351 static gboolean
1352 mrgctx_equal_func (gconstpointer a, gconstpointer b)
1353 {
1354         const MonoMethodRuntimeGenericContext *mrgctx1 = a;
1355         const MonoMethodRuntimeGenericContext *mrgctx2 = b;
1356
1357         return mrgctx1->class_vtable == mrgctx2->class_vtable &&
1358                 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
1359 }
1360
1361 /*
1362  * mono_method_lookup_rgctx:
1363  * @class_vtable: a vtable
1364  * @method_inst: the method inst of a generic method
1365  *
1366  * Returns the MRGCTX for the generic method(s) with the given
1367  * method_inst of the given class_vtable.
1368  *
1369  * LOCKING: Take the domain lock.
1370  */
1371 MonoMethodRuntimeGenericContext*
1372 mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
1373 {
1374         MonoDomain *domain = class_vtable->domain;
1375         MonoMethodRuntimeGenericContext *mrgctx;
1376         MonoMethodRuntimeGenericContext key;
1377
1378         g_assert (!class_vtable->klass->generic_container);
1379         g_assert (!method_inst->is_open);
1380
1381         mono_domain_lock (domain);
1382         if (!domain->method_rgctx_hash)
1383                 domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
1384
1385         key.class_vtable = class_vtable;
1386         key.method_inst = method_inst;
1387
1388         mrgctx = g_hash_table_lookup (domain->method_rgctx_hash, &key);
1389
1390         if (!mrgctx) {
1391                 //int i;
1392
1393                 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
1394                 mrgctx->class_vtable = class_vtable;
1395                 mrgctx->method_inst = method_inst;
1396
1397                 g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
1398
1399                 /*
1400                 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
1401                 for (i = 0; i < method_inst->type_argc; ++i)
1402                         g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
1403                 g_print (">\n");
1404                 */
1405         }
1406
1407         mono_domain_unlock (domain);
1408
1409         g_assert (mrgctx);
1410
1411         return mrgctx;
1412 }
1413
1414 /*
1415  * mono_generic_context_is_sharable_full:
1416  * @context: a generic context
1417  *
1418  * Returns whether the generic context is sharable.  A generic context
1419  * is sharable iff all of its type arguments are reference type, or some of them have a
1420  * reference type, and ALLOW_PARTIAL is TRUE.
1421  */
1422 gboolean
1423 mono_generic_context_is_sharable_full (MonoGenericContext *context,
1424                                                                            gboolean allow_type_vars,
1425                                                                            gboolean allow_partial)
1426 {
1427         g_assert (context->class_inst || context->method_inst);
1428
1429         if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars, allow_partial))
1430                 return FALSE;
1431
1432         if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars, allow_partial))
1433                 return FALSE;
1434
1435         return TRUE;
1436 }
1437
1438 gboolean
1439 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
1440 {
1441         return mono_generic_context_is_sharable_full (context, allow_type_vars, ALLOW_PARTIAL_SHARING);
1442 }
1443
1444 /*
1445  * mono_method_is_generic_impl:
1446  * @method: a method
1447  *
1448  * Returns whether the method is either generic or part of a generic
1449  * class.
1450  */
1451 gboolean
1452 mono_method_is_generic_impl (MonoMethod *method)
1453 {
1454         if (method->is_inflated) {
1455                 g_assert (method->wrapper_type == MONO_WRAPPER_NONE);
1456                 return TRUE;
1457         }
1458         /* We don't treat wrappers as generic code, i.e., we never
1459            apply generic sharing to them.  This is especially
1460            important for static rgctx invoke wrappers, which only work
1461            if not compiled with sharing. */
1462         if (method->wrapper_type != MONO_WRAPPER_NONE)
1463                 return FALSE;
1464         if (method->klass->generic_container)
1465                 return TRUE;
1466         return FALSE;
1467 }
1468
1469 static gboolean
1470 has_constraints (MonoGenericContainer *container)
1471 {
1472         //int i;
1473
1474         return FALSE;
1475         /*
1476         g_assert (container->type_argc > 0);
1477         g_assert (container->type_params);
1478
1479         for (i = 0; i < container->type_argc; ++i)
1480                 if (container->type_params [i].constraints)
1481                         return TRUE;
1482         return FALSE;
1483         */
1484 }
1485
1486 /*
1487  * mono_method_is_generic_sharable_impl_full:
1488  * @method: a method
1489  * @allow_type_vars: whether to regard type variables as reference types
1490  * @alloc_partial: whether to allow partial sharing
1491  *
1492  * Returns TRUE iff the method is inflated or part of an inflated
1493  * class, its context is sharable and it has no constraints on its
1494  * type parameters.  Otherwise returns FALSE.
1495  */
1496 gboolean
1497 mono_method_is_generic_sharable_impl_full (MonoMethod *method, gboolean allow_type_vars,
1498                                                                                    gboolean allow_partial)
1499 {
1500         if (!mono_method_is_generic_impl (method))
1501                 return FALSE;
1502
1503         if (method->is_inflated) {
1504                 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
1505                 MonoGenericContext *context = &inflated->context;
1506
1507                 if (!mono_generic_context_is_sharable_full (context, allow_type_vars, allow_partial))
1508                         return FALSE;
1509
1510                 g_assert (inflated->declaring);
1511
1512                 if (inflated->declaring->is_generic) {
1513                         if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
1514                                 return FALSE;
1515                 }
1516         }
1517
1518         if (method->klass->generic_class) {
1519                 if (!mono_generic_context_is_sharable_full (&method->klass->generic_class->context, allow_type_vars, allow_partial))
1520                         return FALSE;
1521
1522                 g_assert (method->klass->generic_class->container_class &&
1523                                 method->klass->generic_class->container_class->generic_container);
1524
1525                 if (has_constraints (method->klass->generic_class->container_class->generic_container))
1526                         return FALSE;
1527         }
1528
1529         if (method->klass->generic_container && !allow_type_vars)
1530                 return FALSE;
1531
1532         return TRUE;
1533 }
1534
1535 gboolean
1536 mono_method_is_generic_sharable_impl (MonoMethod *method, gboolean allow_type_vars)
1537 {
1538         return mono_method_is_generic_sharable_impl_full (method, allow_type_vars, ALLOW_PARTIAL_SHARING);
1539 }
1540
1541 gboolean
1542 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
1543 {
1544         if (!mono_class_generic_sharing_enabled (method->klass))
1545                 return FALSE;
1546
1547         if (!mono_method_is_generic_sharable_impl (method, allow_type_vars))
1548                 return FALSE;
1549
1550         if (method->is_inflated && mono_method_get_context (method)->method_inst)
1551                 return TRUE;
1552
1553         return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
1554                         method->klass->valuetype) &&
1555                 (method->klass->generic_class || method->klass->generic_container);
1556 }
1557
1558 static MonoGenericInst*
1559 get_object_generic_inst (int type_argc)
1560 {
1561         MonoType **type_argv;
1562         int i;
1563
1564         type_argv = alloca (sizeof (MonoType*) * type_argc);
1565
1566         for (i = 0; i < type_argc; ++i)
1567                 type_argv [i] = &mono_defaults.object_class->byval_arg;
1568
1569         return mono_metadata_get_generic_inst (type_argc, type_argv);
1570 }
1571
1572 /*
1573  * mono_method_construct_object_context:
1574  * @method: a method
1575  *
1576  * Returns a generic context for method with all type variables for
1577  * class and method instantiated with Object.
1578  */
1579 MonoGenericContext
1580 mono_method_construct_object_context (MonoMethod *method)
1581 {
1582         MonoGenericContext object_context;
1583
1584         g_assert (!method->klass->generic_class);
1585         if (method->klass->generic_container) {
1586                 int type_argc = method->klass->generic_container->type_argc;
1587
1588                 object_context.class_inst = get_object_generic_inst (type_argc);
1589         } else {
1590                 object_context.class_inst = NULL;
1591         }
1592
1593         if (mono_method_get_context_general (method, TRUE)->method_inst) {
1594                 int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
1595
1596                 object_context.method_inst = get_object_generic_inst (type_argc);
1597         } else {
1598                 object_context.method_inst = NULL;
1599         }
1600
1601         g_assert (object_context.class_inst || object_context.method_inst);
1602
1603         return object_context;
1604 }
1605
1606 static gboolean gshared_supported;
1607
1608 void
1609 mono_set_generic_sharing_supported (gboolean supported)
1610 {
1611         gshared_supported = supported;
1612 }
1613
1614 /*
1615  * mono_class_generic_sharing_enabled:
1616  * @class: a class
1617  *
1618  * Returns whether generic sharing is enabled for class.
1619  *
1620  * This is a stop-gap measure to slowly introduce generic sharing
1621  * until we have all the issues sorted out, at which time this
1622  * function will disappear and generic sharing will always be enabled.
1623  */
1624 gboolean
1625 mono_class_generic_sharing_enabled (MonoClass *class)
1626 {
1627         static int generic_sharing = MONO_GENERIC_SHARING_NONE;
1628         static gboolean inited = FALSE;
1629
1630         if (!inited) {
1631                 const char *option;
1632
1633                 if (gshared_supported)
1634                         generic_sharing = MONO_GENERIC_SHARING_ALL;
1635                 else
1636                         generic_sharing = MONO_GENERIC_SHARING_NONE;
1637
1638                 if ((option = g_getenv ("MONO_GENERIC_SHARING"))) {
1639                         if (strcmp (option, "corlib") == 0)
1640                                 generic_sharing = MONO_GENERIC_SHARING_CORLIB;
1641                         else if (strcmp (option, "collections") == 0)
1642                                 generic_sharing = MONO_GENERIC_SHARING_COLLECTIONS;
1643                         else if (strcmp (option, "all") == 0)
1644                                 generic_sharing = MONO_GENERIC_SHARING_ALL;
1645                         else if (strcmp (option, "none") == 0)
1646                                 generic_sharing = MONO_GENERIC_SHARING_NONE;
1647                         else
1648                                 g_warning ("Unknown generic sharing option `%s'.", option);
1649                 }
1650
1651                 if (!gshared_supported)
1652                         generic_sharing = MONO_GENERIC_SHARING_NONE;
1653
1654                 inited = TRUE;
1655         }
1656
1657         switch (generic_sharing) {
1658         case MONO_GENERIC_SHARING_NONE:
1659                 return FALSE;
1660         case MONO_GENERIC_SHARING_ALL:
1661                 return TRUE;
1662         case MONO_GENERIC_SHARING_CORLIB :
1663                 return class->image == mono_defaults.corlib;
1664         case MONO_GENERIC_SHARING_COLLECTIONS:
1665                 if (class->image != mono_defaults.corlib)
1666                         return FALSE;
1667                 while (class->nested_in)
1668                         class = class->nested_in;
1669                 return g_str_has_prefix (class->name_space, "System.Collections.Generic");
1670         default:
1671                 g_assert_not_reached ();
1672         }
1673         return FALSE;
1674 }
1675
1676 /*
1677  * mono_get_generic_context_from_code:
1678  *
1679  *   Return the runtime generic context belonging to the method whose native code
1680  * contains CODE.
1681  */
1682 MonoGenericSharingContext*
1683 mono_get_generic_context_from_code (guint8 *code)
1684 {
1685         MonoJitInfo *jit_info = mini_jit_info_table_find (mono_domain_get (), (char*)code, NULL);
1686
1687         g_assert (jit_info);
1688
1689         return mono_jit_info_get_generic_sharing_context (jit_info);
1690 }
1691
1692 MonoGenericContext*
1693 mini_method_get_context (MonoMethod *method)
1694 {
1695         return mono_method_get_context_general (method, TRUE);
1696 }
1697
1698 /*
1699  * mono_method_check_context_used:
1700  * @method: a method
1701  *
1702  * Checks whether the method's generic context uses a type variable.
1703  * Returns an int with the bits MONO_GENERIC_CONTEXT_USED_CLASS and
1704  * MONO_GENERIC_CONTEXT_USED_METHOD set to reflect whether the
1705  * context's class or method instantiation uses type variables.
1706  */
1707 int
1708 mono_method_check_context_used (MonoMethod *method)
1709 {
1710         MonoGenericContext *method_context = mini_method_get_context (method);
1711         int context_used = 0;
1712
1713         if (!method_context) {
1714                 /* It might be a method of an array of an open generic type */
1715                 if (method->klass->rank)
1716                         context_used = mono_class_check_context_used (method->klass);
1717         } else {
1718                 context_used = mono_generic_context_check_used (method_context);
1719                 context_used |= mono_class_check_context_used (method->klass);
1720         }
1721
1722         return context_used;
1723 }
1724
1725 static gboolean
1726 generic_inst_equal (MonoGenericInst *inst1, MonoGenericInst *inst2)
1727 {
1728         int i;
1729
1730         if (!inst1) {
1731                 g_assert (!inst2);
1732                 return TRUE;
1733         }
1734
1735         g_assert (inst2);
1736
1737         if (inst1->type_argc != inst2->type_argc)
1738                 return FALSE;
1739
1740         for (i = 0; i < inst1->type_argc; ++i)
1741                 if (!mono_metadata_type_equal (inst1->type_argv [i], inst2->type_argv [i]))
1742                         return FALSE;
1743
1744         return TRUE;
1745 }
1746
1747 /*
1748  * mono_generic_context_equal_deep:
1749  * @context1: a generic context
1750  * @context2: a generic context
1751  *
1752  * Returns whether context1's type arguments are equal to context2's
1753  * type arguments.
1754  */
1755 gboolean
1756 mono_generic_context_equal_deep (MonoGenericContext *context1, MonoGenericContext *context2)
1757 {
1758         return generic_inst_equal (context1->class_inst, context2->class_inst) &&
1759                 generic_inst_equal (context1->method_inst, context2->method_inst);
1760 }
1761
1762 /*
1763  * mini_class_get_container_class:
1764  * @class: a generic class
1765  *
1766  * Returns the class's container class, which is the class itself if
1767  * it doesn't have generic_class set.
1768  */
1769 MonoClass*
1770 mini_class_get_container_class (MonoClass *class)
1771 {
1772         if (class->generic_class)
1773                 return class->generic_class->container_class;
1774
1775         g_assert (class->generic_container);
1776         return class;
1777 }
1778
1779 /*
1780  * mini_class_get_context:
1781  * @class: a generic class
1782  *
1783  * Returns the class's generic context.
1784  */
1785 MonoGenericContext*
1786 mini_class_get_context (MonoClass *class)
1787 {
1788         if (class->generic_class)
1789                 return &class->generic_class->context;
1790
1791         g_assert (class->generic_container);
1792         return &class->generic_container->context;
1793 }
1794
1795 /*
1796  * mini_get_basic_type_from_generic:
1797  * @gsctx: a generic sharing context
1798  * @type: a type
1799  *
1800  * Returns a closed type corresponding to the possibly open type
1801  * passed to it.
1802  */
1803 MonoType*
1804 mini_get_basic_type_from_generic (MonoGenericSharingContext *gsctx, MonoType *type)
1805 {
1806         if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR))
1807                 g_assert (gsctx);
1808
1809         return mono_type_get_basic_type_from_generic (type);
1810 }
1811
1812 /*
1813  * mini_type_get_underlying_type:
1814  *
1815  *   Return the underlying type of TYPE, taking into account enums, byref and generic
1816  * sharing.
1817  */
1818 MonoType*
1819 mini_type_get_underlying_type (MonoGenericSharingContext *gsctx, MonoType *type)
1820 {
1821         if (type->byref)
1822                 return &mono_defaults.int_class->byval_arg;
1823         return mono_type_get_basic_type_from_generic (mono_type_get_underlying_type (type));
1824 }
1825
1826 /*
1827  * mini_type_stack_size:
1828  * @gsctx: a generic sharing context
1829  * @t: a type
1830  * @align: Pointer to an int for returning the alignment
1831  *
1832  * Returns the type's stack size and the alignment in *align.  The
1833  * type is allowed to be open.
1834  */
1835 int
1836 mini_type_stack_size (MonoGenericSharingContext *gsctx, MonoType *t, int *align)
1837 {
1838         gboolean allow_open = TRUE;
1839
1840         // FIXME: Some callers might not pass in a gsctx
1841         //allow_open = gsctx != NULL;
1842         return mono_type_stack_size_internal (t, align, allow_open);
1843 }
1844
1845 /*
1846  * mini_type_stack_size_full:
1847  *
1848  *   Same as mini_type_stack_size, but handle pinvoke data types as well.
1849  */
1850 int
1851 mini_type_stack_size_full (MonoGenericSharingContext *gsctx, MonoType *t, guint32 *align, gboolean pinvoke)
1852 {
1853         int size;
1854
1855         if (pinvoke) {
1856                 size = mono_type_native_stack_size (t, align);
1857         } else {
1858                 int ialign;
1859
1860                 if (align) {
1861                         size = mini_type_stack_size (gsctx, t, &ialign);
1862                         *align = ialign;
1863                 } else {
1864                         size = mini_type_stack_size (gsctx, t, NULL);
1865                 }
1866         }
1867         
1868         return size;
1869 }
1870
1871 /*
1872  * mono_generic_sharing_init:
1873  *
1874  * Register the generic sharing counters.
1875  */
1876 void
1877 mono_generic_sharing_init (void)
1878 {
1879         mono_install_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
1880 }
1881
1882 void
1883 mono_generic_sharing_cleanup (void)
1884 {
1885         mono_remove_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
1886
1887         if (generic_subclass_hash)
1888                 g_hash_table_destroy (generic_subclass_hash);
1889 }