* Author:
* Mark Probst (mark.probst@gmail.com)
*
- * (C) 2007-2008 Novell, Inc.
+ * Copyright 2007-2009 Novell, Inc (http://www.novell.com)
*/
#include <config.h>
#include <string.h>
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
#ifdef _MSC_VER
#include <glib.h>
#endif
#include <mono/utils/mono-membar.h>
+#include <mono/utils/mono-counters.h>
#include "metadata-internals.h"
#include "class.h"
#include "class-internals.h"
+#include "marshal.h"
+#include "debug-helpers.h"
+#include "tabledefs.h"
+#include "mempool-internals.h"
static int
type_check_context_used (MonoType *type, gboolean recursive)
{
- int t = mono_type_get_type (type);
- int context_used = 0;
-
- if (t == MONO_TYPE_VAR)
- context_used |= MONO_GENERIC_CONTEXT_USED_CLASS;
- else if (t == MONO_TYPE_MVAR)
- context_used |= MONO_GENERIC_CONTEXT_USED_METHOD;
- else if (recursive) {
- if (t == MONO_TYPE_CLASS)
- context_used |= mono_class_check_context_used (mono_type_get_class (type));
- else if (t == MONO_TYPE_GENERICINST) {
+ switch (mono_type_get_type (type)) {
+ case MONO_TYPE_VAR:
+ return MONO_GENERIC_CONTEXT_USED_CLASS;
+ case MONO_TYPE_MVAR:
+ return MONO_GENERIC_CONTEXT_USED_METHOD;
+ case MONO_TYPE_SZARRAY:
+ return mono_class_check_context_used (mono_type_get_class (type));
+ case MONO_TYPE_ARRAY:
+ return mono_class_check_context_used (mono_type_get_array_type (type)->eklass);
+ case MONO_TYPE_CLASS:
+ if (recursive)
+ return mono_class_check_context_used (mono_type_get_class (type));
+ else
+ return 0;
+ case MONO_TYPE_GENERICINST:
+ if (recursive) {
MonoGenericClass *gclass = type->data.generic_class;
- context_used |= mono_generic_context_check_used (&gclass->context);
g_assert (gclass->container_class->generic_container);
+ return mono_generic_context_check_used (&gclass->context);
+ } else {
+ return 0;
}
+ default:
+ return 0;
}
-
- return context_used;
}
static int
}
/*
- * Guards the two global rgctx (template) hash tables and all rgctx
- * templates.
+ * LOCKING: loader lock
*/
-static CRITICAL_SECTION templates_mutex;
+static MonoRuntimeGenericContextOtherInfoTemplate*
+get_other_info_templates (MonoRuntimeGenericContextTemplate *template, int type_argc)
+{
+ g_assert (type_argc >= 0);
+ if (type_argc == 0)
+ return template->other_infos;
+ return g_slist_nth_data (template->method_templates, type_argc - 1);
+}
+/*
+ * LOCKING: loader lock
+ */
static void
-templates_lock (void)
+set_other_info_templates (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
+ MonoRuntimeGenericContextOtherInfoTemplate *oti)
{
- static gboolean inited = FALSE;
-
- if (!inited) {
- mono_loader_lock ();
- if (!inited) {
- InitializeCriticalSection (&templates_mutex);
- inited = TRUE;
+ g_assert (type_argc >= 0);
+ if (type_argc == 0)
+ template->other_infos = oti;
+ else {
+ int length = g_slist_length (template->method_templates);
+ GSList *list;
+
+ /* FIXME: quadratic! */
+ while (length < type_argc) {
+ template->method_templates = g_slist_append_image (image, template->method_templates, NULL);
+ length++;
}
- mono_loader_unlock ();
- }
- EnterCriticalSection (&templates_mutex);
+ list = g_slist_nth (template->method_templates, type_argc - 1);
+ g_assert (list);
+ list->data = oti;
+ }
}
-static void
-templates_unlock (void)
+/*
+ * LOCKING: loader lock
+ */
+static int
+template_get_max_argc (MonoRuntimeGenericContextTemplate *template)
{
- LeaveCriticalSection (&templates_mutex);
+ return g_slist_length (template->method_templates);
}
/*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static MonoRuntimeGenericContextOtherInfoTemplate*
-rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate *template, int slot)
+rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate *template, int type_argc, int slot)
{
int i;
MonoRuntimeGenericContextOtherInfoTemplate *oti;
g_assert (slot >= 0);
- for (oti = template->other_infos, i = 0; i < slot; oti = oti->next, ++i) {
+ for (oti = get_other_info_templates (template, type_argc), i = 0; i < slot; oti = oti->next, ++i) {
if (!oti)
return NULL;
}
}
/*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static int
-rgctx_template_num_other_infos (MonoRuntimeGenericContextTemplate *template)
+rgctx_template_num_other_infos (MonoRuntimeGenericContextTemplate *template, int type_argc)
{
MonoRuntimeGenericContextOtherInfoTemplate *oti;
int i;
- for (i = 0, oti = template->other_infos; oti; ++i, oti = oti->next)
+ for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next)
;
return i;
}
-/* Maps from uninstantiated generic classes to GSLists's of
- * MonoVTable's of classes that are instantiations of the key class.
- *
- * LOCKING: templates lock
- */
-static GHashTable *generic_class_rgctx_hash;
-
/* Maps from uninstantiated generic classes to GList's of
* uninstantiated generic classes whose parent is the key class or an
* instance of the key class.
*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static GHashTable *generic_subclass_hash;
-static void
-list_hash_table_insert (GHashTable **hash, gpointer key, gpointer value)
-{
- GSList *list;
-
- if (!*hash)
- *hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
-
- list = g_hash_table_lookup (*hash, key);
- g_hash_table_insert (*hash, key, g_slist_prepend (list, value));
-}
-
/*
* LOCKING: templates lock
*/
}
/*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static MonoRuntimeGenericContextTemplate*
class_lookup_rgctx_template (MonoClass *class)
}
/*
- * LOCKING: templates lock
- */
-static void
-register_open_instance (MonoClass *class)
-{
- g_assert (class->generic_class && class->generic_class->container_class);
- g_assert (class_lookup_rgctx_template (class));
-
- list_hash_table_insert (&class->image->generic_class_open_instances_hash, class->generic_class->container_class, class);
-}
-
-/*
- * LOCKING: templates lock
- */
-static void
-register_rgctx_vtable (MonoVTable *vtable)
-{
- MonoClass *class = vtable->klass;
-
- if (class->generic_class)
- class = class->generic_class->container_class;
-
- //g_print ("registering rgctx %p for class %s (%p)\n", vtable, mono_type_get_full_name (class), class);
-
- list_hash_table_insert (&generic_class_rgctx_hash, class, vtable);
-}
-
-/*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static void
register_generic_subclass (MonoClass *class)
if (!generic_subclass_hash)
return;
- templates_lock ();
+ mono_loader_lock ();
old_hash = generic_subclass_hash;
generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
g_hash_table_foreach (old_hash, (GHFunc)move_subclasses_not_in_image_foreach_func, image);
- templates_unlock ();
+ mono_loader_unlock ();
g_hash_table_destroy (old_hash);
}
-static void
-move_vtables_not_in_domain_foreach_func (MonoClass *class, GSList *list, MonoDomain *domain)
+static MonoRuntimeGenericContextTemplate*
+alloc_template (MonoClass *class)
{
- GSList *new_list = NULL;
- GSList *iter = list;
-
- while (iter) {
- MonoVTable *vtable = iter->data;
+ static gboolean inited = FALSE;
+ static int num_allocted = 0;
+ static int num_bytes = 0;
- if (vtable->domain != domain)
- new_list = g_slist_prepend (new_list, vtable);
+ int size = sizeof (MonoRuntimeGenericContextTemplate);
- iter = iter->next;
+ if (!inited) {
+ mono_counters_register ("RGCTX template num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_allocted);
+ mono_counters_register ("RGCTX template bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_bytes);
+ inited = TRUE;
}
- g_slist_free (list);
+ num_allocted++;
+ num_bytes += size;
- if (new_list)
- g_hash_table_insert (generic_class_rgctx_hash, class, new_list);
+ return mono_image_alloc0 (class->image, size);
}
-/*
- * mono_class_unregister_domain_generic_vtables:
- * @domain: a domain
- *
- * Removes all vtables of the domain from the generic vtables hash.
- * Must be called when a domain is destroyed.
- */
-void
-mono_class_unregister_domain_generic_vtables (MonoDomain *domain)
+static MonoRuntimeGenericContextOtherInfoTemplate*
+alloc_oti (MonoImage *image)
{
- GHashTable *old_hash;
-
- if (!generic_class_rgctx_hash)
- return;
-
- templates_lock ();
+ static gboolean inited = FALSE;
+ static int num_allocted = 0;
+ static int num_bytes = 0;
- old_hash = generic_class_rgctx_hash;
- generic_class_rgctx_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
+ int size = sizeof (MonoRuntimeGenericContextOtherInfoTemplate);
- g_hash_table_foreach (old_hash, (GHFunc)move_vtables_not_in_domain_foreach_func, domain);
+ if (!inited) {
+ mono_counters_register ("RGCTX oti num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_allocted);
+ mono_counters_register ("RGCTX oti bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_bytes);
+ inited = TRUE;
+ }
- templates_unlock ();
+ num_allocted++;
+ num_bytes += size;
- g_hash_table_destroy (old_hash);
+ return mono_image_alloc0 (image, size);
}
+#define MONO_RGCTX_SLOT_USED_MARKER ((gpointer)&mono_defaults.object_class->byval_arg)
+
/*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static void
-rgctx_template_set_other_slot (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int slot,
- gpointer data, int info_type)
+rgctx_template_set_other_slot (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
+ int slot, gpointer data, int info_type)
{
+ static gboolean inited = FALSE;
+ static int num_markers = 0;
+ static int num_data = 0;
+
int i;
- MonoRuntimeGenericContextOtherInfoTemplate **oti;
+ MonoRuntimeGenericContextOtherInfoTemplate *list = get_other_info_templates (template, type_argc);
+ MonoRuntimeGenericContextOtherInfoTemplate **oti = &list;
- g_assert (slot >= 0);
+ if (!inited) {
+ mono_counters_register ("RGCTX oti num markers", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_markers);
+ mono_counters_register ("RGCTX oti num data", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_data);
+ inited = TRUE;
+ }
- mono_loader_lock ();
+ g_assert (slot >= 0);
+ g_assert (data);
- oti = &template->other_infos;
i = 0;
while (i <= slot) {
if (i > 0)
oti = &(*oti)->next;
if (!*oti)
- *oti = mono_mempool_alloc0 (image->mempool, sizeof (MonoRuntimeGenericContextOtherInfoTemplate));
+ *oti = alloc_oti (image);
++i;
}
(*oti)->data = data;
(*oti)->info_type = info_type;
- mono_loader_unlock ();
+ set_other_info_templates (image, template, type_argc, list);
+
+ if (data == MONO_RGCTX_SLOT_USED_MARKER)
+ ++num_markers;
+ else
+ ++num_data;
}
-static gboolean
-include_arg_info (MonoType *type, MonoRuntimeGenericContextTemplate *parent_template)
+/*
+ * mono_method_get_declaring_generic_method:
+ * @method: an inflated method
+ *
+ * Returns an inflated method's declaring method.
+ */
+MonoMethod*
+mono_method_get_declaring_generic_method (MonoMethod *method)
+{
+ MonoMethodInflated *inflated;
+
+ g_assert (method->is_inflated);
+
+ inflated = (MonoMethodInflated*)method;
+
+ return inflated->declaring;
+}
+
+/*
+ * mono_class_get_method_generic:
+ * @klass: a class
+ * @method: a method
+ *
+ * Given a class and a generic method, which has to be of an
+ * instantiation of the same class that klass is an instantiation of,
+ * returns the corresponding method in klass. Example:
+ *
+ * klass is Gen<string>
+ * method is Gen<object>.work<int>
+ *
+ * returns: Gen<string>.work<int>
+ */
+MonoMethod*
+mono_class_get_method_generic (MonoClass *klass, MonoMethod *method)
{
+ MonoMethod *declaring, *m;
int i;
- if (!MONO_TYPE_IS_REFERENCE (type) &&
- mono_type_get_type (type) != MONO_TYPE_VAR &&
- mono_type_get_type (type) != MONO_TYPE_MVAR)
- return FALSE;
+ if (method->is_inflated)
+ declaring = mono_method_get_declaring_generic_method (method);
+ else
+ declaring = method;
+
+ m = NULL;
+ if (klass->generic_class)
+ m = mono_class_get_inflated_method (klass, declaring);
+
+ if (!m) {
+ mono_class_setup_methods (klass);
+ for (i = 0; i < klass->method.count; ++i) {
+ m = klass->methods [i];
+ if (m == declaring)
+ break;
+ if (m->is_inflated && mono_method_get_declaring_generic_method (m) == declaring)
+ break;
+ }
+ if (i >= klass->method.count)
+ return NULL;
+ }
- if (!parent_template)
- return TRUE;
+ if (method != declaring) {
+ MonoGenericContext context;
- for (i = 0; i < parent_template->num_arg_infos; ++i)
- if (type == parent_template->arg_infos [i])
- return FALSE;
+ context.class_inst = NULL;
+ context.method_inst = mono_method_get_context (method)->method_inst;
- return TRUE;
-}
+ m = mono_class_inflate_generic_method (m, &context);
+ }
-#define MONO_RGCTX_SLOT_USED_MARKER ((gpointer)&mono_defaults.object_class->byval_arg)
+ return m;
+}
static gpointer
-inflate_other_data (gpointer data, int info_type, MonoGenericContext *context)
+inflate_other_data (gpointer data, int info_type, MonoGenericContext *context, MonoClass *class, gboolean temporary)
{
g_assert (data);
case MONO_RGCTX_INFO_STATIC_DATA:
case MONO_RGCTX_INFO_KLASS:
case MONO_RGCTX_INFO_VTABLE:
- return mono_class_inflate_generic_type (data, context);
+ case MONO_RGCTX_INFO_TYPE:
+ case MONO_RGCTX_INFO_REFLECTION_TYPE:
+ return mono_class_inflate_generic_type_with_mempool (temporary ? NULL : class->image,
+ data, context);
case MONO_RGCTX_INFO_METHOD:
case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
- return mono_class_inflate_generic_method (data, context);
+ case MONO_RGCTX_INFO_METHOD_RGCTX:
+ case MONO_RGCTX_INFO_METHOD_CONTEXT:
+ case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: {
+ MonoMethod *method = data;
+ MonoMethod *inflated_method;
+ MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
+ MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
+
+ mono_metadata_free_type (inflated_type);
+
+ mono_class_init (inflated_class);
+
+ g_assert (!method->wrapper_type);
+
+ if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
+ inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
+ inflated_method = mono_method_search_in_array_class (inflated_class,
+ method->name, method->signature);
+ } else {
+ inflated_method = mono_class_inflate_generic_method (method, context);
+ }
+ mono_class_init (inflated_method->klass);
+ g_assert (inflated_method->klass == inflated_class);
+ return inflated_method;
+ }
+
+ case MONO_RGCTX_INFO_CLASS_FIELD: {
+ MonoClassField *field = data;
+ MonoType *inflated_type = mono_class_inflate_generic_type (&field->parent->byval_arg, context);
+ MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
+ int i = field - field->parent->fields;
+ gpointer dummy = NULL;
+
+ mono_metadata_free_type (inflated_type);
+
+ mono_class_get_fields (inflated_class, &dummy);
+ g_assert (inflated_class->fields);
+
+ return &inflated_class->fields [i];
+ }
default:
g_assert_not_reached ();
}
+ /* Not reached, quiet compiler */
+ return NULL;
}
static gpointer
-inflate_other_info (MonoRuntimeGenericContextOtherInfoTemplate *oti, MonoGenericContext *context)
+inflate_other_info (MonoRuntimeGenericContextOtherInfoTemplate *oti,
+ MonoGenericContext *context, MonoClass *class, gboolean temporary)
{
- return inflate_other_data (oti->data, oti->info_type, context);
+ return inflate_other_data (oti->data, oti->info_type, context, class, temporary);
}
+static void
+free_inflated_info (int info_type, gpointer info)
+{
+ if (!info)
+ return;
+
+ switch (info_type) {
+ case MONO_RGCTX_INFO_STATIC_DATA:
+ case MONO_RGCTX_INFO_KLASS:
+ case MONO_RGCTX_INFO_VTABLE:
+ case MONO_RGCTX_INFO_TYPE:
+ case MONO_RGCTX_INFO_REFLECTION_TYPE:
+ mono_metadata_free_type (info);
+ break;
+ default:
+ break;
+ }
+}
+
+static MonoRuntimeGenericContextOtherInfoTemplate
+class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free);
+
/*
* mono_class_get_runtime_generic_context_template:
* @class: a class
* Looks up or constructs, if necessary, the runtime generic context
* for class.
*/
-MonoRuntimeGenericContextTemplate*
+static MonoRuntimeGenericContextTemplate*
mono_class_get_runtime_generic_context_template (MonoClass *class)
{
- int num_parent_args, num_class_args, total_num_args;
MonoRuntimeGenericContextTemplate *parent_template, *template;
- MonoGenericInst *inst;
- int i, j;
- MonoRuntimeGenericContextOtherInfoTemplate *oti;
+ guint32 i;
+
+ g_assert (!class->generic_class);
- templates_lock ();
+ mono_loader_lock ();
template = class_lookup_rgctx_template (class);
- templates_unlock ();
+ mono_loader_unlock ();
if (template)
return template;
- if (class->generic_class) {
- parent_template = mono_class_get_runtime_generic_context_template (class->generic_class->container_class);
-
- mono_loader_lock ();
- template = mono_mempool_alloc0 (class->image->mempool,
- sizeof (MonoRuntimeGenericContextTemplate) + sizeof (MonoType*) * parent_template->num_arg_infos);
- mono_loader_unlock ();
-
- template->num_arg_infos = parent_template->num_arg_infos;
-
- for (i = 0; i < parent_template->num_arg_infos; ++i) {
- template->arg_infos [i] = mono_class_inflate_generic_type (parent_template->arg_infos [i],
- &class->generic_class->context);
- /*
- g_print("arg info %d of %s is %s (from %s)\n", i,
- mono_type_get_full_name (class),
- mono_type_get_name (template->arg_infos [i]),
- mono_type_get_name (parent_template->arg_infos [i]));
- */
- }
-
- /* FIXME: quadratic */
- for (i = 0, oti = parent_template->other_infos; oti; ++i, oti = oti->next) {
- if (oti->data && oti->data != MONO_RGCTX_SLOT_USED_MARKER) {
- rgctx_template_set_other_slot (class->image, template, i,
- inflate_other_info (oti, &class->generic_class->context),
- oti->info_type);
- }
- }
-
- templates_lock ();
-
- if (class_lookup_rgctx_template (class)) {
- /* some other thread already set the template */
- template = class_lookup_rgctx_template (class);
- } else {
- class_set_rgctx_template (class, template);
- register_open_instance (class);
- }
-
- templates_unlock ();
-
- return template;
- }
-
- if (class->parent)
- parent_template = mono_class_get_runtime_generic_context_template (class->parent);
- else
- parent_template = NULL;
-
- if (parent_template)
- num_parent_args = parent_template->num_arg_infos;
- else
- num_parent_args = 0;
-
- if (class->generic_container)
- inst = class->generic_container->context.class_inst;
- else
- inst = NULL;
-
- num_class_args = 0;
- if (inst) {
- for (i = 0; i < inst->type_argc; ++i) {
- if (include_arg_info (inst->type_argv [i], parent_template))
- ++num_class_args;
- }
- }
-
- total_num_args = num_parent_args + num_class_args;
+ template = alloc_template (class);
mono_loader_lock ();
- template = mono_mempool_alloc0 (class->image->mempool,
- sizeof (MonoRuntimeGenericContextTemplate) + sizeof (MonoType*) * total_num_args);
- mono_loader_unlock ();
- template->num_arg_infos = total_num_args;
+ if (class->parent) {
+ if (class->parent->generic_class) {
+ guint32 num_entries;
+ int max_argc, type_argc;
- if (num_parent_args > 0)
- memcpy (template->arg_infos, parent_template->arg_infos,
- sizeof (MonoType*) * parent_template->num_arg_infos);
+ parent_template = mono_class_get_runtime_generic_context_template
+ (class->parent->generic_class->container_class);
- j = 0;
- if (inst) {
- for (i = 0; i < inst->type_argc; ++i) {
- MonoType *type = inst->type_argv [i];
+ max_argc = template_get_max_argc (parent_template);
- if (include_arg_info (type, parent_template))
- template->arg_infos [num_parent_args + j++] = type;
- }
- }
- g_assert (j == num_class_args);
+ for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
+ num_entries = rgctx_template_num_other_infos (parent_template, type_argc);
- /*
- g_print ("class %s has %d type args (%d from parent)\n",
- mono_type_get_full_name(class), total_num_args, num_parent_args);
- */
+ /* FIXME: quadratic! */
+ for (i = 0; i < num_entries; ++i) {
+ MonoRuntimeGenericContextOtherInfoTemplate oti;
- if (parent_template) {
- MonoRuntimeGenericContextOtherInfoTemplate *oti, **new_oti;
-
- oti = parent_template->other_infos;
- new_oti = &template->other_infos;
+ oti = class_get_rgctx_template_oti (class->parent, type_argc, i, FALSE, NULL);
+ if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
+ rgctx_template_set_other_slot (class->image, template, type_argc, i,
+ oti.data, oti.info_type);
+ }
+ }
+ }
+ } else {
+ MonoRuntimeGenericContextOtherInfoTemplate *oti;
+ int max_argc, type_argc;
- mono_loader_lock ();
+ parent_template = mono_class_get_runtime_generic_context_template (class->parent);
- while (oti) {
- *new_oti = mono_mempool_alloc0 (class->image->mempool,
- sizeof (MonoRuntimeGenericContextOtherInfoTemplate));
- (*new_oti)->data = oti->data;
- (*new_oti)->info_type = oti->info_type;
+ max_argc = template_get_max_argc (parent_template);
- oti = oti->next;
- new_oti = &(*new_oti)->next;
+ for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
+ /* FIXME: quadratic! */
+ for (i = 0, oti = parent_template->other_infos; oti; ++i, oti = oti->next) {
+ if (oti->data && oti->data != MONO_RGCTX_SLOT_USED_MARKER) {
+ rgctx_template_set_other_slot (class->image, template, type_argc, i,
+ oti->data, oti->info_type);
+ }
+ }
+ }
}
-
- mono_loader_unlock ();
}
- templates_lock ();
-
if (class_lookup_rgctx_template (class)) {
/* some other thread already set the template */
template = class_lookup_rgctx_template (class);
register_generic_subclass (class);
}
- templates_unlock ();
+ mono_loader_unlock ();
return template;
}
+/*
+ * temporary signifies whether the inflated info (oti.data) will be
+ * used temporarily, in which case it might be heap-allocated, or
+ * permanently, in which case it will be mempool-allocated. If
+ * temporary is set then *do_free will return whether the returned
+ * data must be freed.
+ *
+ * LOCKING: loader lock
+ */
+static MonoRuntimeGenericContextOtherInfoTemplate
+class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean *do_free)
+{
+ g_assert ((temporary && do_free) || (!temporary && !do_free));
+
+ if (class->generic_class) {
+ MonoRuntimeGenericContextOtherInfoTemplate oti;
+ gboolean tmp_do_free;
+
+ oti = class_get_rgctx_template_oti (class->generic_class->container_class,
+ type_argc, slot, TRUE, &tmp_do_free);
+ if (oti.data) {
+ gpointer info = oti.data;
+ oti.data = inflate_other_info (&oti, &class->generic_class->context, class, temporary);
+ if (tmp_do_free)
+ free_inflated_info (oti.info_type, info);
+ }
+ if (temporary)
+ *do_free = TRUE;
+
+ return oti;
+ } else {
+ MonoRuntimeGenericContextTemplate *template;
+ MonoRuntimeGenericContextOtherInfoTemplate *oti;
+
+ template = mono_class_get_runtime_generic_context_template (class);
+ oti = rgctx_template_get_other_slot (template, type_argc, slot);
+ g_assert (oti);
+
+ if (temporary)
+ *do_free = FALSE;
+
+ return *oti;
+ }
+}
+
static MonoClass*
class_uninstantiated (MonoClass *class)
{
class_type_info (MonoDomain *domain, MonoClass *class, int info_type)
{
switch (info_type) {
- case MONO_RGCTX_INFO_STATIC_DATA:
- return mono_class_vtable (domain, class)->data;
+ case MONO_RGCTX_INFO_STATIC_DATA: {
+ MonoVTable *vtable = mono_class_vtable (domain, class);
+ if (!vtable)
+ mono_raise_exception (mono_class_get_exception_for_failure (class));
+ return vtable->data;
+ }
case MONO_RGCTX_INFO_KLASS:
return class;
- case MONO_RGCTX_INFO_VTABLE:
- return mono_class_vtable (domain, class);
+ case MONO_RGCTX_INFO_VTABLE: {
+ MonoVTable *vtable = mono_class_vtable (domain, class);
+ if (!vtable)
+ mono_raise_exception (mono_class_get_exception_for_failure (class));
+ return vtable;
+ }
default:
g_assert_not_reached ();
}
+ /* Not reached */
+ return NULL;
}
static gpointer
-instantiate_other_info (MonoDomain *domain, MonoRuntimeGenericContextOtherInfoTemplate *oti, MonoGenericContext *context)
+instantiate_other_info (MonoDomain *domain, MonoRuntimeGenericContextOtherInfoTemplate *oti,
+ MonoGenericContext *context, MonoClass *class)
{
gpointer data;
+ gboolean temporary;
if (!oti->data)
return NULL;
- data = inflate_other_info (oti, context);
+ switch (oti->info_type) {
+ case MONO_RGCTX_INFO_STATIC_DATA:
+ case MONO_RGCTX_INFO_KLASS:
+ case MONO_RGCTX_INFO_VTABLE:
+ temporary = TRUE;
+ break;
+ default:
+ temporary = FALSE;
+ }
+
+ data = inflate_other_info (oti, context, class, temporary);
switch (oti->info_type) {
case MONO_RGCTX_INFO_STATIC_DATA:
case MONO_RGCTX_INFO_VTABLE: {
MonoClass *arg_class = mono_class_from_mono_type (data);
+ free_inflated_info (oti->info_type, data);
g_assert (arg_class);
+ /* The class might be used as an argument to
+ mono_value_copy(), which requires that its GC
+ descriptor has been computed. */
+ if (oti->info_type == MONO_RGCTX_INFO_KLASS)
+ mono_class_compute_gc_descriptor (arg_class);
+
return class_type_info (domain, arg_class, oti->info_type);
}
+ case MONO_RGCTX_INFO_TYPE:
+ return data;
+ case MONO_RGCTX_INFO_REFLECTION_TYPE:
+ return mono_type_get_object (domain, data);
case MONO_RGCTX_INFO_METHOD:
return data;
case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
- return mono_compile_method (data);
+ return mono_create_ftnptr (mono_domain_get (),
+ mono_runtime_create_jump_trampoline (mono_domain_get (), data, TRUE));
+ case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
+ return mono_create_ftnptr (mono_domain_get (),
+ mono_runtime_create_jump_trampoline (mono_domain_get (),
+ mono_marshal_get_remoting_invoke_with_check (data), TRUE));
+ case MONO_RGCTX_INFO_CLASS_FIELD:
+ return data;
+ case MONO_RGCTX_INFO_METHOD_RGCTX: {
+ MonoMethodInflated *method = data;
+ MonoVTable *vtable;
+
+ g_assert (method->method.method.is_inflated);
+ g_assert (method->context.method_inst);
+
+ vtable = mono_class_vtable (domain, method->method.method.klass);
+ if (!vtable)
+ mono_raise_exception (mono_class_get_exception_for_failure (method->method.method.klass));
+
+ return mono_method_lookup_rgctx (vtable, method->context.method_inst);
+ }
+ case MONO_RGCTX_INFO_METHOD_CONTEXT: {
+ MonoMethodInflated *method = data;
+
+ g_assert (method->method.method.is_inflated);
+ g_assert (method->context.method_inst);
+
+ return method->context.method_inst;
+ }
default:
g_assert_not_reached ();
}
+ /* Not reached */
+ return NULL;
}
/*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static void
-fill_in_rgctx_template_slot (MonoClass *class, int index, gpointer data, int info_type)
+fill_in_rgctx_template_slot (MonoClass *class, int type_argc, int index, gpointer data, int info_type)
{
MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
MonoClass *subclass;
- GSList *instances;
- int old_length, new_length, old_instances_extra_length;
- int old_instances_length = -1;
g_assert (!class->generic_class);
- old_length = rgctx_template_num_other_infos (template);
- rgctx_template_set_other_slot (class->image, template, index, data, info_type);
- new_length = rgctx_template_num_other_infos (template);
-
- /* For all open instantiations on this level: Instantiate the
- * type and put it in the rgctx template.
- */
- if (class->image->generic_class_open_instances_hash)
- instances = g_hash_table_lookup (class->image->generic_class_open_instances_hash, class);
- else
- instances = NULL;
-
- while (instances) {
- MonoClass *instance = instances->data;
- MonoRuntimeGenericContextTemplate *instance_template = class_lookup_rgctx_template (instance);
- int length;
- gpointer inflated_data;
-
- g_assert (instance_template);
- g_assert (instance->generic_class != NULL && instance->generic_class->container_class == class);
-
- length = rgctx_template_num_other_infos (instance_template);
- if (old_instances_length < 0)
- old_instances_length = length;
-
- g_assert (length == old_instances_length);
-
- inflated_data = inflate_other_data (data, info_type, &instance->generic_class->context);
- rgctx_template_set_other_slot (instance->image, instance_template, index,
- inflated_data, info_type);
-
- g_assert (rgctx_template_num_other_infos (instance_template) == new_length);
-
- instances = instances->next;
- }
-
- if (old_instances_length < 0)
- old_instances_length = old_length;
- old_instances_extra_length = MAX(old_instances_length - MONO_RGCTX_MAX_OTHER_INFOS, 0);
-
- g_assert (old_instances_length <= old_length);
-
- /* The reason why the instance's other_infos list can be
- * shorter than the uninstanted class's is that when we mark
- * slots as used in superclasses we only do that in the
- * uninstantiated classes, not in the instances.
- */
- g_assert (old_instances_length <= old_length);
-
- /* For all rgctx's on this level: Instantiate the type and put
- * the class in the rgctx.
- */
- if (generic_class_rgctx_hash)
- instances = g_hash_table_lookup (generic_class_rgctx_hash, class);
- else
- instances = NULL;
-
- while (instances) {
- MonoVTable *vtable = instances->data;
- MonoRuntimeGenericContextTemplate *instance_template;
- int real_index;
- gpointer *table;
-
- instance_template = mono_class_get_runtime_generic_context_template (class_uninstantiated (vtable->klass));
-
- g_assert (vtable->runtime_generic_context);
-
- if (index < MONO_RGCTX_MAX_OTHER_INFOS) {
- real_index = index;
- table = vtable->runtime_generic_context->other_infos;
- } else {
- real_index = index - MONO_RGCTX_MAX_OTHER_INFOS;
-
- if (old_instances_length == new_length) {
- table = vtable->runtime_generic_context->extra_other_infos;
- } else {
- g_assert (old_instances_extra_length < real_index + 1);
-
- /* Allocate new table with the required number
- of slots and copy the old slots over. */
- mono_domain_lock (vtable->domain);
- table = mono_mempool_alloc0 (vtable->domain->mp,
- sizeof (gpointer) * (real_index + 1));
- mono_domain_unlock (vtable->domain);
-
- memcpy (table, vtable->runtime_generic_context->extra_other_infos,
- sizeof (gpointer) * old_instances_extra_length);
- mono_memory_write_barrier ();
- vtable->runtime_generic_context->extra_other_infos = table;
- }
- }
-
- g_assert (!table [real_index]);
-
- //g_print ("rgctx %s . other_infos [%d] = %s\n", mono_type_get_full_name (vtable->klass), index, mono_type_get_full_name (other_class));
-
- instances = instances->next;
- }
+ rgctx_template_set_other_slot (class->image, template, type_argc, index, data, info_type);
/* Recurse for all subclasses */
if (generic_subclass_hash)
subclass = NULL;
while (subclass) {
- MonoRuntimeGenericContextOtherInfoTemplate *subclass_oti;
+ MonoRuntimeGenericContextOtherInfoTemplate subclass_oti;
MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
g_assert (!subclass->generic_class);
g_assert (subclass_template);
- subclass_oti = rgctx_template_get_other_slot (class_lookup_rgctx_template (subclass->parent), index);
- g_assert (subclass_oti->data);
+ subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, NULL);
+ g_assert (subclass_oti.data);
- fill_in_rgctx_template_slot (subclass, index, subclass_oti->data, info_type);
+ fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
subclass = subclass_template->next_subclass;
}
}
/*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static int
-register_other_info (MonoClass *class, gpointer data, int info_type)
+register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type)
{
int i;
MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
MonoClass *parent;
MonoRuntimeGenericContextOtherInfoTemplate *oti;
- g_assert (!class->generic_class && class->generic_container);
-
- for (i = 0, oti = template->other_infos; oti; ++i, oti = oti->next) {
+ for (i = 0, oti = get_other_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
if (!oti->data)
break;
}
parent = class->parent;
while (parent != NULL) {
MonoRuntimeGenericContextTemplate *parent_template;
+ MonoRuntimeGenericContextOtherInfoTemplate *oti;
if (parent->generic_class)
parent = parent->generic_class->container_class;
parent_template = mono_class_get_runtime_generic_context_template (parent);
+ oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
- if (rgctx_template_get_other_slot (parent_template, i))
+ if (oti && oti->data)
break;
- rgctx_template_set_other_slot (parent->image, parent_template, i, MONO_RGCTX_SLOT_USED_MARKER, 0);
+ rgctx_template_set_other_slot (parent->image, parent_template, type_argc, i,
+ MONO_RGCTX_SLOT_USED_MARKER, 0);
parent = parent->parent;
}
/* Fill in the slot in this class and in all subclasses
recursively. */
- fill_in_rgctx_template_slot (class, i, data, info_type);
+ fill_in_rgctx_template_slot (class, type_argc, i, data, info_type);
return i;
}
case MONO_RGCTX_INFO_STATIC_DATA:
case MONO_RGCTX_INFO_KLASS:
case MONO_RGCTX_INFO_VTABLE:
+ case MONO_RGCTX_INFO_TYPE:
+ case MONO_RGCTX_INFO_REFLECTION_TYPE:
return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
case MONO_RGCTX_INFO_METHOD:
case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
+ case MONO_RGCTX_INFO_CLASS_FIELD:
+ case MONO_RGCTX_INFO_METHOD_RGCTX:
+ case MONO_RGCTX_INFO_METHOD_CONTEXT:
+ case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
return data1 == data2;
default:
g_assert_not_reached ();
}
+ /* never reached */
+ return FALSE;
}
-/*
- * mono_class_lookup_or_register_other_info:
- * @class: a class
- * @data: the info data
- * @info_type: the type of info to register about data
- * @generic_context: a generic context
- *
- * Looks up and, if necessary, adds information about other_class in
- * class's runtime generic context. Returns the index of the
- * corresponding other-infos slot.
- */
-int
-mono_class_lookup_or_register_other_info (MonoClass *class, gpointer data, int info_type,
+static int
+lookup_or_register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type,
MonoGenericContext *generic_context)
{
+ static gboolean inited = FALSE;
+ static int max_slot = 0;
+
MonoRuntimeGenericContextTemplate *rgctx_template =
mono_class_get_runtime_generic_context_template (class);
- int i, num_other_infos;
+ MonoRuntimeGenericContextOtherInfoTemplate *oti_list, *oti;
+ int i;
- templates_lock ();
+ g_assert (!class->generic_class);
+ g_assert (class->generic_container || type_argc);
- num_other_infos = rgctx_template_num_other_infos (rgctx_template);
- for (i = 0; i < num_other_infos; ++i) {
- /* FIXME: This is quadratic in complexity! */
- MonoRuntimeGenericContextOtherInfoTemplate *oti = rgctx_template_get_other_slot (rgctx_template, i);
+ mono_loader_lock ();
+
+ oti_list = get_other_info_templates (rgctx_template, type_argc);
+
+ for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
gpointer inflated_data;
- if (!oti || oti->info_type != info_type)
+ if (oti->info_type != info_type || !oti->data)
continue;
- inflated_data = inflate_other_info (oti, generic_context);
+ inflated_data = inflate_other_info (oti, generic_context, class, TRUE);
if (other_info_equal (data, inflated_data, info_type)) {
- templates_unlock ();
+ free_inflated_info (info_type, inflated_data);
+ mono_loader_unlock ();
return i;
}
+ free_inflated_info (info_type, inflated_data);
}
- i = register_other_info (class, data, info_type);
+ /* We haven't found the info */
+ i = register_other_info (class, type_argc, data, info_type);
- templates_unlock ();
+ mono_loader_unlock ();
+
+ if (!inited) {
+ mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
+ inited = TRUE;
+ }
+ if (i > max_slot)
+ max_slot = i;
return i;
}
-static void
-instantiate_arg_info (MonoDomain *domain, MonoRuntimeGenericArgInfo *destination, MonoType *arg_info,
- MonoGenericContext *context)
+/*
+ * mono_method_lookup_or_register_other_info:
+ * @method: a method
+ * @in_mrgctx: whether to put the data into the MRGCTX
+ * @data: the info data
+ * @info_type: the type of info to register about data
+ * @generic_context: a generic context
+ *
+ * Looks up and, if necessary, adds information about other_class in
+ * method's or method's class runtime generic context. Returns the
+ * encoded slot number.
+ */
+guint32
+mono_method_lookup_or_register_other_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
+ int info_type, MonoGenericContext *generic_context)
{
- MonoType *arg_type;
- MonoClass *arg_class;
- MonoVTable *vtable;
+ MonoClass *class = method->klass;
+ int type_argc, index;
- if (!arg_info)
- return;
+ if (in_mrgctx) {
+ MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
- arg_type = mono_class_inflate_generic_type (arg_info, context);
+ g_assert (method->is_inflated && method_inst);
+ type_argc = method_inst->type_argc;
+ g_assert (type_argc > 0);
+ } else {
+ type_argc = 0;
+ }
- arg_class = mono_class_from_mono_type (arg_type);
- g_assert (arg_class);
+ index = lookup_or_register_other_info (class, type_argc, data, info_type, generic_context);
- vtable = mono_class_vtable (domain, arg_class);
+ //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
- destination->static_data = vtable->data;
- destination->klass = arg_class;
- destination->vtable = vtable;
+ if (in_mrgctx)
+ return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
+ else
+ return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
}
/*
- * LOCKING: domain lock for rgctx->vtable->domain
+ * mono_class_rgctx_get_array_size:
+ * @n: The number of the array
+ * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
+ *
+ * Returns the number of slots in the n'th array of a (M)RGCTX. That
+ * number includes the slot for linking and - for MRGCTXs - the two
+ * slots in the first array for additional information.
*/
-static void
-rgctx_alloc_extra_other_types (MonoRuntimeGenericContext *rgctx)
+int
+mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
{
- MonoClass *class = rgctx->vtable->klass;
- MonoDomain *domain = rgctx->vtable->domain;
- MonoRuntimeGenericContextTemplate *rgctx_template;
- int num_extra_other_infos;
+ g_assert (n >= 0 && n < 30);
- rgctx_template = mono_class_get_runtime_generic_context_template (class_uninstantiated (class));
- num_extra_other_infos = MAX (rgctx_template_num_other_infos (rgctx_template) - MONO_RGCTX_MAX_OTHER_INFOS, 0);
-
- if (num_extra_other_infos > 0)
- rgctx->extra_other_infos = mono_mempool_alloc0 (domain->mp,
- sizeof (gpointer) * num_extra_other_infos);
+ if (mrgctx)
+ return 6 << n;
+ else
+ return 4 << n;
}
/*
- * mono_class_fill_runtime_generic_context:
- * @rgctx: a runtime generic context
- *
- * Instantiates all slots of the runtime generic context rgctx.
+ * LOCKING: domain lock
*/
-void
-mono_class_fill_runtime_generic_context (MonoRuntimeGenericContext *rgctx)
+static gpointer*
+alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
+{
+ static gboolean inited = FALSE;
+ static int rgctx_num_alloced = 0;
+ static int rgctx_bytes_alloced = 0;
+ static int mrgctx_num_alloced = 0;
+ static int mrgctx_bytes_alloced = 0;
+
+ int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
+ gpointer array = mono_domain_alloc0 (domain, size);
+
+ if (!inited) {
+ mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
+ mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
+ mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
+ mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
+ inited = TRUE;
+ }
+
+ if (is_mrgctx) {
+ mrgctx_num_alloced++;
+ mrgctx_bytes_alloced += size;
+ } else {
+ rgctx_num_alloced++;
+ rgctx_bytes_alloced += size;
+ }
+
+ return array;
+}
+
+static gpointer
+fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
+ MonoGenericInst *method_inst)
{
- MonoVTable *class_vtable = rgctx->vtable;
+ gpointer info;
+ int i, first_slot, size;
MonoDomain *domain = class_vtable->domain;
MonoClass *class = class_vtable->klass;
- int depth = class->idepth;
- MonoGenericContext *context = &class->generic_class->context;
- MonoRuntimeGenericSuperInfo *super_infos = (MonoRuntimeGenericSuperInfo*)rgctx - depth;
- MonoRuntimeGenericContextTemplate *rgctx_template;
- MonoRuntimeGenericContextOtherInfoTemplate *oti;
- MonoClass *super;
- int i;
+ MonoGenericContext *class_context = class->generic_class ? &class->generic_class->context : NULL;
+ MonoRuntimeGenericContextOtherInfoTemplate oti;
+ MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
+ int rgctx_index;
+ gboolean do_free;
- rgctx_template = mono_class_get_runtime_generic_context_template (class_uninstantiated (class));
+ g_assert (rgctx);
mono_domain_lock (domain);
- depth = 0;
- for (super = class; super; super = super->parent) {
- MonoVTable *vtable = mono_class_vtable (domain, super);
+ /* First check whether that slot isn't already instantiated.
+ This might happen because lookup doesn't lock. Allocate
+ arrays on the way. */
+ first_slot = 0;
+ size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
+ if (method_inst)
+ size -= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
+ for (i = 0; ; ++i) {
+ int offset;
+
+ if (method_inst && i == 0)
+ offset = MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
+ else
+ offset = 0;
+
+ if (slot < first_slot + size - 1) {
+ rgctx_index = slot - first_slot + 1 + offset;
+ info = rgctx [rgctx_index];
+ if (info) {
+ mono_domain_unlock (domain);
+ return info;
+ }
+ break;
+ }
+ if (!rgctx [offset + 0])
+ rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
+ rgctx = rgctx [offset + 0];
+ first_slot += size - 1;
+ size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
+ }
- super_infos [depth].static_data = vtable->data;
- super_infos [depth].klass = super;
- super_infos [depth].vtable = vtable;
+ g_assert (!rgctx [rgctx_index]);
- depth++;
- }
+ mono_domain_unlock (domain);
- rgctx_alloc_extra_other_types (rgctx);
+ oti = class_get_rgctx_template_oti (class_uninstantiated (class),
+ method_inst ? method_inst->type_argc : 0, slot, TRUE, &do_free);
+ /* This might take the loader lock */
+ info = instantiate_other_info (domain, &oti, &context, class);
- for (i = 0, oti = rgctx_template->other_infos; oti; ++i, oti = oti->next) {
- gpointer *arg_info;
+ /*
+ if (method_inst)
+ g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
+ */
- if (i < MONO_RGCTX_MAX_OTHER_INFOS)
- arg_info = &rgctx->other_infos [i];
- else
- arg_info = &rgctx->extra_other_infos [i - MONO_RGCTX_MAX_OTHER_INFOS];
+ /*FIXME We should use CAS here, no need to take a lock.*/
+ mono_domain_lock (domain);
- *arg_info = instantiate_other_info (domain, oti, context);
+ /* Check whether the slot hasn't been instantiated in the
+ meantime. */
+ if (rgctx [rgctx_index])
+ info = rgctx [rgctx_index];
+ else
+ rgctx [rgctx_index] = info;
+
+ mono_domain_unlock (domain);
+
+ if (do_free)
+ free_inflated_info (oti.info_type, oti.data);
+
+ return info;
+}
+
+/*
+ * mono_class_fill_runtime_generic_context:
+ * @class_vtable: a vtable
+ * @slot: a slot index to be instantiated
+ *
+ * Instantiates a slot in the RGCTX.
+ */
+gpointer
+mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot)
+{
+ static gboolean inited = FALSE;
+ static int num_alloced = 0;
+
+ MonoDomain *domain = class_vtable->domain;
+ MonoRuntimeGenericContext *rgctx;
+ gpointer info;
+
+ mono_domain_lock (domain);
+
+ if (!inited) {
+ mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
+ inited = TRUE;
}
- for (i = 0; i < rgctx_template->num_arg_infos; ++i)
- instantiate_arg_info (domain, &rgctx->arg_infos [i], rgctx_template->arg_infos [i], context);
+ rgctx = class_vtable->runtime_generic_context;
+ if (!rgctx) {
+ rgctx = alloc_rgctx_array (domain, 0, FALSE);
+ class_vtable->runtime_generic_context = rgctx;
+ num_alloced++;
+ }
mono_domain_unlock (domain);
+
+ info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
+
+ return info;
}
/*
- * mono_class_setup_runtime_generic_context:
- * @class: a class
- * @domain: a domain
+ * mono_method_fill_runtime_generic_context:
+ * @mrgctx: an MRGCTX
+ * @slot: a slot index to be instantiated
*
- * Sets up the runtime generic context of class in domain.
+ * Instantiates a slot in the MRGCTX.
*/
-void
-mono_class_setup_runtime_generic_context (MonoClass *class, MonoDomain *domain)
+gpointer
+mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot)
{
- MonoVTable *class_vtable = mono_class_vtable (domain, class);
- int depth = class->idepth;
- MonoRuntimeGenericSuperInfo *super_infos;
- MonoRuntimeGenericContext *rgctx;
- MonoRuntimeGenericContextTemplate *rgctx_template;
+ gpointer info;
- /* Never setup a rgctx for an open class. */
- if (mono_class_check_context_used (class))
- return;
+ info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot,
+ mrgctx->method_inst);
+
+ return info;
+}
- //g_print ("setting up rgctx for %s\n", mono_type_get_full_name (class));
+static guint
+mrgctx_hash_func (gconstpointer key)
+{
+ const MonoMethodRuntimeGenericContext *mrgctx = key;
+
+ return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
+}
- /* We have to take the lock here and only release it after
- * we've registered the vtable because otherwise another
- * thread might add a slot to the template which we wouldn't
- * know anything about and we'd end up with a rgctx that's not
- * fully filled in.
- */
- templates_lock ();
+static gboolean
+mrgctx_equal_func (gconstpointer a, gconstpointer b)
+{
+ const MonoMethodRuntimeGenericContext *mrgctx1 = a;
+ const MonoMethodRuntimeGenericContext *mrgctx2 = b;
- rgctx_template = mono_class_get_runtime_generic_context_template (class_uninstantiated (class));
+ return mrgctx1->class_vtable == mrgctx2->class_vtable &&
+ mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
+}
+
+/*
+ * mono_method_lookup_rgctx:
+ * @class_vtable: a vtable
+ * @method_inst: the method inst of a generic method
+ *
+ * Returns the MRGCTX for the generic method(s) with the given
+ * method_inst of the given class_vtable.
+ *
+ * LOCKING: Take the domain lock.
+ */
+MonoMethodRuntimeGenericContext*
+mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
+{
+ MonoDomain *domain = class_vtable->domain;
+ MonoMethodRuntimeGenericContext *mrgctx;
+ MonoMethodRuntimeGenericContext key;
+
+ g_assert (!class_vtable->klass->generic_container);
+ g_assert (!method_inst->is_open);
mono_domain_lock (domain);
- /* We don't allocate arg_infos because we don't use it yet.
- */
- super_infos = mono_mempool_alloc0 (domain->mp,
- sizeof (MonoRuntimeGenericSuperInfo) * depth +
- sizeof (MonoRuntimeGenericContext) +
- sizeof (MonoRuntimeGenericArgInfo) * rgctx_template->num_arg_infos);
+ if (!domain->method_rgctx_hash)
+ domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
+
+ key.class_vtable = class_vtable;
+ key.method_inst = method_inst;
+
+ mrgctx = g_hash_table_lookup (domain->method_rgctx_hash, &key);
+
+ if (!mrgctx) {
+ //int i;
+
+ mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
+ mrgctx->class_vtable = class_vtable;
+ mrgctx->method_inst = method_inst;
+
+ g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
+
+ /*
+ g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
+ for (i = 0; i < method_inst->type_argc; ++i)
+ g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
+ g_print (">\n");
+ */
+ }
+
mono_domain_unlock (domain);
- rgctx = class_vtable->runtime_generic_context = (MonoRuntimeGenericContext*) (super_infos + depth);
+ g_assert (mrgctx);
+
+ return mrgctx;
+}
+
+static gboolean
+generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars)
+{
+ int i;
+
+ for (i = 0; i < inst->type_argc; ++i) {
+ MonoType *type = inst->type_argv [i];
+ int type_type;
+
+ if (MONO_TYPE_IS_REFERENCE (type))
+ continue;
+
+ type_type = mono_type_get_type (type);
+ if (allow_type_vars && (type_type == MONO_TYPE_VAR || type_type == MONO_TYPE_MVAR))
+ continue;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * mono_generic_context_is_sharable:
+ * @context: a generic context
+ *
+ * Returns whether the generic context is sharable. A generic context
+ * is sharable iff all of its type arguments are reference type.
+ */
+gboolean
+mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
+{
+ g_assert (context->class_inst || context->method_inst);
+
+ if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars))
+ return FALSE;
+
+ if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars))
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * mono_method_is_generic_impl:
+ * @method: a method
+ *
+ * Returns whether the method is either generic or part of a generic
+ * class.
+ */
+gboolean
+mono_method_is_generic_impl (MonoMethod *method)
+{
+ if (method->is_inflated) {
+ g_assert (method->wrapper_type == MONO_WRAPPER_NONE);
+ return TRUE;
+ }
+ /* We don't treat wrappers as generic code, i.e., we never
+ apply generic sharing to them. This is especially
+ important for static rgctx invoke wrappers, which only work
+ if not compiled with sharing. */
+ if (method->wrapper_type != MONO_WRAPPER_NONE)
+ return FALSE;
+ if (method->klass->generic_container)
+ return TRUE;
+ return FALSE;
+}
+
+static gboolean
+has_constraints (MonoGenericContainer *container)
+{
+ //int i;
+
+ return FALSE;
+ /*
+ g_assert (container->type_argc > 0);
+ g_assert (container->type_params);
+
+ for (i = 0; i < container->type_argc; ++i)
+ if (container->type_params [i].constraints)
+ return TRUE;
+ return FALSE;
+ */
+}
+
+/*
+ * mono_method_is_generic_sharable_impl:
+ * @method: a method
+ * @allow_type_vars: whether to regard type variables as reference types
+ *
+ * Returns TRUE iff the method is inflated or part of an inflated
+ * class, its context is sharable and it has no constraints on its
+ * type parameters. Otherwise returns FALSE.
+ */
+gboolean
+mono_method_is_generic_sharable_impl (MonoMethod *method, gboolean allow_type_vars)
+{
+ if (!mono_method_is_generic_impl (method))
+ return FALSE;
+
+ if (method->is_inflated) {
+ MonoMethodInflated *inflated = (MonoMethodInflated*)method;
+ MonoGenericContext *context = &inflated->context;
+
+ if (!mono_generic_context_is_sharable (context, allow_type_vars))
+ return FALSE;
+
+ g_assert (inflated->declaring);
+
+ if (inflated->declaring->is_generic) {
+ if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
+ return FALSE;
+ }
+ }
+
+ if (method->klass->generic_class) {
+ if (!mono_generic_context_is_sharable (&method->klass->generic_class->context, allow_type_vars))
+ return FALSE;
+
+ g_assert (method->klass->generic_class->container_class &&
+ method->klass->generic_class->container_class->generic_container);
+
+ if (has_constraints (method->klass->generic_class->container_class->generic_container))
+ return FALSE;
+ }
+
+ if (method->klass->generic_container && !allow_type_vars)
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
+{
+ if (!mono_class_generic_sharing_enabled (method->klass))
+ return FALSE;
+
+ if (!mono_method_is_generic_sharable_impl (method, allow_type_vars))
+ return FALSE;
+
+ if (method->is_inflated && mono_method_get_context (method)->method_inst)
+ return TRUE;
+
+ return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
+ method->klass->valuetype) &&
+ (method->klass->generic_class || method->klass->generic_container);
+}
+
+static MonoGenericInst*
+get_object_generic_inst (int type_argc)
+{
+ MonoType **type_argv;
+ int i;
+
+ type_argv = alloca (sizeof (MonoType*) * type_argc);
- rgctx->domain = domain;
- rgctx->vtable = class_vtable;
+ for (i = 0; i < type_argc; ++i)
+ type_argv [i] = &mono_defaults.object_class->byval_arg;
- rgctx_alloc_extra_other_types (rgctx);
+ return mono_metadata_get_generic_inst (type_argc, type_argv);
+}
+
+/*
+ * mono_method_construct_object_context:
+ * @method: a method
+ *
+ * Returns a generic context for method with all type variables for
+ * class and method instantiated with Object.
+ */
+MonoGenericContext
+mono_method_construct_object_context (MonoMethod *method)
+{
+ MonoGenericContext object_context;
+
+ g_assert (!method->klass->generic_class);
+ if (method->klass->generic_container) {
+ int type_argc = method->klass->generic_container->type_argc;
+
+ object_context.class_inst = get_object_generic_inst (type_argc);
+ } else {
+ object_context.class_inst = NULL;
+ }
+
+ if (mono_method_get_context_general (method, TRUE)->method_inst) {
+ int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
+
+ object_context.method_inst = get_object_generic_inst (type_argc);
+ } else {
+ object_context.method_inst = NULL;
+ }
+
+ g_assert (object_context.class_inst || object_context.method_inst);
+
+ return object_context;
+}
+
+/*
+ * mono_domain_lookup_shared_generic:
+ * @domain: a domain
+ * @open_method: an open generic method
+ *
+ * Looks up the jit info for method via the domain's jit code hash.
+ */
+MonoJitInfo*
+mono_domain_lookup_shared_generic (MonoDomain *domain, MonoMethod *open_method)
+{
+ static gboolean inited = FALSE;
+ static int lookups = 0;
+ static int failed_lookups = 0;
+
+ MonoGenericContext object_context;
+ MonoMethod *object_method;
+ MonoJitInfo *ji;
+
+ object_context = mono_method_construct_object_context (open_method);
+ object_method = mono_class_inflate_generic_method (open_method, &object_context);
+
+ mono_domain_jit_code_hash_lock (domain);
+ ji = mono_internal_hash_table_lookup (&domain->jit_code_hash, object_method);
+ if (ji && !ji->has_generic_jit_info)
+ ji = NULL;
+ mono_domain_jit_code_hash_unlock (domain);
+
+ if (!inited) {
+ mono_counters_register ("Shared generic lookups", MONO_COUNTER_INT|MONO_COUNTER_GENERICS, &lookups);
+ mono_counters_register ("Failed shared generic lookups", MONO_COUNTER_INT|MONO_COUNTER_GENERICS, &failed_lookups);
+ inited = TRUE;
+ }
- register_rgctx_vtable (class_vtable);
+ ++lookups;
+ if (!ji)
+ ++failed_lookups;
- templates_unlock ();
+ return ji;
}