2008-05-26 Zoltan Varga <vargaz@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->is_generic)
35                 return &(mono_method_get_generic_container (method)->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->is_generic) {
141                         g_assert (mono_method_get_generic_container (inflated->declaring)->type_params);
142
143                         if (mono_method_get_generic_container (inflated->declaring)->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 /*
284  * mono_class_generic_class_relation:
285  * @klass: the class to be investigated
286  * @method_klass: the reference class
287  * @generic_context: the generic context of method_klass
288  * @arg_num: where a value will be returned
289  *
290  * Discovers and returns the relation of klass with reference to
291  * method_klass.  This can either be MINI_GENERIC_CLASS_RELATION_SELF,
292  * meaning that klass is the same as method_klass,
293  * MINI_GENERIC_CLASS_RELATION_ARGUMENT, meaning that klass is one of
294  * the type arguments of method_klass, or otherwise
295  * MINI_GENERIC_CLASS_RELATION_OTHER.  In the case of
296  * MINI_GENERIC_CLASS_RELATION_ARGUMENT the number of the argument is
297  * returned in *arg_num.
298  */
299 int
300 mono_class_generic_class_relation (MonoClass *klass, int info_type, MonoClass *method_klass,
301         MonoGenericContext *generic_context, int *arg_num)
302 {
303         int i = mono_class_lookup_or_register_other_info (method_klass, &klass->byval_arg, info_type, generic_context);
304
305         if (arg_num)
306                 *arg_num = i;
307
308         return MINI_GENERIC_CLASS_RELATION_OTHER_TABLE;
309 }
310
311 typedef struct
312 {
313         guint32 token;
314         MonoGenericContext *context;
315 } MonoTokenAndContext;
316
317 static guint
318 token_context_hash (MonoTokenAndContext *tc)
319 {
320         return (guint)((gulong)tc->token | (gulong)tc->context->class_inst | (gulong)tc->context->method_inst);
321 }
322
323 static gboolean
324 token_context_equal (MonoTokenAndContext *tc1, MonoTokenAndContext *tc2)
325 {
326         if (tc1->token != tc2->token)
327                 return FALSE;
328
329         return tc1->context->class_inst == tc2->context->class_inst &&
330                 tc1->context->method_inst == tc2->context->method_inst;
331 }
332
333 /*
334  * mono_helper_get_rgctx_other_ptr:
335  * @caller_class: the klass of the calling method
336  * @vtable: the vtable with the runtime generic context
337  * @token: the token which to look up
338  * @token_source: what kind of item the token is for
339  * @rgctx_type: the kind of value requested
340  *
341  * Is called from method to look up a token for a given runtime
342  * generic sharing context and return some particular information
343  * about the looked up class (the class itself, the vtable or the
344  * static_data pointer).
345  */
346 gpointer
347 mono_helper_get_rgctx_other_ptr (MonoClass *caller_class, MonoVTable *vtable,
348         guint32 token, guint32 token_source, guint32 rgctx_type, gint32 rgctx_index)
349 {
350         MonoImage *image = caller_class->image;
351         MonoClass *klass = vtable->klass;
352         MonoClass *result = NULL;
353         MonoTokenAndContext tc = { token, &klass->generic_class->context };
354         gpointer result_ptr;
355
356         mono_loader_lock ();
357
358         generic_class_lookups++;
359
360         if (!image->generic_class_cache) {
361                 image->generic_class_cache = g_hash_table_new ((GHashFunc)token_context_hash,
362                         (GCompareFunc)token_context_equal);
363         }
364
365         result = g_hash_table_lookup (image->generic_class_cache, &tc);
366
367         mono_loader_unlock ();
368
369         if (!result) {
370                 generic_class_lookup_failures++;
371
372                 switch (token_source) {
373                 case MINI_TOKEN_SOURCE_FIELD: {
374                         MonoClassField *field = mono_field_from_token (image, token, &result, &klass->generic_class->context);
375
376                         g_assert (field);
377                         break;
378                 }
379                 case MINI_TOKEN_SOURCE_CLASS:
380                         result = mono_class_get_full (image, token, &klass->generic_class->context);
381                         break;
382                 case MINI_TOKEN_SOURCE_METHOD: {
383                         MonoMethod *cmethod = mono_get_method_full (image, token, NULL,
384                                 &klass->generic_class->context);
385                         result = cmethod->klass;
386                         break;
387                 }
388                 default :
389                         g_assert_not_reached ();
390                 }
391
392                 g_assert (result);
393
394                 mono_class_init (result);
395
396                 mono_loader_lock ();
397
398                 /*
399                  * In the meantime another thread might have put this class in
400                  * the cache, so check again.
401                  */
402                 if (!g_hash_table_lookup (image->generic_class_cache, &tc)) {
403                         MonoTokenAndContext *tcp = (MonoTokenAndContext*) mono_mempool_alloc0 (image->mempool,
404                                 sizeof (MonoTokenAndContext));
405
406                         *tcp = tc;
407
408                         g_hash_table_insert (image->generic_class_cache, tcp, result);
409                 }
410
411                 mono_loader_unlock ();
412         }
413
414         g_assert (result);
415
416         switch (rgctx_type) {
417         case MONO_RGCTX_INFO_KLASS:
418                 result_ptr = result;
419                 break;
420         case MONO_RGCTX_INFO_STATIC_DATA: {
421                 MonoVTable *result_vtable = mono_class_vtable (vtable->domain, result);
422                 result_ptr = result_vtable->data;
423                 break;
424         }
425         case MONO_RGCTX_INFO_VTABLE:
426                 result_ptr = mono_class_vtable (vtable->domain, result);
427                 break;
428         default:
429                 g_assert_not_reached ();
430         }
431
432         return result_ptr;
433 }
434
435 /*
436  * mono_generic_sharing_init:
437  *
438  * Register the generic sharing counters.
439  */
440 void
441 mono_generic_sharing_init (void)
442 {
443         mono_counters_register ("Generic class lookups", MONO_COUNTER_GENERICS | MONO_COUNTER_INT,
444                         &generic_class_lookups);
445         mono_counters_register ("Generic class lookup failures", MONO_COUNTER_GENERICS | MONO_COUNTER_INT,
446                         &generic_class_lookup_failures);
447 }