2 * generic-sharing.c: Support functions for generic sharing.
5 * Mark Probst (mark.probst@gmail.com)
7 * (C) 2007 Novell, Inc.
12 #include <mono/metadata/class.h>
13 #include <mono/utils/mono-counters.h>
17 static int generic_class_lookups = 0;
18 static int generic_class_lookup_failures = 0;
21 * mini_method_get_context:
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.
30 mini_method_get_context (MonoMethod *method)
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;
42 * mono_method_check_context_used:
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.
51 mono_method_check_context_used (MonoMethod *method)
53 MonoGenericContext *method_context = mini_method_get_context (method);
58 return mono_generic_context_check_used (method_context);
62 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars)
66 for (i = 0; i < inst->type_argc; ++i) {
67 MonoType *type = inst->type_argv [i];
70 if (MONO_TYPE_IS_REFERENCE (type))
73 type_type = mono_type_get_type (type);
74 if (allow_type_vars && (type_type == MONO_TYPE_VAR || type_type == MONO_TYPE_MVAR))
84 * mono_generic_context_is_sharable:
85 * @context: a generic context
87 * Returns whether the generic context is sharable. A generic context
88 * is sharable iff all of its type arguments are reference type.
91 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
93 g_assert (context->class_inst || context->method_inst);
95 if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars))
98 if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars))
105 * mono_method_is_generic_impl:
108 * Returns whether the method is either inflated or part of an
112 mono_method_is_generic_impl (MonoMethod *method)
114 return method->klass->generic_class != NULL && method->is_inflated;
118 * mono_method_is_generic_sharable_impl:
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.
126 mono_method_is_generic_sharable_impl (MonoMethod *method)
128 if (!mono_method_is_generic_impl (method))
131 if (method->is_inflated) {
132 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
133 MonoGenericContext *context = &inflated->context;
135 if (!mono_generic_context_is_sharable (context, FALSE))
138 g_assert (inflated->declaring);
140 if (inflated->declaring->is_generic) {
141 g_assert (mono_method_get_generic_container (inflated->declaring)->type_params);
143 if (mono_method_get_generic_container (inflated->declaring)->type_params->constraints)
148 if (method->klass->generic_class) {
149 if (!mono_generic_context_is_sharable (&method->klass->generic_class->context, FALSE))
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);
156 if (method->klass->generic_class->container_class->generic_container->type_params->constraints)
164 generic_inst_equal (MonoGenericInst *inst1, MonoGenericInst *inst2)
175 if (inst1->type_argc != inst2->type_argc)
178 for (i = 0; i < inst1->type_argc; ++i)
179 if (!mono_metadata_type_equal (inst1->type_argv [i], inst2->type_argv [i]))
186 * mono_generic_context_equal_deep:
187 * @context1: a generic context
188 * @context2: a generic context
190 * Returns whether context1's type arguments are equal to context2's
194 mono_generic_context_equal_deep (MonoGenericContext *context1, MonoGenericContext *context2)
196 return generic_inst_equal (context1->class_inst, context2->class_inst) &&
197 generic_inst_equal (context1->method_inst, context2->method_inst);
201 * mini_class_get_container_class:
202 * @class: a generic class
204 * Returns the class's container class, which is the class itself if
205 * it doesn't have generic_class set.
208 mini_class_get_container_class (MonoClass *class)
210 if (class->generic_class)
211 return class->generic_class->container_class;
213 g_assert (class->generic_container);
218 * mini_class_get_context:
219 * @class: a generic class
221 * Returns the class's generic context.
224 mini_class_get_context (MonoClass *class)
226 if (class->generic_class)
227 return &class->generic_class->context;
229 g_assert (class->generic_container);
230 return &class->generic_container->context;
234 * mini_get_basic_type_from_generic:
235 * @gsctx: a generic sharing context
238 * Returns a closed type corresponding to the possibly open type
242 mini_get_basic_type_from_generic (MonoGenericSharingContext *gsctx, MonoType *type)
244 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR))
247 return mono_type_get_basic_type_from_generic (type);
251 * mini_type_stack_size:
252 * @gsctx: a generic sharing context
254 * @align: Pointer to an int for returning the alignment
256 * Returns the type's stack size and the alignment in *align. The
257 * type is allowed to be open.
260 mini_type_stack_size (MonoGenericSharingContext *gsctx, MonoType *t, int *align)
262 return mono_type_stack_size_internal (t, align, gsctx != NULL);
266 * mono_method_get_declaring_generic_method:
267 * @method: an inflated method
269 * Returns an inflated method's declaring method.
272 mono_method_get_declaring_generic_method (MonoMethod *method)
274 MonoMethodInflated *inflated;
276 g_assert (method->is_inflated);
278 inflated = (MonoMethodInflated*)method;
280 return inflated->declaring;
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
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.
300 mono_class_generic_class_relation (MonoClass *klass, int info_type, MonoClass *method_klass,
301 MonoGenericContext *generic_context, int *arg_num)
303 int i = mono_class_lookup_or_register_other_info (method_klass, &klass->byval_arg, info_type, generic_context);
308 return MINI_GENERIC_CLASS_RELATION_OTHER_TABLE;
314 MonoGenericContext *context;
315 } MonoTokenAndContext;
318 token_context_hash (MonoTokenAndContext *tc)
320 return (guint)((gulong)tc->token | (gulong)tc->context->class_inst | (gulong)tc->context->method_inst);
324 token_context_equal (MonoTokenAndContext *tc1, MonoTokenAndContext *tc2)
326 if (tc1->token != tc2->token)
329 return tc1->context->class_inst == tc2->context->class_inst &&
330 tc1->context->method_inst == tc2->context->method_inst;
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
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).
347 mono_helper_get_rgctx_other_ptr (MonoClass *caller_class, MonoVTable *vtable,
348 guint32 token, guint32 token_source, guint32 rgctx_type, gint32 rgctx_index)
350 MonoImage *image = caller_class->image;
351 MonoClass *klass = vtable->klass;
352 MonoClass *result = NULL;
353 MonoTokenAndContext tc = { token, &klass->generic_class->context };
358 generic_class_lookups++;
360 if (!image->generic_class_cache) {
361 image->generic_class_cache = g_hash_table_new ((GHashFunc)token_context_hash,
362 (GCompareFunc)token_context_equal);
365 result = g_hash_table_lookup (image->generic_class_cache, &tc);
367 mono_loader_unlock ();
370 generic_class_lookup_failures++;
372 switch (token_source) {
373 case MINI_TOKEN_SOURCE_FIELD: {
374 MonoClassField *field = mono_field_from_token (image, token, &result, &klass->generic_class->context);
379 case MINI_TOKEN_SOURCE_CLASS:
380 result = mono_class_get_full (image, token, &klass->generic_class->context);
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;
389 g_assert_not_reached ();
394 mono_class_init (result);
399 * In the meantime another thread might have put this class in
400 * the cache, so check again.
402 if (!g_hash_table_lookup (image->generic_class_cache, &tc)) {
403 MonoTokenAndContext *tcp = (MonoTokenAndContext*) mono_mempool_alloc0 (image->mempool,
404 sizeof (MonoTokenAndContext));
408 g_hash_table_insert (image->generic_class_cache, tcp, result);
411 mono_loader_unlock ();
416 switch (rgctx_type) {
417 case MONO_RGCTX_INFO_KLASS:
420 case MONO_RGCTX_INFO_STATIC_DATA: {
421 MonoVTable *result_vtable = mono_class_vtable (vtable->domain, result);
422 result_ptr = result_vtable->data;
425 case MONO_RGCTX_INFO_VTABLE:
426 result_ptr = mono_class_vtable (vtable->domain, result);
429 g_assert_not_reached ();
436 * mono_generic_sharing_init:
438 * Register the generic sharing counters.
441 mono_generic_sharing_init (void)
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);