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