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