#include <mono/utils/mono-logger-internal.h>
#include <mono/utils/mono-memory-model.h>
#include <mono/utils/atomic.h>
+#include <mono/utils/bsearch.h>
+
MonoStats mono_stats;
gboolean mono_print_vtable = FALSE;
/* Function supplied by the runtime to find classes by name using information from the AOT file */
static MonoGetClassFromName get_class_from_name = NULL;
-static MonoClass * mono_class_create_from_typedef (MonoImage *image, guint32 type_token);
+static MonoClass * mono_class_create_from_typedef (MonoImage *image, guint32 type_token, MonoError *error);
static gboolean mono_class_get_cached_class_info (MonoClass *klass, MonoCachedClassInfo *res);
static gboolean can_access_type (MonoClass *access_klass, MonoClass *member_klass);
static MonoMethod* find_method_in_metadata (MonoClass *klass, const char *name, int param_count, int flags);
void (*mono_debugger_class_init_func) (MonoClass *klass) = NULL;
-void (*mono_debugger_class_loaded_methods_func) (MonoClass *klass) = NULL;
/*
guint32 string_offset = mono_metadata_decode_row_col (&enclosing->image->tables [MONO_TABLE_TYPEDEF], class_nested - 1, MONO_TYPEDEF_NAME);
const char *nname = mono_metadata_string_heap (enclosing->image, string_offset);
- if (strcmp (nname, name) == 0)
- return mono_class_create_from_typedef (enclosing->image, MONO_TOKEN_TYPE_DEF | class_nested);
+ if (strcmp (nname, name) == 0) {
+ MonoClass *res = mono_class_create_from_typedef (enclosing->image, MONO_TOKEN_TYPE_DEF | class_nested, &error);
+ if (!mono_error_ok (&error)) {
+ mono_loader_set_error_from_mono_error (&error);
+ mono_error_cleanup (&error); /*FIXME don't swallow error message.*/
+ return NULL;
+ }
+ return res;
+ }
i = mono_metadata_nesting_typedef (enclosing->image, enclosing->type_token, i + 1);
}
MonoGenericContainer *container = NULL;
MonoClass *gtd = class->generic_class ? mono_class_get_generic_type_definition (class) : NULL;
+ /*
+ * FIXME: We have a race condition here. It's possible that this function returns
+ * to its caller with `instance_size` set to `0` instead of the actual size. This
+ * is not a problem when the function is called recursively on the same class,
+ * because the size will be initialized by the outer invocation. What follows is a
+ * description of how it can occur in other cases, too. There it is a problem,
+ * because it can lead to the GC being asked to allocate an object of size `0`,
+ * which SGen chokes on. The race condition is triggered infrequently by
+ * `tests/sgen-suspend.cs`.
+ *
+ * This function is called for a class whenever one of its subclasses is inited.
+ * For example, it's called for every subclass of Object. What it does is this:
+ *
+ * if (class->setup_fields_called)
+ * return;
+ * ...
+ * class->instance_size = 0;
+ * ...
+ * class->setup_fields_called = 1;
+ * ... critical point
+ * class->instance_size = actual_instance_size;
+ *
+ * The last two steps are sometimes reversed, but that only changes the way in which
+ * the race condition works.
+ *
+ * Assume thread A goes through this function and makes it to the critical point.
+ * Now thread B runs the function and, since `setup_fields_called` is set, returns
+ * immediately, but `instance_size` is incorrect.
+ *
+ * The other case looks like this:
+ *
+ * if (class->setup_fields_called)
+ * return;
+ * ... critical point X
+ * class->instance_size = 0;
+ * ... critical point Y
+ * class->instance_size = actual_instance_size;
+ * ...
+ * class->setup_fields_called = 1;
+ *
+ * Assume thread A goes through the function and makes it to critical point X. Now
+ * thread B runs through the whole of the function, returning, assuming
+ * `instance_size` is set. At that point thread A gets to run and makes it to
+ * critical point Y, at which time `instance_size` is `0` again, invalidating thread
+ * B's assumption.
+ */
if (class->setup_fields_called)
return;
mono_memory_barrier ();
class->size_inited = 1;
class->fields_inited = 1;
+ class->setup_fields_called = 1;
return;
}
class->methods = methods;
- if (mono_debugger_class_loaded_methods_func)
- mono_debugger_class_loaded_methods_func (class);
-
mono_loader_unlock ();
}
/*FIXME verify all callers if they should switch to mono_class_interface_offset_with_variance*/
int
mono_class_interface_offset (MonoClass *klass, MonoClass *itf) {
- MonoClass **result = bsearch (
+ MonoClass **result = mono_binary_search (
itf,
klass->interfaces_packed,
klass->interface_offsets_count,
* We collect the types needed to build the
* instantiations in interfaces at intervals of 3/5, because 3/5 are
* the generic interfaces needed to implement.
+ *
+ * On 4.5, as an optimization, we don't expand ref classes for the variant generic interfaces
+ * (IEnumerator, IReadOnlyList and IReadOnlyColleciton). The regular dispatch code can handle those cases.
*/
- nifaces = generic_ireadonlylist_class ? 5 : 3;
if (eclass->valuetype) {
+ nifaces = generic_ireadonlylist_class ? 5 : 3;
fill_valuetype_array_derived_types (valuetype_types, eclass, original_rank);
/* IList, ICollection, IEnumerable, IReadOnlyList`1 */
int idepth = eclass->idepth;
if (!internal_enumerator)
idepth--;
+ nifaces = generic_ireadonlylist_class ? 2 : 3;
// FIXME: This doesn't seem to work/required for generic params
if (!(eclass->this_arg.type == MONO_TYPE_VAR || eclass->this_arg.type == MONO_TYPE_MVAR || (eclass->image->dynamic && !eclass->wastypebuilder)))
interfaces [i + 0] = inflate_class_one_arg (mono_defaults.generic_ilist_class, iface);
interfaces [i + 1] = inflate_class_one_arg (generic_icollection_class, iface);
- interfaces [i + 2] = inflate_class_one_arg (generic_ienumerable_class, iface);
- if (generic_ireadonlylist_class) {
- interfaces [i + 3] = inflate_class_one_arg (generic_ireadonlylist_class, iface);
- interfaces [i + 4] = inflate_class_one_arg (generic_ireadonlycollection_class, iface);
+
+ if (eclass->valuetype) {
+ interfaces [i + 2] = inflate_class_one_arg (generic_ienumerable_class, iface);
+ if (generic_ireadonlylist_class) {
+ interfaces [i + 3] = inflate_class_one_arg (generic_ireadonlylist_class, iface);
+ interfaces [i + 4] = inflate_class_one_arg (generic_ireadonlycollection_class, iface);
+ }
+ } else {
+ if (!generic_ireadonlylist_class)
+ interfaces [i + 2] = inflate_class_one_arg (generic_ienumerable_class, iface);
}
}
if (internal_enumerator) {
if (class->vtable)
return;
- if (mono_debug_using_mono_debugger ())
- /* The debugger currently depends on this */
- mono_class_setup_methods (class);
-
if (MONO_CLASS_IS_INTERFACE (class)) {
/* This sets method->slot for all methods if this is an interface */
mono_class_setup_methods (class);
#ifndef DISABLE_COM
/*
- * COM initialization (using mono_init_com_types) is delayed until needed.
+ * COM initialization is delayed until needed.
* However when a [ComImport] attribute is present on a type it will trigger
* the initialization. This is not a problem unless the BCL being executed
* lacks the types that COM depends on (e.g. Variant on Silverlight).
}
/* FIXME : we should add an extra checks to ensure COM can be initialized properly before continuing */
- mono_init_com_types ();
}
#endif /*DISABLE_COM*/
if (MONO_CLASS_IS_IMPORT (class)) {
init_com_from_comimport (class);
if (parent == mono_defaults.object_class)
- parent = mono_defaults.com_object_class;
+ parent = mono_class_get_com_object_class ();
}
#endif
if (!parent) {
* - supertypes: array of classes: each element has a class in the hierarchy
* starting from @class up to System.Object
*
- * LOCKING: this assumes the loader lock is held
+ * LOCKING: This function is atomic, in case of contention we waste memory.
*/
void
mono_class_setup_supertypes (MonoClass *class)
int ms;
MonoClass **supertypes;
- if (class->supertypes)
+ mono_atomic_load_acquire (supertypes, void*, &class->supertypes);
+ if (supertypes)
return;
if (class->parent && !class->parent->supertypes)
return TRUE;
}
+
+static void
+mono_class_set_failure_and_error (MonoClass *class, MonoError *error, const char *msg)
+{
+ mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup (msg));
+ mono_error_set_type_load_class (error, class, msg);
+}
+
static void
mono_class_set_failure_from_loader_error (MonoClass *class, MonoError *error, char *msg)
{
* mono_class_create_from_typedef:
* @image: image where the token is valid
* @type_token: typedef token
+ * @error: used to return any error found while creating the type
*
* Create the MonoClass* representing the specified type token.
* @type_token must be a TypeDef token.
* FIXME: don't return NULL on failure, just the the caller figure it out.
*/
static MonoClass *
-mono_class_create_from_typedef (MonoImage *image, guint32 type_token)
+mono_class_create_from_typedef (MonoImage *image, guint32 type_token, MonoError *error)
{
- MonoError error;
MonoTableInfo *tt = &image->tables [MONO_TABLE_TYPEDEF];
MonoClass *class, *parent = NULL;
guint32 cols [MONO_TYPEDEF_SIZE];
guint32 field_last, method_last;
guint32 nesting_tokeen;
- if (mono_metadata_token_table (type_token) != MONO_TABLE_TYPEDEF || tidx > tt->rows)
+ mono_error_init (error);
+
+ if (mono_metadata_token_table (type_token) != MONO_TABLE_TYPEDEF || tidx > tt->rows) {
+ mono_error_set_bad_image (error, image, "Invalid typedef token %x", type_token);
+ g_assert (!mono_loader_get_last_error ());
return NULL;
+ }
mono_loader_lock ();
if ((class = mono_internal_hash_table_lookup (&image->class_cache, GUINT_TO_POINTER (type_token)))) {
mono_loader_unlock ();
+ g_assert (!mono_loader_get_last_error ());
return class;
}
parent = mono_class_get_full (image, parent_token, context);
if (parent == NULL) {
- mono_class_set_failure_from_loader_error (class, &error, g_strdup_printf ("Could not load parent, token is %x", parent_token));
+ mono_class_set_failure_from_loader_error (class, error, g_strdup_printf ("Could not load parent, token is %x", parent_token));
goto parent_failure;
}
for (tmp = parent; tmp; tmp = tmp->parent) {
if (tmp == class) {
- mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Cycle found while resolving parent"));
+ mono_class_set_failure_and_error (class, error, "Cycle found while resolving parent");
goto parent_failure;
}
if (class->generic_container && tmp->generic_class && tmp->generic_class->container_class == class) {
- mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Parent extends generic instance of this type"));
+ mono_class_set_failure_and_error (class, error, "Parent extends generic instance of this type");
goto parent_failure;
}
}
* so it has to come after setup_mono_type ().
*/
if ((nesting_tokeen = mono_metadata_nested_in_typedef (image, type_token))) {
- class->nested_in = mono_class_create_from_typedef (image, nesting_tokeen);
- if (!class->nested_in) {
- mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Could not load nestedin type"));
+ class->nested_in = mono_class_create_from_typedef (image, nesting_tokeen, error);
+ if (!mono_error_ok (error)) {
+ /*FIXME implement a mono_class_set_failure_from_mono_error */
+ mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup (mono_error_get_message (error)));
mono_loader_unlock ();
mono_profiler_class_loaded (class, MONO_PROFILE_FAILED);
+ g_assert (!mono_loader_get_last_error ());
return NULL;
}
}
if (!class->enumtype) {
if (!mono_metadata_interfaces_from_typedef_full (
image, type_token, &interfaces, &icount, FALSE, context)){
- mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup ("Could not load interfaces"));
+ mono_class_set_failure_from_loader_error (class, error, g_strdup ("Could not load interfaces"));
mono_loader_unlock ();
mono_profiler_class_loaded (class, MONO_PROFILE_FAILED);
return NULL;
if (!enum_basetype) {
/*set it to a default value as the whole runtime can't handle this to be null*/
class->cast_class = class->element_class = mono_defaults.int32_class;
- mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL);
+ mono_class_set_failure_and_error (class, error, "Could not enum basetype");
mono_loader_unlock ();
mono_profiler_class_loaded (class, MONO_PROFILE_FAILED);
+ g_assert (!mono_loader_get_last_error ());
return NULL;
}
class->cast_class = class->element_class = mono_class_from_mono_type (enum_basetype);
* work.
*/
if (class->generic_container && !mono_metadata_load_generic_param_constraints_full (image, type_token, class->generic_container)){
- char *class_name = g_strdup_printf("%s.%s", class->name_space, class->name);
- char *error = concat_two_strings_with_zero (class->image, class_name, class->image->assembly_name);
- mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, error);
- g_free (class_name);
+ mono_class_set_failure_from_loader_error (class, error, g_strdup ("Could not load generic parameter constraints"));
mono_loader_unlock ();
mono_profiler_class_loaded (class, MONO_PROFILE_FAILED);
+ g_assert (!mono_loader_get_last_error ());
return NULL;
}
mono_loader_unlock ();
mono_profiler_class_loaded (class, MONO_PROFILE_OK);
+ g_assert (!mono_loader_get_last_error ());
return class;
mono_class_setup_mono_type (class);
mono_loader_unlock ();
mono_profiler_class_loaded (class, MONO_PROFILE_FAILED);
+ g_assert (!mono_loader_get_last_error ());
return NULL;
-
}
/** is klass Nullable<T>? */
{
if (!klass->inited)
mono_class_init (klass);
+ /* This can happen with dynamically created types */
+ if (!klass->fields_inited)
+ mono_class_setup_fields_locking (klass);
/* in arrays, sizes.class_size is unioned with element_size
* and arrays have no static fields
switch (type_token & 0xff000000){
case MONO_TOKEN_TYPE_DEF:
- class = mono_class_create_from_typedef (image, type_token);
+ class = mono_class_create_from_typedef (image, type_token, &error);
+ if (!mono_error_ok (&error)) {
+ mono_loader_set_error_from_mono_error (&error);
+ /*FIXME don't swallow the error message*/
+ mono_error_cleanup (&error);
+ return NULL;
+ }
break;
case MONO_TOKEN_TYPE_REF:
class = mono_class_from_typeref (image, type_token);
* @name: the type short name.
*
* Obtains a MonoClass with a given namespace and a given name which
- * is located in the given MonoImage.
+ * is located in the given MonoImage.
+ *
+ * To reference nested classes, use the "/" character as a separator.
+ * For example use "Foo/Bar" to reference the class Bar that is nested
+ * inside Foo, like this: "class Foo { class Bar {} }".
*/
MonoClass *
mono_class_from_name (MonoImage *image, const char* name_space, const char *name)
return class;
}
-/*FIXME test for interfaces with variant generic arguments*/
+/**
+ * mono_class_is_subclass_of:
+ * @klass: class to probe if it is a subclass of another one
+ * @klassc: the class we suspect is the base class
+ * @check_interfaces: whether we should perform interface checks
+ *
+ * This method determines whether @klass is a subclass of @klassc.
+ *
+ * If the @check_interfaces flag is set, then if @klassc is an interface
+ * this method return true if the @klass implements the interface or
+ * if @klass is an interface, if one of its base classes is @klass.
+ *
+ * If @check_interfaces is false then, then if @klass is not an interface
+ * then it returns true if the @klass is a subclass of @klassc.
+ *
+ * if @klass is an interface and @klassc is System.Object, then this function
+ * return true.
+ *
+ */
gboolean
mono_class_is_subclass_of (MonoClass *klass, MonoClass *klassc,
gboolean check_interfaces)
{
+/*FIXME test for interfaces with variant generic arguments*/
+
if (check_interfaces && MONO_CLASS_IS_INTERFACE (klassc) && !MONO_CLASS_IS_INTERFACE (klass)) {
if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, klassc->interface_id))
return TRUE;
MonoMethod** method;
if (!iter)
return NULL;
- if (klass->methods || !MONO_CLASS_HAS_STATIC_METADATA (klass) || mono_debug_using_mono_debugger ()) {
+ if (klass->methods || !MONO_CLASS_HAS_STATIC_METADATA (klass)) {
if (!*iter) {
mono_class_setup_methods (klass);
/*
MonoClass*
mono_class_get_nested_types (MonoClass* klass, gpointer *iter)
{
+ MonoError error;
GList *item;
int i;
MonoClass* nclass;
guint32 cols [MONO_NESTED_CLASS_SIZE];
mono_metadata_decode_row (&klass->image->tables [MONO_TABLE_NESTEDCLASS], i - 1, cols, MONO_NESTED_CLASS_SIZE);
- nclass = mono_class_create_from_typedef (klass->image, MONO_TOKEN_TYPE_DEF | cols [MONO_NESTED_CLASS_NESTED]);
- if (!nclass) {
- mono_loader_clear_error ();
+ nclass = mono_class_create_from_typedef (klass->image, MONO_TOKEN_TYPE_DEF | cols [MONO_NESTED_CLASS_NESTED], &error);
+ if (!mono_error_ok (&error)) {
+ /*FIXME don't swallow the error message*/
+ mono_error_cleanup (&error);
+
i = mono_metadata_nesting_typedef (klass->image, klass->type_token, i + 1);
continue;
}
return NULL;
}
+
+/**
+ * mono_class_is_delegate
+ * @klass: the MonoClass to act on
+ *
+ * Returns: true if the MonoClass represents a System.Delegate.
+ */
+mono_bool
+mono_class_is_delegate (MonoClass *klass)
+{
+ return klass->delegate;
+}
+
+/**
+ * mono_class_implements_interface
+ * @klass: The MonoClass to act on
+ * @interface: The interface to check if @klass implements.
+ *
+ * Returns: true if @klass implements @interface.
+ */
+mono_bool
+mono_class_implements_interface (MonoClass* klass, MonoClass* iface)
+{
+ return mono_class_is_assignable_from (iface, klass);
+}
+
/**
* mono_field_get_name:
* @field: the MonoClassField to act on