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->generic_container)
35 return &method->generic_container->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->generic_container) {
141 g_assert (inflated->declaring->generic_container->type_params);
143 if (inflated->declaring->generic_container->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 inflated_type_is_equal_to_class (MonoType *inflated_type, MonoClass *klass)
286 return klass == mono_class_from_mono_type (inflated_type);
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
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.
306 mono_class_generic_class_relation (MonoClass *klass, int info_type, MonoClass *method_klass,
307 MonoGenericContext *generic_context, int *arg_num)
309 MonoRuntimeGenericContextTemplate *rgctx_template =
310 mono_class_get_runtime_generic_context_template (method_klass);
313 /* Reflection types can only be handled in the extensible
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;
320 if (arg_info == NULL)
323 inflated_arg = mono_class_inflate_generic_type(arg_info, generic_context);
325 if (inflated_type_is_equal_to_class (inflated_arg, klass)) {
328 return MINI_GENERIC_CLASS_RELATION_ARGUMENT;
332 if (!klass->generic_class && !klass->generic_container)
333 g_assert_not_reached ();
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;
340 i = mono_class_lookup_or_register_other_info (method_klass, &klass->byval_arg, info_type, generic_context);
343 return MINI_GENERIC_CLASS_RELATION_OTHER_TABLE;
349 MonoGenericContext *context;
350 } MonoTokenAndContext;
353 token_context_hash (MonoTokenAndContext *tc)
355 return (guint)((gulong)tc->token | (gulong)tc->context->class_inst | (gulong)tc->context->method_inst);
359 token_context_equal (MonoTokenAndContext *tc1, MonoTokenAndContext *tc2)
361 if (tc1->token != tc2->token)
364 return tc1->context->class_inst == tc2->context->class_inst &&
365 tc1->context->method_inst == tc2->context->method_inst;
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
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).
382 mono_helper_get_rgctx_other_ptr (MonoClass *caller_class, MonoRuntimeGenericContext *rgctx,
383 guint32 token, guint32 token_source, guint32 rgctx_type, gint32 rgctx_index)
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;
395 generic_class_lookups++;
397 if (!image->generic_class_cache) {
398 image->generic_class_cache = g_hash_table_new ((GHashFunc)token_context_hash,
399 (GCompareFunc)token_context_equal);
402 result = g_hash_table_lookup (image->generic_class_cache, &tc);
404 mono_loader_unlock ();
407 generic_class_lookup_failures++;
409 switch (token_source) {
410 case MINI_TOKEN_SOURCE_FIELD: {
411 MonoClassField *field = mono_field_from_token (image, token, &result, &klass->generic_class->context);
416 case MINI_TOKEN_SOURCE_CLASS:
417 result = mono_class_get_full (image, token, &klass->generic_class->context);
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;
426 g_assert_not_reached ();
431 mono_class_init (result);
436 * In the meantime another thread might have put this class in
437 * the cache, so check again.
439 if (!g_hash_table_lookup (image->generic_class_cache, &tc)) {
440 MonoTokenAndContext *tcp = (MonoTokenAndContext*) mono_mempool_alloc0 (image->mempool,
441 sizeof (MonoTokenAndContext));
445 g_hash_table_insert (image->generic_class_cache, tcp, result);
448 mono_loader_unlock ();
453 switch (rgctx_type) {
454 case MONO_RGCTX_INFO_KLASS:
457 case MONO_RGCTX_INFO_STATIC_DATA: {
458 MonoVTable *vtable = mono_class_vtable (rgctx->domain, result);
459 result_ptr = vtable->data;
462 case MONO_RGCTX_INFO_VTABLE:
463 result_ptr = mono_class_vtable (rgctx->domain, result);
466 g_assert_not_reached ();
469 g_assert (rgctx_index >= 0);
470 if (rgctx_index < MONO_RGCTX_MAX_OTHER_INFOS) {
471 rgctx_result_ptr = rgctx->other_infos [rgctx_index];
473 g_assert (rgctx->extra_other_infos);
475 rgctx_result_ptr = rgctx->extra_other_infos [rgctx_index - MONO_RGCTX_MAX_OTHER_INFOS];
478 g_assert (rgctx_result_ptr == result);
484 * mono_generic_sharing_init:
486 * Register the generic sharing counters.
489 mono_generic_sharing_init (void)
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);