2008-08-29 Geoff Norton <gnorton@novell.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 /*
18  * mono_get_generic_context_from_code:
19  *
20  *   Return the runtime generic context belonging to the method whose native code
21  * contains CODE.
22  */
23 MonoGenericSharingContext*
24 mono_get_generic_context_from_code (guint8 *code)
25 {
26         MonoJitInfo *jit_info = mono_jit_info_table_find (mono_domain_get (), (char*)code);
27
28         g_assert (jit_info);
29
30         return mono_jit_info_get_generic_sharing_context (jit_info);
31 }
32
33 /*
34  * mini_method_get_context:
35  * @method: a method
36  *
37  * Returns the generic context of a method or NULL if it doesn't have
38  * one.  For an inflated method that's the context stored in the
39  * method.  Otherwise it's in the method's generic container or in the
40  * generic container of the method's class.
41  */
42 MonoGenericContext*
43 mini_method_get_context (MonoMethod *method)
44 {
45         if (method->is_inflated)
46                 return mono_method_get_context (method);
47         if (method->is_generic)
48                 return &(mono_method_get_generic_container (method)->context);
49         if (method->klass->generic_container)
50                 return &method->klass->generic_container->context;
51         return NULL;
52 }
53
54 /*
55  * mono_method_check_context_used:
56  * @method: a method
57  *
58  * Checks whether the method's generic context uses a type variable.
59  * Returns an int with the bits MONO_GENERIC_CONTEXT_USED_CLASS and
60  * MONO_GENERIC_CONTEXT_USED_METHOD set to reflect whether the
61  * context's class or method instantiation uses type variables.
62  */
63 int
64 mono_method_check_context_used (MonoMethod *method)
65 {
66         MonoGenericContext *method_context = mini_method_get_context (method);
67         int context_used;
68
69         if (!method_context)
70                 return 0;
71
72         context_used = mono_generic_context_check_used (method_context);
73         context_used |= mono_class_check_context_used (method->klass);
74
75         return context_used;
76 }
77
78 static gboolean
79 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars)
80 {
81         int i;
82
83         for (i = 0; i < inst->type_argc; ++i) {
84                 MonoType *type = inst->type_argv [i];
85                 int type_type;
86
87                 if (MONO_TYPE_IS_REFERENCE (type))
88                         continue;
89
90                 type_type = mono_type_get_type (type);
91                 if (allow_type_vars && (type_type == MONO_TYPE_VAR || type_type == MONO_TYPE_MVAR))
92                         continue;
93
94                 return FALSE;
95         }
96
97         return TRUE;
98 }
99
100 /*
101  * mono_generic_context_is_sharable:
102  * @context: a generic context
103  *
104  * Returns whether the generic context is sharable.  A generic context
105  * is sharable iff all of its type arguments are reference type.
106  */
107 gboolean
108 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
109 {
110         g_assert (context->class_inst || context->method_inst);
111
112         if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars))
113                 return FALSE;
114
115         if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars))
116                 return FALSE;
117
118         return TRUE;
119 }
120
121 /*
122  * mono_method_is_generic_impl:
123  * @method: a method
124  *
125  * Returns whether the method is either generic or part of a generic
126  * class.
127  */
128 gboolean
129 mono_method_is_generic_impl (MonoMethod *method)
130 {
131         if (method->is_inflated) {
132                 g_assert (method->wrapper_type == MONO_WRAPPER_NONE);
133                 return TRUE;
134         }
135         /* We don't treat wrappers as generic code, i.e., we never
136            apply generic sharing to them.  This is especially
137            important for static rgctx invoke wrappers, which only work
138            if not compiled with sharing. */
139         if (method->wrapper_type != MONO_WRAPPER_NONE)
140                 return FALSE;
141         if (method->klass->generic_container)
142                 return TRUE;
143         return FALSE;
144 }
145
146 /*
147  * mono_method_is_generic_sharable_impl:
148  * @method: a method
149  * @allow_type_vars: whether to regard type variables as reference types
150  *
151  * Returns TRUE iff the method is inflated or part of an inflated
152  * class, its context is sharable and it has no constraints on its
153  * type parameters.  Otherwise returns FALSE.
154  */
155 gboolean
156 mono_method_is_generic_sharable_impl (MonoMethod *method, gboolean allow_type_vars)
157 {
158         if (!mono_method_is_generic_impl (method))
159                 return FALSE;
160
161         if (method->is_inflated) {
162                 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
163                 MonoGenericContext *context = &inflated->context;
164
165                 if (!mono_generic_context_is_sharable (context, allow_type_vars))
166                         return FALSE;
167
168                 g_assert (inflated->declaring);
169
170                 if (inflated->declaring->is_generic) {
171                         g_assert (mono_method_get_generic_container (inflated->declaring)->type_params);
172
173                         if (mono_method_get_generic_container (inflated->declaring)->type_params->constraints)
174                                 return FALSE;
175                 }
176         }
177
178         if (method->klass->generic_class) {
179                 if (!mono_generic_context_is_sharable (&method->klass->generic_class->context, allow_type_vars))
180                         return FALSE;
181
182                 g_assert (method->klass->generic_class->container_class &&
183                                 method->klass->generic_class->container_class->generic_container &&
184                                 method->klass->generic_class->container_class->generic_container->type_params);
185
186                 if (method->klass->generic_class->container_class->generic_container->type_params->constraints)
187                         return FALSE;
188         }
189
190         if (method->klass->generic_container && !allow_type_vars)
191                 return FALSE;
192
193         return TRUE;
194 }
195
196 static gboolean
197 generic_inst_equal (MonoGenericInst *inst1, MonoGenericInst *inst2)
198 {
199         int i;
200
201         if (!inst1) {
202                 g_assert (!inst2);
203                 return TRUE;
204         }
205
206         g_assert (inst2);
207
208         if (inst1->type_argc != inst2->type_argc)
209                 return FALSE;
210
211         for (i = 0; i < inst1->type_argc; ++i)
212                 if (!mono_metadata_type_equal (inst1->type_argv [i], inst2->type_argv [i]))
213                         return FALSE;
214
215         return TRUE;
216 }
217
218 /*
219  * mono_generic_context_equal_deep:
220  * @context1: a generic context
221  * @context2: a generic context
222  *
223  * Returns whether context1's type arguments are equal to context2's
224  * type arguments.
225  */
226 gboolean
227 mono_generic_context_equal_deep (MonoGenericContext *context1, MonoGenericContext *context2)
228 {
229         return generic_inst_equal (context1->class_inst, context2->class_inst) &&
230                 generic_inst_equal (context1->method_inst, context2->method_inst);
231 }
232
233 /*
234  * mini_class_get_container_class:
235  * @class: a generic class
236  *
237  * Returns the class's container class, which is the class itself if
238  * it doesn't have generic_class set.
239  */
240 MonoClass*
241 mini_class_get_container_class (MonoClass *class)
242 {
243         if (class->generic_class)
244                 return class->generic_class->container_class;
245
246         g_assert (class->generic_container);
247         return class;
248 }
249
250 /*
251  * mini_class_get_context:
252  * @class: a generic class
253  *
254  * Returns the class's generic context.
255  */
256 MonoGenericContext*
257 mini_class_get_context (MonoClass *class)
258 {
259         if (class->generic_class)
260                 return &class->generic_class->context;
261
262         g_assert (class->generic_container);
263         return &class->generic_container->context;
264 }
265
266 /*
267  * mini_get_basic_type_from_generic:
268  * @gsctx: a generic sharing context
269  * @type: a type
270  *
271  * Returns a closed type corresponding to the possibly open type
272  * passed to it.
273  */
274 MonoType*
275 mini_get_basic_type_from_generic (MonoGenericSharingContext *gsctx, MonoType *type)
276 {
277         if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR))
278                 g_assert (gsctx);
279
280         return mono_type_get_basic_type_from_generic (type);
281 }
282
283 /*
284  * mini_type_get_underlying_type:
285  *
286  *   Return the underlying type of TYPE, taking into account enums and generic
287  * sharing.
288  */
289 MonoType*
290 mini_type_get_underlying_type (MonoGenericSharingContext *gsctx, MonoType *type)
291 {
292         return mono_type_get_basic_type_from_generic (mono_type_get_underlying_type (type));
293 }
294
295 /*
296  * mini_type_stack_size:
297  * @gsctx: a generic sharing context
298  * @t: a type
299  * @align: Pointer to an int for returning the alignment
300  *
301  * Returns the type's stack size and the alignment in *align.  The
302  * type is allowed to be open.
303  */
304 int
305 mini_type_stack_size (MonoGenericSharingContext *gsctx, MonoType *t, int *align)
306 {
307         gboolean allow_open = TRUE;
308
309         // FIXME: Some callers might not pass in a gsctx
310         //allow_open = gsctx != NULL;
311         return mono_type_stack_size_internal (t, align, allow_open);
312 }
313
314 /*
315  * mini_type_stack_size_full:
316  *
317  *   Same as mini_type_stack_size, but handle pinvoke data types as well.
318  */
319 int
320 mini_type_stack_size_full (MonoGenericSharingContext *gsctx, MonoType *t, guint32 *align, gboolean pinvoke)
321 {
322         int size;
323
324         if (pinvoke) {
325                 size = mono_type_native_stack_size (t, align);
326         } else {
327                 int ialign;
328
329                 if (align) {
330                         size = mini_type_stack_size (gsctx, t, &ialign);
331                         *align = ialign;
332                 } else {
333                         size = mini_type_stack_size (gsctx, t, NULL);
334                 }
335         }
336         
337         return size;
338 }
339
340 /*
341  * mono_generic_sharing_init:
342  *
343  * Register the generic sharing counters.
344  */
345 void
346 mono_generic_sharing_init (void)
347 {
348 }