* 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>
}
/*
- * Guards the two global rgctx (template) hash tables and all rgctx
- * templates.
- *
- * Ordering: The loader lock can be taken while the templates lock is
- * held.
- */
-static CRITICAL_SECTION templates_mutex;
-
-static void
-templates_lock (void)
-{
- static gboolean inited = FALSE;
-
- if (!inited) {
- mono_loader_lock ();
- if (!inited) {
- InitializeCriticalSection (&templates_mutex);
- inited = TRUE;
- }
- mono_loader_unlock ();
- }
-
- EnterCriticalSection (&templates_mutex);
-}
-
-static void
-templates_unlock (void)
-{
- LeaveCriticalSection (&templates_mutex);
-}
-
-/*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static MonoRuntimeGenericContextOtherInfoTemplate*
get_other_info_templates (MonoRuntimeGenericContextTemplate *template, int type_argc)
}
/*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static void
-set_other_info_templates (MonoMemPool *mp, MonoRuntimeGenericContextTemplate *template, int type_argc,
+set_other_info_templates (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
MonoRuntimeGenericContextOtherInfoTemplate *oti)
{
g_assert (type_argc >= 0);
/* FIXME: quadratic! */
while (length < type_argc) {
- template->method_templates = g_slist_append_mempool (mp, template->method_templates, NULL);
+ template->method_templates = g_slist_append_image (image, template->method_templates, NULL);
length++;
}
}
/*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static int
template_get_max_argc (MonoRuntimeGenericContextTemplate *template)
}
/*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static MonoRuntimeGenericContextOtherInfoTemplate*
rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate *template, int type_argc, int slot)
}
/*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static int
rgctx_template_num_other_infos (MonoRuntimeGenericContextTemplate *template, int type_argc)
* 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;
}
/*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static MonoRuntimeGenericContextTemplate*
class_lookup_rgctx_template (MonoClass *class)
}
/*
- * 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);
}
-/*
- * LOCKING: loader lock
- */
static MonoRuntimeGenericContextTemplate*
alloc_template (MonoClass *class)
{
return mono_image_alloc0 (class->image, size);
}
-/*
- * LOCKING: loader lock
- */
static MonoRuntimeGenericContextOtherInfoTemplate*
alloc_oti (MonoImage *image)
{
#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 type_argc,
g_assert (slot >= 0);
g_assert (data);
- mono_loader_lock ();
-
i = 0;
while (i <= slot) {
if (i > 0)
++i;
}
- mono_loader_unlock ();
-
g_assert (!(*oti)->data);
(*oti)->data = data;
(*oti)->info_type = info_type;
- set_other_info_templates (image->mempool, template, type_argc, list);
+ set_other_info_templates (image, template, type_argc, list);
if (data == MONO_RGCTX_SLOT_USED_MARKER)
++num_markers;
case MONO_RGCTX_INFO_VTABLE:
case MONO_RGCTX_INFO_TYPE:
case MONO_RGCTX_INFO_REFLECTION_TYPE:
- return mono_class_inflate_generic_type_with_mempool (temporary ? NULL : class->image->mempool,
+ 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:
case MONO_RGCTX_INFO_METHOD_RGCTX:
- case MONO_RGCTX_INFO_METHOD_CONTEXT: {
+ 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);
mono_class_init (inflated_class);
- if (method->wrapper_type != MONO_WRAPPER_NONE) {
- g_assert (info_type != MONO_RGCTX_INFO_METHOD_RGCTX);
- g_assert (method->wrapper_type == MONO_WRAPPER_STATIC_RGCTX_INVOKE);
+ g_assert (!method->wrapper_type);
- method = mono_marshal_method_from_wrapper (method);
- method = mono_class_inflate_generic_method (method, context);
- method = mono_marshal_get_static_rgctx_invoke (method);
+ 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);
}
-
- 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;
default:
g_assert_not_reached ();
}
+ /* Not reached, quiet compiler */
+ return NULL;
}
static gpointer
mono_class_get_runtime_generic_context_template (MonoClass *class)
{
MonoRuntimeGenericContextTemplate *parent_template, *template;
- MonoGenericInst *inst;
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_container)
- inst = class->generic_container->context.class_inst;
- else
- inst = NULL;
-
- mono_loader_lock ();
template = alloc_template (class);
- mono_loader_unlock ();
- templates_lock ();
+ mono_loader_lock ();
if (class->parent) {
if (class->parent->generic_class) {
register_generic_subclass (class);
}
- templates_unlock ();
+ mono_loader_unlock ();
return template;
}
* 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)
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
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:
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);
- return mono_method_lookup_rgctx (mono_class_vtable (domain, method->method.method.klass),
- 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;
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 type_argc, int index, gpointer data, int info_type)
}
/*
- * LOCKING: templates lock
+ * LOCKING: loader lock
*/
static int
register_other_info (MonoClass *class, int type_argc, gpointer data, int info_type)
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;
}
static int
MonoRuntimeGenericContextTemplate *rgctx_template =
mono_class_get_runtime_generic_context_template (class);
- MonoRuntimeGenericContextOtherInfoTemplate *oti;
+ MonoRuntimeGenericContextOtherInfoTemplate *oti_list, *oti;
int i;
g_assert (!class->generic_class);
g_assert (class->generic_container || type_argc);
- templates_lock ();
+ mono_loader_lock ();
+
+ oti_list = get_other_info_templates (rgctx_template, type_argc);
- for (oti = get_other_info_templates (rgctx_template, type_argc), i = 0; oti; oti = oti->next, ++i) {
+ for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
gpointer inflated_data;
- if (!oti || oti->info_type != info_type || !oti->data)
+ if (oti->info_type != info_type || !oti->data)
continue;
inflated_data = inflate_other_info (oti, generic_context, class, TRUE);
if (other_info_equal (data, inflated_data, info_type)) {
- templates_unlock ();
- free_inflated_info (oti->info_type, inflated_data);
+ free_inflated_info (info_type, inflated_data);
+ mono_loader_unlock ();
return i;
}
free_inflated_info (info_type, inflated_data);
}
+ /* 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);
return array;
}
-/*
- * LOCKING: domain lock
- */
static gpointer
fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
MonoGenericInst *method_inst)
g_assert (rgctx);
+ mono_domain_lock (domain);
+
/* 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 -= sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
+ size -= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
for (i = 0; ; ++i) {
int offset;
if (method_inst && i == 0)
- offset = sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
+ 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)
+ if (info) {
+ mono_domain_unlock (domain);
return info;
+ }
break;
}
if (!rgctx [offset + 0])
g_assert (!rgctx [rgctx_index]);
+ mono_domain_unlock (domain);
+
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);
/*
if (method_inst)
g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
*/
- info = rgctx [rgctx_index] = instantiate_other_info (domain, &oti, &context, class);
+ /*FIXME We should use CAS here, no need to take a lock.*/
+ mono_domain_lock (domain);
+
+ /* 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);
num_alloced++;
}
- info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
-
mono_domain_unlock (domain);
+ info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
+
return info;
}
gpointer
mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot)
{
- MonoDomain *domain = mrgctx->class_vtable->domain;
gpointer info;
- mono_domain_lock (domain);
-
info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot,
mrgctx->method_inst);
- mono_domain_unlock (domain);
-
return info;
}
*
* 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)
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
g_assert (inflated->declaring);
if (inflated->declaring->is_generic) {
- g_assert (mono_method_get_generic_container (inflated->declaring)->type_params);
-
- if (mono_method_get_generic_container (inflated->declaring)->type_params->constraints)
+ if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
return FALSE;
}
}
return FALSE;
g_assert (method->klass->generic_class->container_class &&
- method->klass->generic_class->container_class->generic_container &&
- method->klass->generic_class->container_class->generic_container->type_params);
+ method->klass->generic_class->container_class->generic_container);
- if (method->klass->generic_class->container_class->generic_container->type_params->constraints)
+ if (has_constraints (method->klass->generic_class->container_class->generic_container))
return FALSE;
}
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);
+
+ for (i = 0; i < type_argc; ++i)
+ type_argv [i] = &mono_defaults.object_class->byval_arg;
+
+ 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;
+ }
+
+ ++lookups;
+ if (!ji)
+ ++failed_lookups;
+
+ return ji;
+}