2008-03-20 Mark Probst <mark.probst@gmail.com>
[mono.git] / mono / 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 static int generic_class_lookups = 0;
18 static int generic_class_lookup_failures = 0;
19
20 /*
21  * mini_method_get_context:
22  * @method: a method
23  *
24  * Returns the generic context of a method or NULL if it doesn't have
25  * one.  For an inflated method that's the context stored in the
26  * method.  Otherwise it's in the method's generic container or in the
27  * generic container of the method's class.
28  */
29 MonoGenericContext*
30 mini_method_get_context (MonoMethod *method)
31 {
32         if (method->is_inflated)
33                 return mono_method_get_context (method);
34         if (method->generic_container)
35                 return &method->generic_container->context;
36         if (method->klass->generic_container)
37                 return &method->klass->generic_container->context;
38         return NULL;
39 }
40
41 /*
42  * mono_method_check_context_used:
43  * @method: a method
44  *
45  * Checks whether the method's generic context uses a type variable.
46  * Returns an int with the bits MONO_GENERIC_CONTEXT_USED_CLASS and
47  * MONO_GENERIC_CONTEXT_USED_METHOD set to reflect whether the
48  * context's class or method instantiation uses type variables.
49  */
50 int
51 mono_method_check_context_used (MonoMethod *method)
52 {
53         MonoGenericContext *method_context = mini_method_get_context (method);
54
55         if (!method_context)
56                 return 0;
57
58         return mono_generic_context_check_used (method_context);
59 }
60
61 static gboolean
62 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars)
63 {
64         int i;
65
66         for (i = 0; i < inst->type_argc; ++i) {
67                 MonoType *type = inst->type_argv [i];
68                 int type_type;
69
70                 if (MONO_TYPE_IS_REFERENCE (type))
71                         continue;
72
73                 type_type = mono_type_get_type (type);
74                 if (allow_type_vars && (type_type == MONO_TYPE_VAR || type_type == MONO_TYPE_MVAR))
75                         continue;
76
77                 return FALSE;
78         }
79
80         return TRUE;
81 }
82
83 /*
84  * mono_generic_context_is_sharable:
85  * @context: a generic context
86  *
87  * Returns whether the generic context is sharable.  A generic context
88  * is sharable iff all of its type arguments are reference type.
89  */
90 gboolean
91 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
92 {
93         g_assert (context->class_inst || context->method_inst);
94
95         if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars))
96                 return FALSE;
97
98         if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars))
99                 return FALSE;
100
101         return TRUE;
102 }
103
104 /*
105  * mono_method_is_generic_impl:
106  * @method: a method
107  *
108  * Returns whether the method is either inflated or part of an
109  * inflated class.
110  */
111 gboolean
112 mono_method_is_generic_impl (MonoMethod *method)
113 {
114         return method->klass->generic_class != NULL && method->is_inflated;
115 }
116
117 /*
118  * mono_method_is_generic_sharable_impl:
119  * @method: a method
120  *
121  * Returns TRUE iff the method is inflated or part of an inflated
122  * class, its context is sharable and it has no constraints on its
123  * type parameters.  Otherwise returns FALSE.
124  */
125 gboolean
126 mono_method_is_generic_sharable_impl (MonoMethod *method)
127 {
128         if (!mono_method_is_generic_impl (method))
129                 return FALSE;
130
131         if (method->is_inflated) {
132                 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
133                 MonoGenericContext *context = &inflated->context;
134
135                 if (!mono_generic_context_is_sharable (context, FALSE))
136                         return FALSE;
137
138                 g_assert (inflated->declaring);
139
140                 if (inflated->declaring->generic_container) {
141                         g_assert (inflated->declaring->generic_container->type_params);
142
143                         if (inflated->declaring->generic_container->type_params->constraints)
144                                 return FALSE;
145                 }
146         }
147
148         if (method->klass->generic_class) {
149                 if (!mono_generic_context_is_sharable (&method->klass->generic_class->context, FALSE))
150                         return FALSE;
151
152                 g_assert (method->klass->generic_class->container_class &&
153                                 method->klass->generic_class->container_class->generic_container &&
154                                 method->klass->generic_class->container_class->generic_container->type_params);
155
156                 if (method->klass->generic_class->container_class->generic_container->type_params->constraints)
157                         return FALSE;
158         }
159
160         return TRUE;
161 }
162
163 static gboolean
164 generic_inst_equal (MonoGenericInst *inst1, MonoGenericInst *inst2)
165 {
166         int i;
167
168         if (!inst1) {
169                 g_assert (!inst2);
170                 return TRUE;
171         }
172
173         g_assert (inst2);
174
175         if (inst1->type_argc != inst2->type_argc)
176                 return FALSE;
177
178         for (i = 0; i < inst1->type_argc; ++i)
179                 if (!mono_metadata_type_equal (inst1->type_argv [i], inst2->type_argv [i]))
180                         return FALSE;
181
182         return TRUE;
183 }
184
185 /*
186  * mono_generic_context_equal_deep:
187  * @context1: a generic context
188  * @context2: a generic context
189  *
190  * Returns whether context1's type arguments are equal to context2's
191  * type arguments.
192  */
193 gboolean
194 mono_generic_context_equal_deep (MonoGenericContext *context1, MonoGenericContext *context2)
195 {
196         return generic_inst_equal (context1->class_inst, context2->class_inst) &&
197                 generic_inst_equal (context1->method_inst, context2->method_inst);
198 }
199
200 /*
201  * mini_class_get_container_class:
202  * @class: a generic class
203  *
204  * Returns the class's container class, which is the class itself if
205  * it doesn't have generic_class set.
206  */
207 MonoClass*
208 mini_class_get_container_class (MonoClass *class)
209 {
210         if (class->generic_class)
211                 return class->generic_class->container_class;
212
213         g_assert (class->generic_container);
214         return class;
215 }
216
217 /*
218  * mini_class_get_context:
219  * @class: a generic class
220  *
221  * Returns the class's generic context.
222  */
223 MonoGenericContext*
224 mini_class_get_context (MonoClass *class)
225 {
226         if (class->generic_class)
227                 return &class->generic_class->context;
228
229         g_assert (class->generic_container);
230         return &class->generic_container->context;
231 }
232
233 /*
234  * mini_get_basic_type_from_generic:
235  * @gsctx: a generic sharing context
236  * @type: a type
237  *
238  * Returns a closed type corresponding to the possibly open type
239  * passed to it.
240  */
241 MonoType*
242 mini_get_basic_type_from_generic (MonoGenericSharingContext *gsctx, MonoType *type)
243 {
244         if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR))
245                 g_assert (gsctx);
246
247         return mono_type_get_basic_type_from_generic (type);
248 }
249
250 /*
251  * mini_type_stack_size:
252  * @gsctx: a generic sharing context
253  * @t: a type
254  * @align: Pointer to an int for returning the alignment
255  *
256  * Returns the type's stack size and the alignment in *align.  The
257  * type is allowed to be open.
258  */
259 int
260 mini_type_stack_size (MonoGenericSharingContext *gsctx, MonoType *t, int *align)
261 {
262         return mono_type_stack_size_internal (t, align, gsctx != NULL);
263 }
264
265 /*
266  * mono_method_get_declaring_generic_method:
267  * @method: an inflated method
268  *
269  * Returns an inflated method's declaring method.
270  */
271 MonoMethod*
272 mono_method_get_declaring_generic_method (MonoMethod *method)
273 {
274         MonoMethodInflated *inflated;
275
276         g_assert (method->is_inflated);
277
278         inflated = (MonoMethodInflated*)method;
279
280         return inflated->declaring;
281 }
282
283 static gboolean
284 inflated_type_is_equal_to_class (MonoType *inflated_type, MonoClass *klass)
285 {
286         return klass == mono_class_from_mono_type (inflated_type);
287 }
288
289 /*
290  * mono_class_generic_class_relation:
291  * @klass: the class to be investigated
292  * @method_klass: the reference class
293  * @generic_context: the generic context of method_klass
294  * @arg_num: where a value will be returned
295  *
296  * Discovers and returns the relation of klass with reference to
297  * method_klass.  This can either be MINI_GENERIC_CLASS_RELATION_SELF,
298  * meaning that klass is the same as method_klass,
299  * MINI_GENERIC_CLASS_RELATION_ARGUMENT, meaning that klass is one of
300  * the type arguments of method_klass, or otherwise
301  * MINI_GENERIC_CLASS_RELATION_OTHER.  In the case of
302  * MINI_GENERIC_CLASS_RELATION_ARGUMENT the number of the argument is
303  * returned in *arg_num.
304  */
305 int
306 mono_class_generic_class_relation (MonoClass *klass, int info_type, MonoClass *method_klass,
307         MonoGenericContext *generic_context, int *arg_num)
308 {
309         MonoRuntimeGenericContextTemplate *rgctx_template =
310                 mono_class_get_runtime_generic_context_template (method_klass);
311         int i;
312
313         /* Reflection types can only be handled in the extensible
314            rgctx part. */
315         if (info_type != MONO_RGCTX_INFO_REFLECTION_TYPE) {
316                 for (i = 0; i < rgctx_template->num_arg_infos; ++i) {
317                         MonoType *arg_info = rgctx_template->arg_infos [i];
318                         MonoType *inflated_arg;
319
320                         if (arg_info == NULL)
321                                 continue;
322
323                         inflated_arg = mono_class_inflate_generic_type(arg_info, generic_context);
324
325                         if (inflated_type_is_equal_to_class (inflated_arg, klass)) {
326                                 if (arg_num)
327                                         *arg_num = i;
328                                 return MINI_GENERIC_CLASS_RELATION_ARGUMENT;
329                         }
330                 }
331
332                 if (!klass->generic_class && !klass->generic_container)
333                         g_assert_not_reached ();
334
335                 if (mini_class_get_container_class (klass) == mini_class_get_container_class (method_klass) &&
336                                 mono_generic_context_equal_deep (mini_class_get_context (klass), generic_context))
337                         return MINI_GENERIC_CLASS_RELATION_SELF;
338         }
339
340         i = mono_class_lookup_or_register_other_info (method_klass, &klass->byval_arg, info_type, generic_context);
341         if (arg_num)
342                 *arg_num = i;
343         return MINI_GENERIC_CLASS_RELATION_OTHER_TABLE;
344 }
345
346 typedef struct
347 {
348         guint32 token;
349         MonoGenericContext *context;
350 } MonoTokenAndContext;
351
352 static guint
353 token_context_hash (MonoTokenAndContext *tc)
354 {
355         return (guint)((gulong)tc->token | (gulong)tc->context->class_inst | (gulong)tc->context->method_inst);
356 }
357
358 static gboolean
359 token_context_equal (MonoTokenAndContext *tc1, MonoTokenAndContext *tc2)
360 {
361         if (tc1->token != tc2->token)
362                 return FALSE;
363
364         return tc1->context->class_inst == tc2->context->class_inst &&
365                 tc1->context->method_inst == tc2->context->method_inst;
366 }
367
368 /*
369  * mono_helper_get_rgctx_other_ptr:
370  * @caller_class: the klass of the calling method
371  * @rgctx: the runtime generic context
372  * @token: the token which to look up
373  * @token_source: what kind of item the token is for
374  * @rgctx_type: the kind of value requested
375  *
376  * Is called from method to look up a token for a given runtime
377  * generic sharing context and return some particular information
378  * about the looked up class (the class itself, the vtable or the
379  * static_data pointer).
380  */
381 gpointer
382 mono_helper_get_rgctx_other_ptr (MonoClass *caller_class, MonoRuntimeGenericContext *rgctx,
383         guint32 token, guint32 token_source, guint32 rgctx_type, gint32 rgctx_index)
384 {
385         MonoImage *image = caller_class->image;
386         int depth = caller_class->idepth;
387         MonoRuntimeGenericSuperInfo *super_info = &((MonoRuntimeGenericSuperInfo*)rgctx)[-depth];
388         MonoClass *klass = super_info->klass;
389         MonoClass *result = NULL;
390         MonoTokenAndContext tc = { token, &klass->generic_class->context };
391         gpointer rgctx_result_ptr, result_ptr;
392
393         mono_loader_lock ();
394
395         generic_class_lookups++;
396
397         if (!image->generic_class_cache) {
398                 image->generic_class_cache = g_hash_table_new ((GHashFunc)token_context_hash,
399                         (GCompareFunc)token_context_equal);
400         }
401
402         result = g_hash_table_lookup (image->generic_class_cache, &tc);
403
404         mono_loader_unlock ();
405
406         if (!result) {
407                 generic_class_lookup_failures++;
408
409                 switch (token_source) {
410                 case MINI_TOKEN_SOURCE_FIELD: {
411                         MonoClassField *field = mono_field_from_token (image, token, &result, &klass->generic_class->context);
412
413                         g_assert (field);
414                         break;
415                 }
416                 case MINI_TOKEN_SOURCE_CLASS:
417                         result = mono_class_get_full (image, token, &klass->generic_class->context);
418                         break;
419                 case MINI_TOKEN_SOURCE_METHOD: {
420                         MonoMethod *cmethod = mono_get_method_full (image, token, NULL,
421                                 &klass->generic_class->context);
422                         result = cmethod->klass;
423                         break;
424                 }
425                 default :
426                         g_assert_not_reached ();
427                 }
428
429                 g_assert (result);
430
431                 mono_class_init (result);
432
433                 mono_loader_lock ();
434
435                 /*
436                  * In the meantime another thread might have put this class in
437                  * the cache, so check again.
438                  */
439                 if (!g_hash_table_lookup (image->generic_class_cache, &tc)) {
440                         MonoTokenAndContext *tcp = (MonoTokenAndContext*) mono_mempool_alloc0 (image->mempool,
441                                 sizeof (MonoTokenAndContext));
442
443                         *tcp = tc;
444
445                         g_hash_table_insert (image->generic_class_cache, tcp, result);
446                 }
447
448                 mono_loader_unlock ();
449         }
450
451         g_assert (result);
452
453         switch (rgctx_type) {
454         case MONO_RGCTX_INFO_KLASS:
455                 result_ptr = result;
456                 break;
457         case MONO_RGCTX_INFO_STATIC_DATA: {
458                 MonoVTable *vtable = mono_class_vtable (rgctx->domain, result);
459                 result_ptr = vtable->data;
460                 break;
461         }
462         case MONO_RGCTX_INFO_VTABLE:
463                 result_ptr = mono_class_vtable (rgctx->domain, result);
464                 break;
465         default:
466                 g_assert_not_reached ();
467         }
468
469         g_assert (rgctx_index >= 0);
470         if (rgctx_index < MONO_RGCTX_MAX_OTHER_INFOS) {
471                 rgctx_result_ptr = rgctx->other_infos [rgctx_index];
472         } else {
473                 g_assert (rgctx->extra_other_infos);
474
475                 rgctx_result_ptr = rgctx->extra_other_infos [rgctx_index - MONO_RGCTX_MAX_OTHER_INFOS];
476         }
477
478         g_assert (rgctx_result_ptr == result);
479
480         return result_ptr;
481 }
482
483 /*
484  * mono_generic_sharing_init:
485  *
486  * Register the generic sharing counters.
487  */
488 void
489 mono_generic_sharing_init (void)
490 {
491         mono_counters_register ("Generic class lookups", MONO_COUNTER_GENERICS | MONO_COUNTER_INT,
492                         &generic_class_lookups);
493         mono_counters_register ("Generic class lookup failures", MONO_COUNTER_GENERICS | MONO_COUNTER_INT,
494                         &generic_class_lookup_failures);
495 }