Permalink
Find file
10750 lines (9375 sloc) 322 KB
/*
* class.c: Class management for the Mono runtime
*
* Author:
* Miguel de Icaza (miguel@ximian.com)
*
* Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
* Copyright 2004-2009 Novell, Inc (http://www.novell.com)
* Copyright 2012 Xamarin Inc (http://www.xamarin.com)
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
#include <config.h>
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#include <glib.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <mono/metadata/image.h>
#include <mono/metadata/image-internals.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/assembly-internals.h>
#include <mono/metadata/metadata.h>
#include <mono/metadata/metadata-internals.h>
#include <mono/metadata/profiler-private.h>
#include <mono/metadata/tabledefs.h>
#include <mono/metadata/tokentype.h>
#include <mono/metadata/class-internals.h>
#include <mono/metadata/object.h>
#include <mono/metadata/appdomain.h>
#include <mono/metadata/mono-endian.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/reflection.h>
#include <mono/metadata/exception.h>
#include <mono/metadata/security-manager.h>
#include <mono/metadata/security-core-clr.h>
#include <mono/metadata/attrdefs.h>
#include <mono/metadata/gc-internals.h>
#include <mono/metadata/verify-internals.h>
#include <mono/metadata/mono-debug.h>
#include <mono/utils/mono-counters.h>
#include <mono/utils/mono-string.h>
#include <mono/utils/mono-error-internals.h>
#include <mono/utils/mono-logger-internals.h>
#include <mono/utils/mono-memory-model.h>
#include <mono/utils/atomic.h>
#include <mono/utils/bsearch.h>
#include <mono/utils/checked-build.h>
MonoStats mono_stats;
gboolean mono_print_vtable = FALSE;
gboolean mono_align_small_structs = FALSE;
/* Statistics */
guint32 inflated_classes_size, inflated_methods_size;
guint32 classes_size, class_ext_size, class_ext_count;
guint32 class_def_count, class_gtd_count, class_ginst_count, class_gparam_count, class_array_count, class_pointer_count;
/* Low level lock which protects data structures in this module */
static mono_mutex_t classes_mutex;
/* 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, 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);
static int generic_array_methods (MonoClass *klass);
static void setup_generic_array_ifaces (MonoClass *klass, MonoClass *iface, MonoMethod **methods, int pos);
static MonoMethod* mono_class_get_virtual_methods (MonoClass* klass, gpointer *iter);
static char* mono_assembly_name_from_token (MonoImage *image, guint32 type_token);
static void mono_field_resolve_type (MonoClassField *field, MonoError *error);
static guint32 mono_field_resolve_flags (MonoClassField *field);
static void mono_class_setup_vtable_full (MonoClass *klass, GList *in_setup);
static void mono_generic_class_setup_parent (MonoClass *klass, MonoClass *gklass);
static gboolean mono_class_set_failure (MonoClass *klass, MonoErrorBoxed *boxed_error);
/*
We use gclass recording to allow recursive system f types to be referenced by a parent.
Given the following type hierarchy:
class TextBox : TextBoxBase<TextBox> {}
class TextBoxBase<T> : TextInput<TextBox> where T : TextBoxBase<T> {}
class TextInput<T> : Input<T> where T: TextInput<T> {}
class Input<T> {}
The runtime tries to load TextBoxBase<>.
To load TextBoxBase<> to do so it must resolve the parent which is TextInput<TextBox>.
To instantiate TextInput<TextBox> it must resolve TextInput<> and TextBox.
To load TextBox it must resolve the parent which is TextBoxBase<TextBox>.
At this point the runtime must instantiate TextBoxBase<TextBox>. Both types are partially loaded
at this point, iow, both are registered in the type map and both and a NULL parent. This means
that the resulting generic instance will have a NULL parent, which is wrong and will cause breakage.
To fix that what we do is to record all generic instantes created while resolving the parent of
any generic type definition and, after resolved, correct the parent field if needed.
*/
static int record_gclass_instantiation;
static GSList *gclass_recorded_list;
typedef gboolean (*gclass_record_func) (MonoClass*, void*);
/* This TLS variable points to a GSList of classes which have setup_fields () executing */
static MonoNativeTlsKey setup_fields_tls_id;
static MonoNativeTlsKey init_pending_tls_id;
static inline void
classes_lock (void)
{
mono_locks_os_acquire (&classes_mutex, ClassesLock);
}
static inline void
classes_unlock (void)
{
mono_locks_os_release (&classes_mutex, ClassesLock);
}
/*
* LOCKING: loader lock must be held until pairing disable_gclass_recording is called.
*/
static void
enable_gclass_recording (void)
{
++record_gclass_instantiation;
}
/*
* LOCKING: loader lock must be held since pairing enable_gclass_recording was called.
*/
static void
disable_gclass_recording (gclass_record_func func, void *user_data)
{
GSList **head = &gclass_recorded_list;
g_assert (record_gclass_instantiation > 0);
--record_gclass_instantiation;
while (*head) {
GSList *node = *head;
if (func ((MonoClass*)node->data, user_data)) {
*head = node->next;
g_slist_free_1 (node);
} else {
head = &node->next;
}
}
/* We automatically discard all recorded gclasses when disabled. */
if (!record_gclass_instantiation && gclass_recorded_list) {
g_slist_free (gclass_recorded_list);
gclass_recorded_list = NULL;
}
}
/**
* mono_class_from_typeref:
* @image: a MonoImage
* @type_token: a TypeRef token
*
* Creates the MonoClass* structure representing the type defined by
* the typeref token valid inside @image.
* Returns: The MonoClass* representing the typeref token, NULL ifcould
* not be loaded.
*/
MonoClass *
mono_class_from_typeref (MonoImage *image, guint32 type_token)
{
MonoError error;
MonoClass *klass = mono_class_from_typeref_checked (image, type_token, &error);
g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/
return klass;
}
/**
* mono_class_from_typeref_checked:
* @image: a MonoImage
* @type_token: a TypeRef token
* @error: error return code, if any.
*
* Creates the MonoClass* structure representing the type defined by
* the typeref token valid inside @image.
*
* Returns: The MonoClass* representing the typeref token, NULL if it could
* not be loaded with the @error value filled with the information about the
* error.
*/
MonoClass *
mono_class_from_typeref_checked (MonoImage *image, guint32 type_token, MonoError *error)
{
guint32 cols [MONO_TYPEREF_SIZE];
MonoTableInfo *t = &image->tables [MONO_TABLE_TYPEREF];
guint32 idx;
const char *name, *nspace;
MonoClass *res = NULL;
MonoImage *module;
mono_error_init (error);
if (!mono_verifier_verify_typeref_row (image, (type_token & 0xffffff) - 1, error))
return NULL;
mono_metadata_decode_row (t, (type_token&0xffffff)-1, cols, MONO_TYPEREF_SIZE);
name = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAME]);
nspace = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAMESPACE]);
idx = cols [MONO_TYPEREF_SCOPE] >> MONO_RESOLUTION_SCOPE_BITS;
switch (cols [MONO_TYPEREF_SCOPE] & MONO_RESOLUTION_SCOPE_MASK) {
case MONO_RESOLUTION_SCOPE_MODULE:
/*
LAMESPEC The spec says that a null module resolution scope should go through the exported type table.
This is not the observed behavior of existing implementations.
The defacto behavior is that it's just a typedef in disguise.
*/
/* a typedef in disguise */
res = mono_class_from_name_checked (image, nspace, name, error);
goto done;
case MONO_RESOLUTION_SCOPE_MODULEREF:
module = mono_image_load_module_checked (image, idx, error);
if (module)
res = mono_class_from_name_checked (module, nspace, name, error);
goto done;
case MONO_RESOLUTION_SCOPE_TYPEREF: {
MonoClass *enclosing;
GList *tmp;
if (idx == mono_metadata_token_index (type_token)) {
mono_error_set_bad_image (error, image, "Image with self-referencing typeref token %08x.", type_token);
return NULL;
}
enclosing = mono_class_from_typeref_checked (image, MONO_TOKEN_TYPE_REF | idx, error);
return_val_if_nok (error, NULL);
GList *nested_classes = mono_class_get_nested_classes_property (enclosing);
if (enclosing->nested_classes_inited && nested_classes) {
/* Micro-optimization: don't scan the metadata tables if enclosing is already inited */
for (tmp = nested_classes; tmp; tmp = tmp->next) {
res = (MonoClass *)tmp->data;
if (strcmp (res->name, name) == 0)
return res;
}
} else {
/* Don't call mono_class_init as we might've been called by it recursively */
int i = mono_metadata_nesting_typedef (enclosing->image, enclosing->type_token, 1);
while (i) {
guint32 class_nested = mono_metadata_decode_row_col (&enclosing->image->tables [MONO_TABLE_NESTEDCLASS], i - 1, MONO_NESTED_CLASS_NESTED);
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, error);
i = mono_metadata_nesting_typedef (enclosing->image, enclosing->type_token, i + 1);
}
}
g_warning ("TypeRef ResolutionScope not yet handled (%d) for %s.%s in image %s", idx, nspace, name, image->name);
goto done;
}
case MONO_RESOLUTION_SCOPE_ASSEMBLYREF:
break;
}
if (idx > image->tables [MONO_TABLE_ASSEMBLYREF].rows) {
mono_error_set_bad_image (error, image, "Image with invalid assemblyref token %08x.", idx);
return NULL;
}
if (!image->references || !image->references [idx - 1])
mono_assembly_load_reference (image, idx - 1);
g_assert (image->references [idx - 1]);
/* If the assembly did not load, register this as a type load exception */
if (image->references [idx - 1] == REFERENCE_MISSING){
MonoAssemblyName aname;
char *human_name;
mono_assembly_get_assemblyref (image, idx - 1, &aname);
human_name = mono_stringify_assembly_name (&aname);
mono_error_set_assembly_load_simple (error, human_name, image->assembly ? image->assembly->ref_only : FALSE);
return NULL;
}
res = mono_class_from_name_checked (image->references [idx - 1]->image, nspace, name, error);
done:
/* Generic case, should be avoided for when a better error is possible. */
if (!res && mono_error_ok (error)) {
char *name = mono_class_name_from_token (image, type_token);
char *assembly = mono_assembly_name_from_token (image, type_token);
mono_error_set_type_load_name (error, name, assembly, "Could not resolve type with token %08x", type_token);
}
return res;
}
static void *
mono_image_memdup (MonoImage *image, void *data, guint size)
{
void *res = mono_image_alloc (image, size);
memcpy (res, data, size);
return res;
}
/* Copy everything mono_metadata_free_array free. */
MonoArrayType *
mono_dup_array_type (MonoImage *image, MonoArrayType *a)
{
if (image) {
a = (MonoArrayType *)mono_image_memdup (image, a, sizeof (MonoArrayType));
if (a->sizes)
a->sizes = (int *)mono_image_memdup (image, a->sizes, a->numsizes * sizeof (int));
if (a->lobounds)
a->lobounds = (int *)mono_image_memdup (image, a->lobounds, a->numlobounds * sizeof (int));
} else {
a = (MonoArrayType *)g_memdup (a, sizeof (MonoArrayType));
if (a->sizes)
a->sizes = (int *)g_memdup (a->sizes, a->numsizes * sizeof (int));
if (a->lobounds)
a->lobounds = (int *)g_memdup (a->lobounds, a->numlobounds * sizeof (int));
}
return a;
}
/* Copy everything mono_metadata_free_method_signature free. */
MonoMethodSignature*
mono_metadata_signature_deep_dup (MonoImage *image, MonoMethodSignature *sig)
{
int i;
sig = mono_metadata_signature_dup_full (image, sig);
sig->ret = mono_metadata_type_dup (image, sig->ret);
for (i = 0; i < sig->param_count; ++i)
sig->params [i] = mono_metadata_type_dup (image, sig->params [i]);
return sig;
}
static void
_mono_type_get_assembly_name (MonoClass *klass, GString *str)
{
MonoAssembly *ta = klass->image->assembly;
char *name;
name = mono_stringify_assembly_name (&ta->aname);
g_string_append_printf (str, ", %s", name);
g_free (name);
}
static inline void
mono_type_name_check_byref (MonoType *type, GString *str)
{
if (type->byref)
g_string_append_c (str, '&');
}
/**
* mono_identifier_escape_type_name_chars:
* @str: a destination string
* @identifier: an IDENTIFIER in internal form
*
* Returns: str.
*
* The displayed form of the identifier is appended to str.
*
* The displayed form of an identifier has the characters ,+&*[]\
* that have special meaning in type names escaped with a preceeding
* backslash (\) character.
*/
static GString*
mono_identifier_escape_type_name_chars (GString* str, const char* identifier)
{
if (!identifier)
return str;
size_t n = str->len;
// reserve space for common case: there will be no escaped characters.
g_string_set_size(str, n + strlen(identifier));
g_string_set_size(str, n);
for (const char* s = identifier; *s != 0 ; s++) {
switch (*s) {
case ',':
case '+':
case '&':
case '*':
case '[':
case ']':
case '\\':
g_string_append_c (str, '\\');
g_string_append_c (str, *s);
break;
default:
g_string_append_c (str, *s);
break;
}
}
return str;
}
static void
mono_type_get_name_recurse (MonoType *type, GString *str, gboolean is_recursed,
MonoTypeNameFormat format)
{
MonoClass *klass;
switch (type->type) {
case MONO_TYPE_ARRAY: {
int i, rank = type->data.array->rank;
MonoTypeNameFormat nested_format;
nested_format = format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ?
MONO_TYPE_NAME_FORMAT_FULL_NAME : format;
mono_type_get_name_recurse (
&type->data.array->eklass->byval_arg, str, FALSE, nested_format);
g_string_append_c (str, '[');
if (rank == 1)
g_string_append_c (str, '*');
for (i = 1; i < rank; i++)
g_string_append_c (str, ',');
g_string_append_c (str, ']');
mono_type_name_check_byref (type, str);
if (format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED)
_mono_type_get_assembly_name (type->data.array->eklass, str);
break;
}
case MONO_TYPE_SZARRAY: {
MonoTypeNameFormat nested_format;
nested_format = format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ?
MONO_TYPE_NAME_FORMAT_FULL_NAME : format;
mono_type_get_name_recurse (
&type->data.klass->byval_arg, str, FALSE, nested_format);
g_string_append (str, "[]");
mono_type_name_check_byref (type, str);
if (format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED)
_mono_type_get_assembly_name (type->data.klass, str);
break;
}
case MONO_TYPE_PTR: {
MonoTypeNameFormat nested_format;
nested_format = format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED ?
MONO_TYPE_NAME_FORMAT_FULL_NAME : format;
mono_type_get_name_recurse (
type->data.type, str, FALSE, nested_format);
g_string_append_c (str, '*');
mono_type_name_check_byref (type, str);
if (format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED)
_mono_type_get_assembly_name (mono_class_from_mono_type (type->data.type), str);
break;
}
case MONO_TYPE_VAR:
case MONO_TYPE_MVAR:
if (!mono_generic_param_info (type->data.generic_param))
g_string_append_printf (str, "%s%d", type->type == MONO_TYPE_VAR ? "!" : "!!", type->data.generic_param->num);
else
g_string_append (str, mono_generic_param_info (type->data.generic_param)->name);
mono_type_name_check_byref (type, str);
break;
default:
klass = mono_class_from_mono_type (type);
if (klass->nested_in) {
mono_type_get_name_recurse (
&klass->nested_in->byval_arg, str, TRUE, format);
if (format == MONO_TYPE_NAME_FORMAT_IL)
g_string_append_c (str, '.');
else
g_string_append_c (str, '+');
} else if (*klass->name_space) {
if (format == MONO_TYPE_NAME_FORMAT_IL)
g_string_append (str, klass->name_space);
else
mono_identifier_escape_type_name_chars (str, klass->name_space);
g_string_append_c (str, '.');
}
if (format == MONO_TYPE_NAME_FORMAT_IL) {
char *s = strchr (klass->name, '`');
int len = s ? s - klass->name : strlen (klass->name);
g_string_append_len (str, klass->name, len);
} else {
mono_identifier_escape_type_name_chars (str, klass->name);
}
if (is_recursed)
break;
if (mono_class_is_ginst (klass)) {
MonoGenericClass *gclass = mono_class_get_generic_class (klass);
MonoGenericInst *inst = gclass->context.class_inst;
MonoTypeNameFormat nested_format;
int i;
nested_format = format == MONO_TYPE_NAME_FORMAT_FULL_NAME ?
MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED : format;
if (format == MONO_TYPE_NAME_FORMAT_IL)
g_string_append_c (str, '<');
else
g_string_append_c (str, '[');
for (i = 0; i < inst->type_argc; i++) {
MonoType *t = inst->type_argv [i];
if (i)
g_string_append_c (str, ',');
if ((nested_format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) &&
(t->type != MONO_TYPE_VAR) && (type->type != MONO_TYPE_MVAR))
g_string_append_c (str, '[');
mono_type_get_name_recurse (inst->type_argv [i], str, FALSE, nested_format);
if ((nested_format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) &&
(t->type != MONO_TYPE_VAR) && (type->type != MONO_TYPE_MVAR))
g_string_append_c (str, ']');
}
if (format == MONO_TYPE_NAME_FORMAT_IL)
g_string_append_c (str, '>');
else
g_string_append_c (str, ']');
} else if (mono_class_is_gtd (klass) &&
(format != MONO_TYPE_NAME_FORMAT_FULL_NAME) &&
(format != MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED)) {
int i;
if (format == MONO_TYPE_NAME_FORMAT_IL)
g_string_append_c (str, '<');
else
g_string_append_c (str, '[');
for (i = 0; i < mono_class_get_generic_container (klass)->type_argc; i++) {
if (i)
g_string_append_c (str, ',');
g_string_append (str, mono_generic_container_get_param_info (mono_class_get_generic_container (klass), i)->name);
}
if (format == MONO_TYPE_NAME_FORMAT_IL)
g_string_append_c (str, '>');
else
g_string_append_c (str, ']');
}
mono_type_name_check_byref (type, str);
if ((format == MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED) &&
(type->type != MONO_TYPE_VAR) && (type->type != MONO_TYPE_MVAR))
_mono_type_get_assembly_name (klass, str);
break;
}
}
/**
* mono_type_get_name_full:
* @type: a type
* @format: the format for the return string.
*
*
* Returns: The string representation in a number of formats:
*
* if format is MONO_TYPE_NAME_FORMAT_REFLECTION, the return string is
* returned in the formatrequired by System.Reflection, this is the
* inverse of mono_reflection_parse_type ().
*
* if format is MONO_TYPE_NAME_FORMAT_IL, it returns a syntax that can
* be used by the IL assembler.
*
* if format is MONO_TYPE_NAME_FORMAT_FULL_NAME
*
* if format is MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED
*/
char*
mono_type_get_name_full (MonoType *type, MonoTypeNameFormat format)
{
GString* result;
result = g_string_new ("");
mono_type_get_name_recurse (type, result, FALSE, format);
return g_string_free (result, FALSE);
}
/**
* mono_type_get_full_name:
* @class: a class
*
* Returns: The string representation for type as required by System.Reflection.
* The inverse of mono_reflection_parse_type ().
*/
char *
mono_type_get_full_name (MonoClass *klass)
{
return mono_type_get_name_full (mono_class_get_type (klass), MONO_TYPE_NAME_FORMAT_REFLECTION);
}
/**
* mono_type_get_name:
* @type: a type
*
* Returns: The string representation for type as it would be represented in IL code.
*/
char*
mono_type_get_name (MonoType *type)
{
return mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_IL);
}
/*
* mono_type_get_underlying_type:
* @type: a type
*
* Returns: The MonoType for the underlying integer type if @type
* is an enum and byref is false, otherwise the type itself.
*/
MonoType*
mono_type_get_underlying_type (MonoType *type)
{
if (type->type == MONO_TYPE_VALUETYPE && type->data.klass->enumtype && !type->byref)
return mono_class_enum_basetype (type->data.klass);
if (type->type == MONO_TYPE_GENERICINST && type->data.generic_class->container_class->enumtype && !type->byref)
return mono_class_enum_basetype (type->data.generic_class->container_class);
return type;
}
/**
* mono_class_is_open_constructed_type:
* @type: a type
*
* Returns: TRUE if type represents a generics open constructed type.
* IOW, not all type parameters required for the instantiation have
* been provided or it's a generic type definition.
*
* An open constructed type means it's a non realizable type. Not to
* be mixed up with an abstract type - we can't cast or dispatch to
* an open type, for example.
*/
gboolean
mono_class_is_open_constructed_type (MonoType *t)
{
switch (t->type) {
case MONO_TYPE_VAR:
case MONO_TYPE_MVAR:
return TRUE;
case MONO_TYPE_SZARRAY:
return mono_class_is_open_constructed_type (&t->data.klass->byval_arg);
case MONO_TYPE_ARRAY:
return mono_class_is_open_constructed_type (&t->data.array->eklass->byval_arg);
case MONO_TYPE_PTR:
return mono_class_is_open_constructed_type (t->data.type);
case MONO_TYPE_GENERICINST:
return t->data.generic_class->context.class_inst->is_open;
case MONO_TYPE_CLASS:
case MONO_TYPE_VALUETYPE:
return mono_class_is_gtd (t->data.klass);
default:
return FALSE;
}
}
/*
This is a simple function to catch the most common bad instances of generic types.
Specially those that might lead to further failures in the runtime.
*/
static gboolean
is_valid_generic_argument (MonoType *type)
{
switch (type->type) {
case MONO_TYPE_VOID:
//case MONO_TYPE_TYPEDBYREF:
return FALSE;
default:
return TRUE;
}
}
static MonoType*
inflate_generic_type (MonoImage *image, MonoType *type, MonoGenericContext *context, MonoError *error)
{
mono_error_init (error);
switch (type->type) {
case MONO_TYPE_MVAR: {
MonoType *nt;
int num = mono_type_get_generic_param_num (type);
MonoGenericInst *inst = context->method_inst;
if (!inst)
return NULL;
if (num >= inst->type_argc) {
MonoGenericParamInfo *info = mono_generic_param_info (type->data.generic_param);
mono_error_set_bad_image (error, image, "MVAR %d (%s) cannot be expanded in this context with %d instantiations",
num, info ? info->name : "", inst->type_argc);
return NULL;
}
if (!is_valid_generic_argument (inst->type_argv [num])) {
MonoGenericParamInfo *info = mono_generic_param_info (type->data.generic_param);
mono_error_set_bad_image (error, image, "MVAR %d (%s) cannot be expanded with type 0x%x",
num, info ? info->name : "", inst->type_argv [num]->type);
return NULL;
}
/*
* Note that the VAR/MVAR cases are different from the rest. The other cases duplicate @type,
* while the VAR/MVAR duplicates a type from the context. So, we need to ensure that the
* ->byref and ->attrs from @type are propagated to the returned type.
*/
nt = mono_metadata_type_dup (image, inst->type_argv [num]);
nt->byref = type->byref;
nt->attrs = type->attrs;
return nt;
}
case MONO_TYPE_VAR: {
MonoType *nt;
int num = mono_type_get_generic_param_num (type);
MonoGenericInst *inst = context->class_inst;
if (!inst)
return NULL;
if (num >= inst->type_argc) {
MonoGenericParamInfo *info = mono_generic_param_info (type->data.generic_param);
mono_error_set_bad_image (error, image, "VAR %d (%s) cannot be expanded in this context with %d instantiations",
num, info ? info->name : "", inst->type_argc);
return NULL;
}
if (!is_valid_generic_argument (inst->type_argv [num])) {
MonoGenericParamInfo *info = mono_generic_param_info (type->data.generic_param);
mono_error_set_bad_image (error, image, "VAR %d (%s) cannot be expanded with type 0x%x",
num, info ? info->name : "", inst->type_argv [num]->type);
return NULL;
}
nt = mono_metadata_type_dup (image, inst->type_argv [num]);
nt->byref = type->byref;
nt->attrs = type->attrs;
return nt;
}
case MONO_TYPE_SZARRAY: {
MonoClass *eclass = type->data.klass;
MonoType *nt, *inflated = inflate_generic_type (NULL, &eclass->byval_arg, context, error);
if (!inflated || !mono_error_ok (error))
return NULL;
nt = mono_metadata_type_dup (image, type);
nt->data.klass = mono_class_from_mono_type (inflated);
mono_metadata_free_type (inflated);
return nt;
}
case MONO_TYPE_ARRAY: {
MonoClass *eclass = type->data.array->eklass;
MonoType *nt, *inflated = inflate_generic_type (NULL, &eclass->byval_arg, context, error);
if (!inflated || !mono_error_ok (error))
return NULL;
nt = mono_metadata_type_dup (image, type);
nt->data.array->eklass = mono_class_from_mono_type (inflated);
mono_metadata_free_type (inflated);
return nt;
}
case MONO_TYPE_GENERICINST: {
MonoGenericClass *gclass = type->data.generic_class;
MonoGenericInst *inst;
MonoType *nt;
if (!gclass->context.class_inst->is_open)
return NULL;
inst = mono_metadata_inflate_generic_inst (gclass->context.class_inst, context, error);
return_val_if_nok (error, NULL);
if (inst != gclass->context.class_inst)
gclass = mono_metadata_lookup_generic_class (gclass->container_class, inst, gclass->is_dynamic);
if (gclass == type->data.generic_class)
return NULL;
nt = mono_metadata_type_dup (image, type);
nt->data.generic_class = gclass;
return nt;
}
case MONO_TYPE_CLASS:
case MONO_TYPE_VALUETYPE: {
MonoClass *klass = type->data.klass;
MonoGenericContainer *container = mono_class_try_get_generic_container (klass);
MonoGenericInst *inst;
MonoGenericClass *gclass = NULL;
MonoType *nt;
if (!container)
return NULL;
/* We can't use context->class_inst directly, since it can have more elements */
inst = mono_metadata_inflate_generic_inst (container->context.class_inst, context, error);
return_val_if_nok (error, NULL);
if (inst == container->context.class_inst)
return NULL;
gclass = mono_metadata_lookup_generic_class (klass, inst, image_is_dynamic (klass->image));
nt = mono_metadata_type_dup (image, type);
nt->type = MONO_TYPE_GENERICINST;
nt->data.generic_class = gclass;
return nt;
}
default:
return NULL;
}
return NULL;
}
MonoGenericContext *
mono_generic_class_get_context (MonoGenericClass *gclass)
{
return &gclass->context;
}
MonoGenericContext *
mono_class_get_context (MonoClass *klass)
{
MonoGenericClass *gklass = mono_class_try_get_generic_class (klass);
return gklass ? mono_generic_class_get_context (gklass) : NULL;
}
/*
* mono_class_inflate_generic_type_with_mempool:
* @mempool: a mempool
* @type: a type
* @context: a generics context
* @error: error context
*
* The same as mono_class_inflate_generic_type, but allocates the MonoType
* from mempool if it is non-NULL. If it is NULL, the MonoType is
* allocated on the heap and is owned by the caller.
* The returned type can potentially be the same as TYPE, so it should not be
* modified by the caller, and it should be freed using mono_metadata_free_type ().
*/
MonoType*
mono_class_inflate_generic_type_with_mempool (MonoImage *image, MonoType *type, MonoGenericContext *context, MonoError *error)
{
MonoType *inflated = NULL;
mono_error_init (error);
if (context)
inflated = inflate_generic_type (image, type, context, error);
return_val_if_nok (error, NULL);
if (!inflated) {
MonoType *shared = mono_metadata_get_shared_type (type);
if (shared) {
return shared;
} else {
return mono_metadata_type_dup (image, type);
}
}
mono_stats.inflated_type_count++;
return inflated;
}
/*
* mono_class_inflate_generic_type:
* @type: a type
* @context: a generics context
*
* If @type is a generic type and @context is not NULL, instantiate it using the
* generics context @context.
*
* Returns: The instantiated type or a copy of @type. The returned MonoType is allocated
* on the heap and is owned by the caller. Returns NULL on error.
*
* @deprecated Please use mono_class_inflate_generic_type_checked instead
*/
MonoType*
mono_class_inflate_generic_type (MonoType *type, MonoGenericContext *context)
{
MonoError error;
MonoType *result;
result = mono_class_inflate_generic_type_checked (type, context, &error);
mono_error_cleanup (&error);
return result;
}
/*
* mono_class_inflate_generic_type:
* @type: a type
* @context: a generics context
* @error: error context to use
*
* If @type is a generic type and @context is not NULL, instantiate it using the
* generics context @context.
*
* Returns: The instantiated type or a copy of @type. The returned MonoType is allocated
* on the heap and is owned by the caller.
*/
MonoType*
mono_class_inflate_generic_type_checked (MonoType *type, MonoGenericContext *context, MonoError *error)
{
return mono_class_inflate_generic_type_with_mempool (NULL, type, context, error);
}
/*
* mono_class_inflate_generic_type_no_copy:
*
* Same as inflate_generic_type_with_mempool, but return TYPE if no inflation
* was done.
*/
static MonoType*
mono_class_inflate_generic_type_no_copy (MonoImage *image, MonoType *type, MonoGenericContext *context, MonoError *error)
{
MonoType *inflated = NULL;
mono_error_init (error);
if (context) {
inflated = inflate_generic_type (image, type, context, error);
return_val_if_nok (error, NULL);
}
if (!inflated)
return type;
mono_stats.inflated_type_count++;
return inflated;
}
/*
* mono_class_inflate_generic_class:
*
* Inflate the class @gklass with @context. Set @error on failure.
*/
MonoClass*
mono_class_inflate_generic_class_checked (MonoClass *gklass, MonoGenericContext *context, MonoError *error)
{
MonoClass *res;
MonoType *inflated;
inflated = mono_class_inflate_generic_type_checked (&gklass->byval_arg, context, error);
return_val_if_nok (error, NULL);
res = mono_class_from_mono_type (inflated);
mono_metadata_free_type (inflated);
return res;
}
static MonoGenericContext
inflate_generic_context (MonoGenericContext *context, MonoGenericContext *inflate_with, MonoError *error)
{
MonoGenericInst *class_inst = NULL;
MonoGenericInst *method_inst = NULL;
MonoGenericContext res = { NULL, NULL };
mono_error_init (error);
if (context->class_inst) {
class_inst = mono_metadata_inflate_generic_inst (context->class_inst, inflate_with, error);
if (!mono_error_ok (error))
goto fail;
}
if (context->method_inst) {
method_inst = mono_metadata_inflate_generic_inst (context->method_inst, inflate_with, error);
if (!mono_error_ok (error))
goto fail;
}
res.class_inst = class_inst;
res.method_inst = method_inst;
fail:
return res;
}
/*
* mono_class_inflate_generic_method:
* @method: a generic method
* @context: a generics context
*
* Instantiate the generic method @method using the generics context @context.
*
* Returns: The new instantiated method
*/
MonoMethod *
mono_class_inflate_generic_method (MonoMethod *method, MonoGenericContext *context)
{
return mono_class_inflate_generic_method_full (method, NULL, context);
}
MonoMethod *
mono_class_inflate_generic_method_checked (MonoMethod *method, MonoGenericContext *context, MonoError *error)
{
return mono_class_inflate_generic_method_full_checked (method, NULL, context, error);
}
/**
* mono_class_inflate_generic_method_full:
*
* Instantiate method @method with the generic context @context.
* BEWARE: All non-trivial fields are invalid, including klass, signature, and header.
* Use mono_method_signature () and mono_method_get_header () to get the correct values.
*/
MonoMethod*
mono_class_inflate_generic_method_full (MonoMethod *method, MonoClass *klass_hint, MonoGenericContext *context)
{
MonoError error;
MonoMethod *res = mono_class_inflate_generic_method_full_checked (method, klass_hint, context, &error);
if (!mono_error_ok (&error))
/*FIXME do proper error handling - on this case, kill this function. */
g_error ("Could not inflate generic method due to %s", mono_error_get_message (&error));
return res;
}
/**
* mono_class_inflate_generic_method_full_checked:
* Same as mono_class_inflate_generic_method_full but return failure using @error.
*/
MonoMethod*
mono_class_inflate_generic_method_full_checked (MonoMethod *method, MonoClass *klass_hint, MonoGenericContext *context, MonoError *error)
{
MonoMethod *result;
MonoMethodInflated *iresult, *cached;
MonoMethodSignature *sig;
MonoGenericContext tmp_context;
mono_error_init (error);
/* The `method' has already been instantiated before => we need to peel out the instantiation and create a new context */
while (method->is_inflated) {
MonoGenericContext *method_context = mono_method_get_context (method);
MonoMethodInflated *imethod = (MonoMethodInflated *) method;
tmp_context = inflate_generic_context (method_context, context, error);
return_val_if_nok (error, NULL);
context = &tmp_context;
if (mono_metadata_generic_context_equal (method_context, context))
return method;
method = imethod->declaring;
}
/*
* A method only needs to be inflated if the context has argument for which it is
* parametric. Eg:
*
* class Foo<T> { void Bar(); } - doesn't need to be inflated if only mvars' are supplied
* class Foo { void Bar<T> (); } - doesn't need to be if only vars' are supplied
*
*/
if (!((method->is_generic && context->method_inst) ||
(mono_class_is_gtd (method->klass) && context->class_inst)))
return method;
iresult = g_new0 (MonoMethodInflated, 1);
iresult->context = *context;
iresult->declaring = method;
if (!context->method_inst && method->is_generic)
iresult->context.method_inst = mono_method_get_generic_container (method)->context.method_inst;
if (!context->class_inst) {
g_assert (!mono_class_is_ginst (iresult->declaring->klass));
if (mono_class_is_gtd (iresult->declaring->klass))
iresult->context.class_inst = mono_class_get_generic_container (iresult->declaring->klass)->context.class_inst;
}
/* This can happen with some callers like mono_object_get_virtual_method () */
if (!mono_class_is_gtd (iresult->declaring->klass) && !mono_class_is_ginst (iresult->declaring->klass))
iresult->context.class_inst = NULL;
MonoImageSet *set = mono_metadata_get_image_set_for_method (iresult);
// check cache
mono_image_set_lock (set);
cached = (MonoMethodInflated *)g_hash_table_lookup (set->gmethod_cache, iresult);
mono_image_set_unlock (set);
if (cached) {
g_free (iresult);
return (MonoMethod*)cached;
}
mono_stats.inflated_method_count++;
inflated_methods_size += sizeof (MonoMethodInflated);
sig = mono_method_signature (method);
if (!sig) {
char *name = mono_type_get_full_name (method->klass);
mono_error_set_bad_image (error, method->klass->image, "Could not resolve signature of method %s:%s", name, method->name);
g_free (name);
goto fail;
}
if (sig->pinvoke) {
memcpy (&iresult->method.pinvoke, method, sizeof (MonoMethodPInvoke));
} else {
memcpy (&iresult->method.method, method, sizeof (MonoMethod));
}
result = (MonoMethod *) iresult;
result->is_inflated = TRUE;
result->is_generic = FALSE;
result->sre_method = FALSE;
result->signature = NULL;
if (method->wrapper_type) {
MonoMethodWrapper *mw = (MonoMethodWrapper*)method;
MonoMethodWrapper *resw = (MonoMethodWrapper*)result;
int len = GPOINTER_TO_INT (((void**)mw->method_data) [0]);
resw->method_data = (void **)g_malloc (sizeof (gpointer) * (len + 1));
memcpy (resw->method_data, mw->method_data, sizeof (gpointer) * (len + 1));
}
if (iresult->context.method_inst) {
/* Set the generic_container of the result to the generic_container of method */
MonoGenericContainer *generic_container = mono_method_get_generic_container (method);
if (generic_container && iresult->context.method_inst == generic_container->context.method_inst) {
result->is_generic = 1;
mono_method_set_generic_container (result, generic_container);
}
}
if (klass_hint) {
MonoGenericClass *gklass_hint = mono_class_try_get_generic_class (klass_hint);
if (gklass_hint && (gklass_hint->container_class != method->klass || gklass_hint->context.class_inst != context->class_inst))
klass_hint = NULL;
}
if (mono_class_is_gtd (method->klass))
result->klass = klass_hint;
if (!result->klass) {
MonoType *inflated = inflate_generic_type (NULL, &method->klass->byval_arg, context, error);
if (!mono_error_ok (error))
goto fail;
result->klass = inflated ? mono_class_from_mono_type (inflated) : method->klass;
if (inflated)
mono_metadata_free_type (inflated);
}
/*
* FIXME: This should hold, but it doesn't:
*
* if (result->is_inflated && mono_method_get_context (result)->method_inst &&
* mono_method_get_context (result)->method_inst == mono_method_get_generic_container (((MonoMethodInflated*)result)->declaring)->context.method_inst) {
* g_assert (result->is_generic);
* }
*
* Fixing this here causes other things to break, hence a very
* ugly hack in mini-trampolines.c - see
* is_generic_method_definition().
*/
// check cache
mono_image_set_lock (set);
cached = (MonoMethodInflated *)g_hash_table_lookup (set->gmethod_cache, iresult);
if (!cached) {
g_hash_table_insert (set->gmethod_cache, iresult, iresult);
iresult->owner = set;
cached = iresult;
}
mono_image_set_unlock (set);
return (MonoMethod*)cached;
fail:
g_free (iresult);
return NULL;
}
/**
* mono_get_inflated_method:
*
* Obsolete. We keep it around since it's mentioned in the public API.
*/
MonoMethod*
mono_get_inflated_method (MonoMethod *method)
{
return method;
}
/*
* mono_method_get_context_general:
* @method: a method
* @uninflated: handle uninflated methods?
*
* Returns the generic context of a method or NULL if it doesn't have
* one. For an inflated method that's the context stored in the
* method. Otherwise it's in the method's generic container or in the
* generic container of the method's class.
*/
MonoGenericContext*
mono_method_get_context_general (MonoMethod *method, gboolean uninflated)
{
if (method->is_inflated) {
MonoMethodInflated *imethod = (MonoMethodInflated *) method;
return &imethod->context;
}
if (!uninflated)
return NULL;
if (method->is_generic)
return &(mono_method_get_generic_container (method)->context);
if (mono_class_is_gtd (method->klass))
return &mono_class_get_generic_container (method->klass)->context;
return NULL;
}
/*
* mono_method_get_context:
* @method: a method
*
* Returns the generic context for method if it's inflated, otherwise
* NULL.
*/
MonoGenericContext*
mono_method_get_context (MonoMethod *method)
{
return mono_method_get_context_general (method, FALSE);
}
/*
* mono_method_get_generic_container:
*
* Returns the generic container of METHOD, which should be a generic method definition.
* Returns NULL if METHOD is not a generic method definition.
* LOCKING: Acquires the loader lock.
*/
MonoGenericContainer*
mono_method_get_generic_container (MonoMethod *method)
{
MonoGenericContainer *container;
if (!method->is_generic)
return NULL;
container = (MonoGenericContainer *)mono_image_property_lookup (method->klass->image, method, MONO_METHOD_PROP_GENERIC_CONTAINER);
g_assert (container);
return container;
}
/*
* mono_method_set_generic_container:
*
* Sets the generic container of METHOD to CONTAINER.
* LOCKING: Acquires the image lock.
*/
void
mono_method_set_generic_container (MonoMethod *method, MonoGenericContainer* container)
{
g_assert (method->is_generic);
mono_image_property_insert (method->klass->image, method, MONO_METHOD_PROP_GENERIC_CONTAINER, container);
}
/**
* mono_class_find_enum_basetype:
* @class: The enum class
*
* Determine the basetype of an enum by iterating through its fields. We do this
* in a separate function since it is cheaper than calling mono_class_setup_fields.
*/
static MonoType*
mono_class_find_enum_basetype (MonoClass *klass, MonoError *error)
{
MonoGenericContainer *container = NULL;
MonoImage *m = klass->image;
const int top = mono_class_get_field_count (klass);
int i, first_field_idx;
g_assert (klass->enumtype);
mono_error_init (error);
container = mono_class_try_get_generic_container (klass);
if (mono_class_is_ginst (klass)) {
MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
container = mono_class_get_generic_container (gklass);
g_assert (container);
}
/*
* Fetch all the field information.
*/
first_field_idx = mono_class_get_first_field_idx (klass);
for (i = 0; i < top; i++){
const char *sig;
guint32 cols [MONO_FIELD_SIZE];
int idx = first_field_idx + i;
MonoType *ftype;
/* first_field_idx and idx points into the fieldptr table */
mono_metadata_decode_table_row (m, MONO_TABLE_FIELD, idx, cols, MONO_FIELD_SIZE);
if (cols [MONO_FIELD_FLAGS] & FIELD_ATTRIBUTE_STATIC) //no need to decode static fields
continue;
if (!mono_verifier_verify_field_signature (klass->image, cols [MONO_FIELD_SIGNATURE], NULL)) {
mono_error_set_bad_image (error, klass->image, "Invalid field signature %x", cols [MONO_FIELD_SIGNATURE]);
goto fail;
}
sig = mono_metadata_blob_heap (m, cols [MONO_FIELD_SIGNATURE]);
mono_metadata_decode_value (sig, &sig);
/* FIELD signature == 0x06 */
if (*sig != 0x06) {
mono_error_set_bad_image (error, klass->image, "Invalid field signature %x, expected 0x6 but got %x", cols [MONO_FIELD_SIGNATURE], *sig);
goto fail;
}
ftype = mono_metadata_parse_type_checked (m, container, cols [MONO_FIELD_FLAGS], FALSE, sig + 1, &sig, error);
if (!ftype)
goto fail;
if (mono_class_is_ginst (klass)) {
//FIXME do we leak here?
ftype = mono_class_inflate_generic_type_checked (ftype, mono_class_get_context (klass), error);
if (!mono_error_ok (error))
goto fail;
ftype->attrs = cols [MONO_FIELD_FLAGS];
}
return ftype;
}
mono_error_set_type_load_class (error, klass, "Could not find base type");
fail:
return NULL;
}
/*
* Checks for MonoClass::has_failure without resolving all MonoType's into MonoClass'es
*/
static gboolean
mono_type_has_exceptions (MonoType *type)
{
switch (type->type) {
case MONO_TYPE_CLASS:
case MONO_TYPE_VALUETYPE:
case MONO_TYPE_SZARRAY:
return mono_class_has_failure (type->data.klass);
case MONO_TYPE_ARRAY:
return mono_class_has_failure (type->data.array->eklass);
case MONO_TYPE_GENERICINST:
return mono_class_has_failure (mono_generic_class_get_class (type->data.generic_class));
default:
return FALSE;
}
}
void
mono_error_set_for_class_failure (MonoError *oerror, const MonoClass *klass)
{
g_assert (mono_class_has_failure (klass));
MonoErrorBoxed *box = mono_class_get_exception_data ((MonoClass*)klass);
mono_error_set_from_boxed (oerror, box);
}
/*
* mono_class_alloc:
*
* Allocate memory for some data belonging to CLASS, either from its image's mempool,
* or from the heap.
*/
gpointer
mono_class_alloc (MonoClass *klass, int size)
{
MonoGenericClass *gklass = mono_class_try_get_generic_class (klass);
if (gklass)
return mono_image_set_alloc (gklass->owner, size);
else
return mono_image_alloc (klass->image, size);
}
gpointer
mono_class_alloc0 (MonoClass *klass, int size)
{
gpointer res;
res = mono_class_alloc (klass, size);
memset (res, 0, size);
return res;
}
#define mono_class_new0(klass,struct_type, n_structs) \
((struct_type *) mono_class_alloc0 ((klass), ((gsize) sizeof (struct_type)) * ((gsize) (n_structs))))
/**
* mono_class_setup_basic_field_info:
* @class: The class to initialize
*
* Initializes the following fields in MonoClass:
* * klass->fields (only field->parent and field->name)
* * klass->field.count
* * klass->first_field_idx
* LOCKING: Acquires the loader lock
*/
static void
mono_class_setup_basic_field_info (MonoClass *klass)
{
MonoGenericClass *gklass;
MonoClassField *field;
MonoClassField *fields;
MonoClass *gtd;
MonoImage *image;
int i, top;
if (klass->fields)
return;
gklass = mono_class_try_get_generic_class (klass);
gtd = gklass ? mono_class_get_generic_type_definition (klass) : NULL;
image = klass->image;
if (gklass && image_is_dynamic (gklass->container_class->image) && !gklass->container_class->wastypebuilder) {
/*
* This happens when a generic instance of an unfinished generic typebuilder
* is used as an element type for creating an array type. We can't initialize
* the fields of this class using the fields of gklass, since gklass is not
* finished yet, fields could be added to it later.
*/
return;
}
if (gtd) {
mono_class_setup_basic_field_info (gtd);
mono_loader_lock ();
mono_class_set_field_count (klass, mono_class_get_field_count (gtd));
mono_loader_unlock ();
}
top = mono_class_get_field_count (klass);
fields = (MonoClassField *)mono_class_alloc0 (klass, sizeof (MonoClassField) * top);
/*
* Fetch all the field information.
*/
int first_field_idx = mono_class_has_static_metadata (klass) ? mono_class_get_first_field_idx (klass) : 0;
for (i = 0; i < top; i++) {
field = &fields [i];
field->parent = klass;
if (gtd) {
field->name = mono_field_get_name (&gtd->fields [i]);
} else {
int idx = first_field_idx + i;
/* first_field_idx and idx points into the fieldptr table */
guint32 name_idx = mono_metadata_decode_table_row_col (image, MONO_TABLE_FIELD, idx, MONO_FIELD_NAME);
/* The name is needed for fieldrefs */
field->name = mono_metadata_string_heap (image, name_idx);
}
}
mono_memory_barrier ();
mono_loader_lock ();
if (!klass->fields)
klass->fields = fields;
mono_loader_unlock ();
}
/**
* mono_class_set_failure_causedby_class:
* @klass: the class that is failing
* @caused_by: the class that caused the failure
* @msg: Why @klass is failing.
*
* If @caused_by has a failure, sets a TypeLoadException failure on
* @klass with message "@msg, due to: {@caused_by message}".
*
* Returns: TRUE if a failiure was set, or FALSE if @caused_by doesn't have a failure.
*/
static gboolean
mono_class_set_type_load_failure_causedby_class (MonoClass *klass, const MonoClass *caused_by, const gchar* msg)
{
if (mono_class_has_failure (caused_by)) {
MonoError cause_error;
mono_error_init (&cause_error);
mono_error_set_for_class_failure (&cause_error, caused_by);
mono_class_set_type_load_failure (klass, "%s, due to: %s", msg, mono_error_get_message (&cause_error));
mono_error_cleanup (&cause_error);
return TRUE;
} else {
return FALSE;
}
}
/**
* mono_class_setup_fields:
* @klass: The class to initialize
*
* Initializes klass->fields, computes class layout and sizes.
* typebuilder_setup_fields () is the corresponding function for dynamic classes.
* Sets the following fields in @klass:
* - all the fields initialized by mono_class_init_sizes ()
* - element_class/cast_class (for enums)
* - field->type/offset for all fields
* - fields_inited
*
* LOCKING: Acquires the loader lock.
*/
void
mono_class_setup_fields (MonoClass *klass)
{
MonoError error;
MonoImage *m = klass->image;
int top;
guint32 layout = mono_class_get_flags (klass) & TYPE_ATTRIBUTE_LAYOUT_MASK;
int i;
guint32 real_size = 0;
guint32 packing_size = 0;
int instance_size;
gboolean explicit_size;
MonoClassField *field;
MonoGenericClass *gklass = mono_class_try_get_generic_class (klass);
MonoClass *gtd = gklass ? mono_class_get_generic_type_definition (klass) : NULL;
if (klass->fields_inited)
return;
if (gklass && image_is_dynamic (gklass->container_class->image) && !gklass->container_class->wastypebuilder) {
/*
* This happens when a generic instance of an unfinished generic typebuilder
* is used as an element type for creating an array type. We can't initialize
* the fields of this class using the fields of gklass, since gklass is not
* finished yet, fields could be added to it later.
*/
return;
}
mono_class_setup_basic_field_info (klass);
top = mono_class_get_field_count (klass);
if (gtd) {
mono_class_setup_fields (gtd);
if (mono_class_set_type_load_failure_causedby_class (klass, gtd, "Generic type definition failed"))
return;
}
instance_size = 0;
if (klass->parent) {
/* For generic instances, klass->parent might not have been initialized */
mono_class_init (klass->parent);
mono_class_setup_fields (klass->parent);
if (mono_class_set_type_load_failure_causedby_class (klass, klass->parent, "Could not set up parent class"))
return;
instance_size = klass->parent->instance_size;
} else {
instance_size = sizeof (MonoObject);
}
/* Get the real size */
explicit_size = mono_metadata_packing_from_typedef (klass->image, klass->type_token, &packing_size, &real_size);
if (explicit_size)
instance_size += real_size;
/*
* This function can recursively call itself.
* Prevent infinite recursion by using a list in TLS.
*/
GSList *init_list = (GSList *)mono_native_tls_get_value (setup_fields_tls_id);
if (g_slist_find (init_list, klass))
return;
init_list = g_slist_prepend (init_list, klass);
mono_native_tls_set_value (setup_fields_tls_id, init_list);
/*
* Fetch all the field information.
*/
int first_field_idx = mono_class_has_static_metadata (klass) ? mono_class_get_first_field_idx (klass) : 0;
for (i = 0; i < top; i++) {
int idx = first_field_idx + i;
field = &klass->fields [i];
if (!field->type) {
mono_field_resolve_type (field, &error);
if (!mono_error_ok (&error)) {
/*mono_field_resolve_type already failed class*/
mono_error_cleanup (&error);
break;
}
if (!field->type)
g_error ("could not resolve %s:%s\n", mono_type_get_full_name(klass), field->name);
g_assert (field->type);
}
if (mono_field_is_deleted (field))
continue;
if (layout == TYPE_ATTRIBUTE_EXPLICIT_LAYOUT) {
guint32 uoffset;
mono_metadata_field_info (m, idx, &uoffset, NULL, NULL);
int offset = uoffset;
if (offset == (guint32)-1 && !(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) {
mono_class_set_type_load_failure (klass, "Missing field layout info for %s", field->name);
break;
}
if (offset < -1) { /*-1 is used to encode special static fields */
mono_class_set_type_load_failure (klass, "Field '%s' has a negative offset %d", field->name, offset);
break;
}
if (mono_class_is_gtd (klass)) {
mono_class_set_type_load_failure (klass, "Generic class cannot have explicit layout.");
break;
}
}
if (mono_type_has_exceptions (field->type)) {
char *class_name = mono_type_get_full_name (klass);
char *type_name = mono_type_full_name (field->type);
mono_class_set_type_load_failure (klass, "");
g_warning ("Invalid type %s for instance field %s:%s", type_name, class_name, field->name);
g_free (class_name);
g_free (type_name);
break;
}
/* The def_value of fields is compute lazily during vtable creation */
}
if (!mono_class_has_failure (klass)) {
mono_loader_lock ();
mono_class_layout_fields (klass, instance_size, packing_size, FALSE);
mono_loader_unlock ();
}
init_list = g_slist_remove (init_list, klass);
mono_native_tls_set_value (setup_fields_tls_id, init_list);
}
static void
init_sizes_with_info (MonoClass *klass, MonoCachedClassInfo *cached_info)
{
if (cached_info) {
mono_loader_lock ();
klass->instance_size = cached_info->instance_size;
klass->sizes.class_size = cached_info->class_size;
klass->packing_size = cached_info->packing_size;
klass->min_align = cached_info->min_align;
klass->blittable = cached_info->blittable;
klass->has_references = cached_info->has_references;
klass->has_static_refs = cached_info->has_static_refs;
klass->no_special_static_fields = cached_info->no_special_static_fields;
mono_loader_unlock ();
}
else {
if (!klass->size_inited)
mono_class_setup_fields (klass);
}
}
/*
* mono_class_init_sizes:
*
* Initializes the size related fields of @klass without loading all field data if possible.
* Sets the following fields in @klass:
* - instance_size
* - sizes.class_size
* - packing_size
* - min_align
* - blittable
* - has_references
* - has_static_refs
* - size_inited
* Can fail the class.
*
* LOCKING: Acquires the loader lock.
*/
static void
mono_class_init_sizes (MonoClass *klass)
{
MonoCachedClassInfo cached_info;
gboolean has_cached_info;
if (klass->size_inited)
return;
has_cached_info = mono_class_get_cached_class_info (klass, &cached_info);
init_sizes_with_info (klass, has_cached_info ? &cached_info : NULL);
}
/*
* mono_type_get_basic_type_from_generic:
* @type: a type
*
* Returns a closed type corresponding to the possibly open type
* passed to it.
*/
MonoType*
mono_type_get_basic_type_from_generic (MonoType *type)
{
/* When we do generic sharing we let type variables stand for reference/primitive types. */
if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) &&
(!type->data.generic_param->gshared_constraint || type->data.generic_param->gshared_constraint->type == MONO_TYPE_OBJECT))
return &mono_defaults.object_class->byval_arg;
return type;
}
static gboolean
class_has_references (MonoClass *klass)
{
mono_class_init_sizes (klass);
/*
* has_references is not set if this is called recursively, but this is not a problem since this is only used
* during field layout, and instance fields are initialized before static fields, and instance fields can't
* embed themselves.
*/
return klass->has_references;
}
static gboolean
type_has_references (MonoClass *klass, MonoType *ftype)
{
if (MONO_TYPE_IS_REFERENCE (ftype) || IS_GC_REFERENCE (klass, ftype) || ((MONO_TYPE_ISSTRUCT (ftype) && class_has_references (mono_class_from_mono_type (ftype)))))
return TRUE;
if (!ftype->byref && (ftype->type == MONO_TYPE_VAR || ftype->type == MONO_TYPE_MVAR)) {
MonoGenericParam *gparam = ftype->data.generic_param;
if (gparam->gshared_constraint)
return class_has_references (mono_class_from_mono_type (gparam->gshared_constraint));
}
return FALSE;
}
/*
* mono_class_layout_fields:
* @class: a class
* @base_instance_size: base instance size
* @packing_size:
*
* This contains the common code for computing the layout of classes and sizes.
* This should only be called from mono_class_setup_fields () and
* typebuilder_setup_fields ().
*
* LOCKING: Acquires the loader lock
*/
void
mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_size, gboolean sre)
{
int i;
const int top = mono_class_get_field_count (klass);
guint32 layout = mono_class_get_flags (klass) & TYPE_ATTRIBUTE_LAYOUT_MASK;
guint32 pass, passes, real_size;
gboolean gc_aware_layout = FALSE;
gboolean has_static_fields = FALSE;
gboolean has_references = FALSE;
gboolean has_static_refs = FALSE;
MonoClassField *field;
gboolean blittable;
int instance_size = base_instance_size;
int class_size, min_align;
int *field_offsets;
gboolean *fields_has_references;
/*
* We want to avoid doing complicated work inside locks, so we compute all the required
* information and write it to @klass inside a lock.
*/
if (klass->fields_inited)
return;
if ((packing_size & 0xffffff00) != 0) {
mono_class_set_type_load_failure (klass, "Could not load struct '%s' with packing size %d >= 256", klass->name, packing_size);
return;
}
if (klass->parent) {
min_align = klass->parent->min_align;
/* we use | since it may have been set already */
has_references = klass->has_references | klass->parent->has_references;
} else {
min_align = 1;
}
/* We can't really enable 16 bytes alignment until the GC supports it.
The whole layout/instance size code must be reviewed because we do alignment calculation in terms of the
boxed instance, which leads to unexplainable holes at the beginning of an object embedding a simd type.
Bug #506144 is an example of this issue.
if (klass->simd_type)
min_align = 16;
*/
/*
* When we do generic sharing we need to have layout
* information for open generic classes (either with a generic
* context containing type variables or with a generic
* container), so we don't return in that case anymore.
*/
if (klass->enumtype) {
for (i = 0; i < top; i++) {
field = &klass->fields [i];
if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) {
klass->cast_class = klass->element_class = mono_class_from_mono_type (field->type);
break;
}
}
if (!mono_class_enum_basetype (klass)) {
mono_class_set_type_load_failure (klass, "The enumeration's base type is invalid.");
return;
}
}
/*
* Enable GC aware auto layout: in this mode, reference
* fields are grouped together inside objects, increasing collector
* performance.
* Requires that all classes whose layout is known to native code be annotated
* with [StructLayout (LayoutKind.Sequential)]
* Value types have gc_aware_layout disabled by default, as per
* what the default is for other runtimes.
*/
/* corlib is missing [StructLayout] directives in many places */
if (layout == TYPE_ATTRIBUTE_AUTO_LAYOUT) {
if (!klass->valuetype)
gc_aware_layout = TRUE;
}
/* Compute klass->blittable */
blittable = TRUE;
if (klass->parent)
blittable = klass->parent->blittable;
if (layout == TYPE_ATTRIBUTE_AUTO_LAYOUT && !(mono_is_corlib_image (klass->image) && !strcmp (klass->name_space, "System") && !strcmp (klass->name, "ValueType")) && top)
blittable = FALSE;
for (i = 0; i < top; i++) {
field = &klass->fields [i];
if (mono_field_is_deleted (field))
continue;
if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
continue;
if (blittable) {
if (field->type->byref || MONO_TYPE_IS_REFERENCE (field->type)) {
blittable = FALSE;
} else {
MonoClass *field_class = mono_class_from_mono_type (field->type);
if (field_class) {
mono_class_setup_fields (field_class);
if (mono_class_has_failure (field_class)) {
MonoError field_error;
mono_error_init (&field_error);
mono_error_set_for_class_failure (&field_error, field_class);
mono_class_set_type_load_failure (klass, "Could not set up field '%s' due to: %s", field->name, mono_error_get_message (&field_error));
mono_error_cleanup (&field_error);
break;
}
}
if (!field_class || !field_class->blittable)
blittable = FALSE;
}
}
if (klass->enumtype)
blittable = klass->element_class->blittable;
}
if (mono_class_has_failure (klass))
return;
if (klass == mono_defaults.string_class)
blittable = FALSE;
/* Compute klass->has_references */
/*
* Process non-static fields first, since static fields might recursively
* refer to the class itself.
*/
for (i = 0; i < top; i++) {
MonoType *ftype;
field = &klass->fields [i];
if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) {
ftype = mono_type_get_underlying_type (field->type);
ftype = mono_type_get_basic_type_from_generic (ftype);
if (type_has_references (klass, ftype))
has_references = TRUE;
}
}
/*
* Compute field layout and total size (not considering static fields)
*/
field_offsets = g_new0 (int, top);
fields_has_references = g_new0 (gboolean, top);
int first_field_idx = mono_class_has_static_metadata (klass) ? mono_class_get_first_field_idx (klass) : 0;
switch (layout) {
case TYPE_ATTRIBUTE_AUTO_LAYOUT:
case TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT:
if (gc_aware_layout)
passes = 2;
else
passes = 1;
if (layout != TYPE_ATTRIBUTE_AUTO_LAYOUT)
passes = 1;
if (klass->parent) {
mono_class_setup_fields (klass->parent);
if (mono_class_set_type_load_failure_causedby_class (klass, klass->parent, "Cannot initialize parent class"))
return;
real_size = klass->parent->instance_size;
} else {
real_size = sizeof (MonoObject);
}
for (pass = 0; pass < passes; ++pass) {
for (i = 0; i < top; i++){
gint32 align;
guint32 size;
MonoType *ftype;
field = &klass->fields [i];
if (mono_field_is_deleted (field))
continue;
if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
continue;
ftype = mono_type_get_underlying_type (field->type);
ftype = mono_type_get_basic_type_from_generic (ftype);
if (gc_aware_layout) {
fields_has_references [i] = type_has_references (klass, ftype);
if (fields_has_references [i]) {
if (pass == 1)
continue;
} else {
if (pass == 0)
continue;
}
}
if ((top == 1) && (instance_size == sizeof (MonoObject)) &&
(strcmp (mono_field_get_name (field), "$PRIVATE$") == 0)) {
/* This field is a hack inserted by MCS to empty structures */
continue;
}
size = mono_type_size (field->type, &align);
/* FIXME (LAMESPEC): should we also change the min alignment according to pack? */
align = packing_size ? MIN (packing_size, align): align;
/* if the field has managed references, we need to force-align it
* see bug #77788
*/
if (type_has_references (klass, ftype))
align = MAX (align, sizeof (gpointer));
min_align = MAX (align, min_align);
field_offsets [i] = real_size;
if (align) {
field_offsets [i] += align - 1;
field_offsets [i] &= ~(align - 1);
}
/*TypeBuilders produce all sort of weird things*/
g_assert (image_is_dynamic (klass->image) || field_offsets [i] > 0);
real_size = field_offsets [i] + size;
}
/* Make SIMD types as big as a SIMD register since they can be stored into using simd stores */
if (klass->simd_type)
real_size = MAX (real_size, sizeof (MonoObject) + 16);
instance_size = MAX (real_size, instance_size);
if (instance_size & (min_align - 1)) {
instance_size += min_align - 1;
instance_size &= ~(min_align - 1);
}
}
break;
case TYPE_ATTRIBUTE_EXPLICIT_LAYOUT: {
guint8 *ref_bitmap;
real_size = 0;
for (i = 0; i < top; i++) {
gint32 align;
guint32 size;
MonoType *ftype;
field = &klass->fields [i];
/*
* There must be info about all the fields in a type if it
* uses explicit layout.
*/
if (mono_field_is_deleted (field))
continue;
if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
continue;
size = mono_type_size (field->type, &align);
align = packing_size ? MIN (packing_size, align): align;
min_align = MAX (align, min_align);
if (sre) {
/* Already set by typebuilder_setup_fields () */
field_offsets [i] = field->offset + sizeof (MonoObject);
} else {
int idx = first_field_idx + i;
guint32 offset;
mono_metadata_field_info (klass->image, idx, &offset, NULL, NULL);
field_offsets [i] = offset + sizeof (MonoObject);
}
ftype = mono_type_get_underlying_type (field->type);
ftype = mono_type_get_basic_type_from_generic (ftype);
if (type_has_references (klass, ftype)) {
if (field_offsets [i] % sizeof (gpointer)) {
mono_class_set_type_load_failure (klass, "Reference typed field '%s' has explicit offset that is not pointer-size aligned.", field->name);
}
}
/*
* Calc max size.
*/
real_size = MAX (real_size, size + field_offsets [i]);
}
if (klass->has_references) {
ref_bitmap = g_new0 (guint8, real_size / sizeof (gpointer));
/* Check for overlapping reference and non-reference fields */
for (i = 0; i < top; i++) {
MonoType *ftype;
field = &klass->fields [i];
if (mono_field_is_deleted (field))
continue;
if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
continue;
ftype = mono_type_get_underlying_type (field->type);
if (MONO_TYPE_IS_REFERENCE (ftype))
ref_bitmap [field_offsets [i] / sizeof (gpointer)] = 1;
}
for (i = 0; i < top; i++) {
field = &klass->fields [i];
if (mono_field_is_deleted (field))
continue;
if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
continue;
// FIXME: Too much code does this
#if 0
if (!MONO_TYPE_IS_REFERENCE (field->type) && ref_bitmap [field_offsets [i] / sizeof (gpointer)]) {
mono_class_set_type_load_failure (klass, "Could not load type '%s' because it contains an object field at offset %d that is incorrectly aligned or overlapped by a non-object field.", klass->name, field_offsets [i]);
}
#endif
}
g_free (ref_bitmap);
}
instance_size = MAX (real_size, instance_size);
if (instance_size & (min_align - 1)) {
instance_size += min_align - 1;
instance_size &= ~(min_align - 1);
}
break;
}
}
if (layout != TYPE_ATTRIBUTE_EXPLICIT_LAYOUT) {
/*
* This leads to all kinds of problems with nested structs, so only
* enable it when a MONO_DEBUG property is set.
*
* For small structs, set min_align to at least the struct size to improve
* performance, and since the JIT memset/memcpy code assumes this and generates
* unaligned accesses otherwise. See #78990 for a testcase.
*/
if (mono_align_small_structs && top) {
if (instance_size <= sizeof (MonoObject) + sizeof (gpointer))
min_align = MAX (min_align, instance_size - sizeof (MonoObject));
}
}
if (klass->byval_arg.type == MONO_TYPE_VAR || klass->byval_arg.type == MONO_TYPE_MVAR)
instance_size = sizeof (MonoObject) + mono_type_stack_size_internal (&klass->byval_arg, NULL, TRUE);
else if (klass->byval_arg.type == MONO_TYPE_PTR)
instance_size = sizeof (MonoObject) + sizeof (gpointer);
/* Publish the data */
mono_loader_lock ();
if (klass->instance_size && !klass->image->dynamic) {
/* Might be already set using cached info */
if (klass->instance_size != instance_size) {
/* Emit info to help debugging */
g_print ("%s\n", mono_class_full_name (klass));
g_print ("%d %d %d %d\n", klass->instance_size, instance_size, klass->blittable, blittable);
g_print ("%d %d %d %d\n", klass->has_references, has_references, klass->packing_size, packing_size);
g_print ("%d %d\n", klass->min_align, min_align);
for (i = 0; i < top; ++i) {
field = &klass->fields [i];
if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
printf (" %s %d %d %d\n", klass->fields [i].name, klass->fields [i].offset, field_offsets [i], fields_has_references [i]);
}
}
g_assert (klass->instance_size == instance_size);
} else {
klass->instance_size = instance_size;
}
klass->blittable = blittable;
klass->has_references = has_references;
klass->packing_size = packing_size;
klass->min_align = min_align;
for (i = 0; i < top; ++i) {
field = &klass->fields [i];
if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
klass->fields [i].offset = field_offsets [i];
}
mono_memory_barrier ();
klass->size_inited = 1;
mono_loader_unlock ();
/*
* Compute static field layout and size
* Static fields can reference the class itself, so this has to be
* done after instance_size etc. are initialized.
*/
class_size = 0;
for (i = 0; i < top; i++) {
gint32 align;
guint32 size;
field = &klass->fields [i];
if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC) || field->type->attrs & FIELD_ATTRIBUTE_LITERAL)
continue;
if (mono_field_is_deleted (field))
continue;
if (mono_type_has_exceptions (field->type)) {
mono_class_set_type_load_failure (klass, "Field '%s' has an invalid type.", field->name);
break;
}
has_static_fields = TRUE;
size = mono_type_size (field->type, &align);
field_offsets [i] = class_size;
/*align is always non-zero here*/
field_offsets [i] += align - 1;
field_offsets [i] &= ~(align - 1);
class_size = field_offsets [i] + size;
}
if (has_static_fields && class_size == 0)
/* Simplify code which depends on class_size != 0 if the class has static fields */
class_size = 8;
/* Compute klass->has_static_refs */
has_static_refs = FALSE;
for (i = 0; i < top; i++) {
MonoType *ftype;
field = &klass->fields [i];
if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) {
ftype = mono_type_get_underlying_type (field->type);
ftype = mono_type_get_basic_type_from_generic (ftype);
if (type_has_references (klass, ftype))
has_static_refs = TRUE;
}
}
/*valuetypes can't be neither bigger than 1Mb or empty. */
if (klass->valuetype && (klass->instance_size <= 0 || klass->instance_size > (0x100000 + sizeof (MonoObject))))
mono_class_set_type_load_failure (klass, "Value type instance size (%d) cannot be zero, negative, or bigger than 1Mb", klass->instance_size);
/* Publish the data */
mono_loader_lock ();
if (!klass->rank)
klass->sizes.class_size = class_size;
klass->has_static_refs = has_static_refs;
for (i = 0; i < top; ++i) {
field = &klass->fields [i];
if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
field->offset = field_offsets [i];
}
mono_memory_barrier ();
klass->fields_inited = 1;
mono_loader_unlock ();
g_free (field_offsets);
g_free (fields_has_references);
}
static MonoMethod*
create_array_method (MonoClass *klass, const char *name, MonoMethodSignature *sig)
{
MonoMethod *method;
method = (MonoMethod *) mono_image_alloc0 (klass->image, sizeof (MonoMethodPInvoke));
method->klass = klass;
method->flags = METHOD_ATTRIBUTE_PUBLIC;
method->iflags = METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL;
method->signature = sig;
method->name = name;
method->slot = -1;
/* .ctor */
if (name [0] == '.') {
method->flags |= METHOD_ATTRIBUTE_RT_SPECIAL_NAME | METHOD_ATTRIBUTE_SPECIAL_NAME;
} else {
method->iflags |= METHOD_IMPL_ATTRIBUTE_RUNTIME;
}
return method;
}
/*
* mono_class_setup_methods:
* @class: a class
*
* Initializes the 'methods' array in CLASS.
* Calling this method should be avoided if possible since it allocates a lot
* of long-living MonoMethod structures.
* Methods belonging to an interface are assigned a sequential slot starting
* from 0.
*
* On failure this function sets klass->has_failure and stores a MonoErrorBoxed with details
*/
void
mono_class_setup_methods (MonoClass *klass)
{
int i, count;
MonoMethod **methods;
if (klass->methods)
return;
if (mono_class_is_ginst (klass)) {
MonoError error;
MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
mono_class_init (gklass);
if (!mono_class_has_failure (gklass))
mono_class_setup_methods (gklass);
if (mono_class_set_type_load_failure_causedby_class (klass, gklass, "Generic type definition failed to load"))
return;
/* The + 1 makes this always non-NULL to pass the check in mono_class_setup_methods () */
count = mono_class_get_method_count (gklass);
methods = (MonoMethod **)mono_class_alloc0 (klass, sizeof (MonoMethod*) * (count + 1));
for (i = 0; i < count; i++) {
methods [i] = mono_class_inflate_generic_method_full_checked (
gklass->methods [i], klass, mono_class_get_context (klass), &error);
if (!mono_error_ok (&error)) {
char *method = mono_method_full_name (gklass->methods [i], TRUE);
mono_class_set_type_load_failure (klass, "Could not inflate method %s due to %s", method, mono_error_get_message (&error));
g_free (method);
mono_error_cleanup (&error);
return;
}
}
} else if (klass->rank) {
MonoError error;
MonoMethod *amethod;
MonoMethodSignature *sig;
int count_generic = 0, first_generic = 0;
int method_num = 0;
gboolean jagged_ctor = FALSE;
count = 3 + (klass->rank > 1? 2: 1);
mono_class_setup_interfaces (klass, &error);
g_assert (mono_error_ok (&error)); /*FIXME can this fail for array types?*/
if (klass->rank == 1 && klass->element_class->rank) {
jagged_ctor = TRUE;
count ++;
}
if (klass->interface_count) {
count_generic = generic_array_methods (klass);
first_generic = count;
count += klass->interface_count * count_generic;
}
methods = (MonoMethod **)mono_class_alloc0 (klass, sizeof (MonoMethod*) * count);
sig = mono_metadata_signature_alloc (klass->image, klass->rank);
sig->ret = &mono_defaults.void_class->byval_arg;
sig->pinvoke = TRUE;
sig->hasthis = TRUE;
for (i = 0; i < klass->rank; ++i)
sig->params [i] = &mono_defaults.int32_class->byval_arg;
amethod = create_array_method (klass, ".ctor", sig);
methods [method_num++] = amethod;
if (klass->rank > 1) {
sig = mono_metadata_signature_alloc (klass->image, klass->rank * 2);
sig->ret = &mono_defaults.void_class->byval_arg;
sig->pinvoke = TRUE;
sig->hasthis = TRUE;
for (i = 0; i < klass->rank * 2; ++i)
sig->params [i] = &mono_defaults.int32_class->byval_arg;
amethod = create_array_method (klass, ".ctor", sig);
methods [method_num++] = amethod;
}
if (jagged_ctor) {
/* Jagged arrays have an extra ctor in .net which creates an array of arrays */
sig = mono_metadata_signature_alloc (klass->image, klass->rank + 1);
sig->ret = &mono_defaults.void_class->byval_arg;
sig->pinvoke = TRUE;
sig->hasthis = TRUE;
for (i = 0; i < klass->rank + 1; ++i)
sig->params [i] = &mono_defaults.int32_class->byval_arg;
amethod = create_array_method (klass, ".ctor", sig);
methods [method_num++] = amethod;
}
/* element Get (idx11, [idx2, ...]) */
sig = mono_metadata_signature_alloc (klass->image, klass->rank);
sig->ret = &klass->element_class->byval_arg;
sig->pinvoke = TRUE;
sig->hasthis = TRUE;
for (i = 0; i < klass->rank; ++i)
sig->params [i] = &mono_defaults.int32_class->byval_arg;
amethod = create_array_method (klass, "Get", sig);
methods [method_num++] = amethod;
/* element& Address (idx11, [idx2, ...]) */
sig = mono_metadata_signature_alloc (klass->image, klass->rank);
sig->ret = &klass->element_class->this_arg;
sig->pinvoke = TRUE;
sig->hasthis = TRUE;
for (i = 0; i < klass->rank; ++i)
sig->params [i] = &mono_defaults.int32_class->byval_arg;
amethod = create_array_method (klass, "Address", sig);
methods [method_num++] = amethod;
/* void Set (idx11, [idx2, ...], element) */
sig = mono_metadata_signature_alloc (klass->image, klass->rank + 1);
sig->ret = &mono_defaults.void_class->byval_arg;
sig->pinvoke = TRUE;
sig->hasthis = TRUE;
for (i = 0; i < klass->rank; ++i)
sig->params [i] = &mono_defaults.int32_class->byval_arg;
sig->params [i] = &klass->element_class->byval_arg;
amethod = create_array_method (klass, "Set", sig);
methods [method_num++] = amethod;
for (i = 0; i < klass->interface_count; i++)
setup_generic_array_ifaces (klass, klass->interfaces [i], methods, first_generic + i * count_generic);
} else if (mono_class_has_static_metadata (klass)) {
MonoError error;
int first_idx = mono_class_get_first_method_idx (klass);
count = mono_class_get_method_count (klass);
methods = (MonoMethod **)mono_class_alloc (klass, sizeof (MonoMethod*) * count);
for (i = 0; i < count; ++i) {
int idx = mono_metadata_translate_token_index (klass->image, MONO_TABLE_METHOD, first_idx + i + 1);
methods [i] = mono_get_method_checked (klass->image, MONO_TOKEN_METHOD_DEF | idx, klass, NULL, &error);
if (!methods [i]) {
mono_class_set_type_load_failure (klass, "Could not load method %d due to %s", i, mono_error_get_message (&error));
mono_error_cleanup (&error);
}
}
} else {
methods = (MonoMethod **)mono_class_alloc (klass, sizeof (MonoMethod*) * 1);
count = 0;
}
if (MONO_CLASS_IS_INTERFACE (klass)) {
int slot = 0;
/*Only assign slots to virtual methods as interfaces are allowed to have static methods.*/
for (i = 0; i < count; ++i) {
if (methods [i]->flags & METHOD_ATTRIBUTE_VIRTUAL)
methods [i]->slot = slot++;
}
}
mono_image_lock (klass->image);
if (!klass->methods) {
mono_class_set_method_count (klass, count);
/* Needed because of the double-checking locking pattern */
mono_memory_barrier ();
klass->methods = methods;
}
mono_image_unlock (klass->image);
}
/*
* mono_class_get_method_by_index:
*
* Returns klass->methods [index], initializing klass->methods if neccesary.
*
* LOCKING: Acquires the loader lock.
*/
MonoMethod*
mono_class_get_method_by_index (MonoClass *klass, int index)
{
MonoError error;
MonoGenericClass *gklass = mono_class_try_get_generic_class (klass);
/* Avoid calling setup_methods () if possible */
if (gklass && !klass->methods) {
MonoMethod *m;
m = mono_class_inflate_generic_method_full_checked (
gklass->container_class->methods [index], klass, mono_class_get_context (klass), &error);
g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
/*
* If setup_methods () is called later for this class, no duplicates are created,
* since inflate_generic_method guarantees that only one instance of a method
* is created for each context.
*/
/*
mono_class_setup_methods (klass);
g_assert (m == klass->methods [index]);
*/
return m;
} else {
mono_class_setup_methods (klass);
if (mono_class_has_failure (klass)) /*FIXME do proper error handling*/
return NULL;
g_assert (index >= 0 && index < mono_class_get_method_count (klass));
return klass->methods [index];
}
}
/*
* mono_class_get_inflated_method:
*
* Given an inflated class CLASS and a method METHOD which should be a method of
* CLASS's generic definition, return the inflated method corresponding to METHOD.
*/
MonoMethod*
mono_class_get_inflated_method (MonoClass *klass, MonoMethod *method)
{
MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
int i, mcount;
g_assert (method->klass == gklass);
mono_class_setup_methods (gklass);
g_assert (!mono_class_has_failure (gklass)); /*FIXME do proper error handling*/
mcount = mono_class_get_method_count (gklass);
for (i = 0; i < mcount; ++i) {
if (gklass->methods [i] == method) {
if (klass->methods) {
return klass->methods [i];
} else {
MonoError error;
MonoMethod *result = mono_class_inflate_generic_method_full_checked (gklass->methods [i], klass, mono_class_get_context (klass), &error);
g_assert (mono_error_ok (&error)); /* FIXME don't swallow this error */
return result;
}
}
}
return NULL;
}
/*
* mono_class_get_vtable_entry:
*
* Returns klass->vtable [offset], computing it if neccesary. Returns NULL on failure.
* LOCKING: Acquires the loader lock.
*/
MonoMethod*
mono_class_get_vtable_entry (MonoClass *klass, int offset)
{
MonoMethod *m;
if (klass->rank == 1) {
/*
* szarrays do not overwrite any methods of Array, so we can avoid
* initializing their vtables in some cases.
*/
mono_class_setup_vtable (klass->parent);
if (offset < klass->parent->vtable_size)
return klass->parent->vtable [offset];
}
if (mono_class_is_ginst (klass)) {
MonoError error;
MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
mono_class_setup_vtable (gklass);
m = gklass->vtable [offset];
m = mono_class_inflate_generic_method_full_checked (m, klass, mono_class_get_context (klass), &error);
g_assert (mono_error_ok (&error)); /* FIXME don't swallow this error */
} else {
mono_class_setup_vtable (klass);
if (mono_class_has_failure (klass))
return NULL;
m = klass->vtable [offset];
}
return m;
}
/*
* mono_class_get_vtable_size:
*
* Return the vtable size for KLASS.
*/
int
mono_class_get_vtable_size (MonoClass *klass)
{
mono_class_setup_vtable (klass);
return klass->vtable_size;
}
/*
* mono_class_setup_properties:
*
* Initialize klass->ext.property and klass->ext.properties.
*
* This method can fail the class.
*/
static void
mono_class_setup_properties (MonoClass *klass)
{
guint startm, endm, i, j;
guint32 cols [MONO_PROPERTY_SIZE];
MonoTableInfo *msemt = &klass->image->tables [MONO_TABLE_METHODSEMANTICS];
MonoProperty *properties;
guint32 last;
int first, count;
MonoClassPropertyInfo *info;
info = mono_class_get_property_info (klass);
if (info)
return;
if (mono_class_is_ginst (klass)) {
MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
mono_class_init (gklass);
mono_class_setup_properties (gklass);
if (mono_class_set_type_load_failure_causedby_class (klass, gklass, "Generic type definition failed to load"))
return;
MonoClassPropertyInfo *ginfo = mono_class_get_property_info (gklass);
properties = mono_class_new0 (klass, MonoProperty, ginfo->count + 1);
for (i = 0; i < ginfo->count; i++) {
MonoError error;
MonoProperty *prop = &properties [i];
*prop = ginfo->properties [i];
if (prop->get)
prop->get = mono_class_inflate_generic_method_full_checked (
prop->get, klass, mono_class_get_context (klass), &error);
if (prop->set)
prop->set = mono_class_inflate_generic_method_full_checked (
prop->set, klass, mono_class_get_context (klass), &error);
g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/
prop->parent = klass;
}
first = ginfo->first;
count = ginfo->count;
} else {
first = mono_metadata_properties_from_typedef (klass->image, mono_metadata_token_index (klass->type_token) - 1, &last);
count = last - first;
if (count) {
mono_class_setup_methods (klass);
if (mono_class_has_failure (klass))
return;
}
properties = (MonoProperty *)mono_class_alloc0 (klass, sizeof (MonoProperty) * count);
for (i = first; i < last; ++i) {
mono_metadata_decode_table_row (klass->image, MONO_TABLE_PROPERTY, i, cols, MONO_PROPERTY_SIZE);
properties [i - first].parent = klass;
properties [i - first].attrs = cols [MONO_PROPERTY_FLAGS];
properties [i - first].name = mono_metadata_string_heap (klass->image, cols [MONO_PROPERTY_NAME]);
startm = mono_metadata_methods_from_property (klass->image, i, &endm);
int first_idx = mono_class_get_first_method_idx (klass);
for (j = startm; j < endm; ++j) {
MonoMethod *method;
mono_metadata_decode_row (msemt, j, cols, MONO_METHOD_SEMA_SIZE);
if (klass->image->uncompressed_metadata) {
MonoError error;
/* It seems like the MONO_METHOD_SEMA_METHOD column needs no remapping */
method = mono_get_method_checked (klass->image, MONO_TOKEN_METHOD_DEF | cols [MONO_METHOD_SEMA_METHOD], klass, NULL, &error);
mono_error_cleanup (&error); /* FIXME don't swallow this error */
} else {
method = klass->methods [cols [MONO_METHOD_SEMA_METHOD] - 1 - first_idx];
}
switch (cols [MONO_METHOD_SEMA_SEMANTICS]) {
case METHOD_SEMANTIC_SETTER:
properties [i - first].set = method;
break;
case METHOD_SEMANTIC_GETTER:
properties [i - first].get = method;
break;
default:
break;
}
}
}
}
info = mono_class_alloc0 (klass, sizeof (MonoClassPropertyInfo));
info->first = first;
info->count = count;
info->properties = properties;
mono_memory_barrier ();
/* This might leak 'info' which was allocated from the image mempool */
mono_class_set_property_info (klass, info);
}
static MonoMethod**
inflate_method_listz (MonoMethod **methods, MonoClass *klass, MonoGenericContext *context)
{
MonoMethod **om, **retval;
int count;
for (om = methods, count = 0; *om; ++om, ++count)
;
retval = g_new0 (MonoMethod*, count + 1);
count = 0;
for (om = methods, count = 0; *om; ++om, ++count) {
MonoError error;
retval [count] = mono_class_inflate_generic_method_full_checked (*om, klass, context, &error);
g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/
}
return retval;
}
/*This method can fail the class.*/
static void
mono_class_setup_events (MonoClass *klass)
{
int first, count;
guint startm, endm, i, j;
guint32 cols [MONO_EVENT_SIZE];
MonoTableInfo *msemt = &klass->image->tables [MONO_TABLE_METHODSEMANTICS];
guint32 last;
MonoEvent *events;
MonoClassEventInfo *info = mono_class_get_event_info (klass);
if (info)
return;
if (mono_class_is_ginst (klass)) {
MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
MonoGenericContext *context = NULL;
mono_class_setup_events (gklass);
if (mono_class_set_type_load_failure_causedby_class (klass, gklass, "Generic type definition failed to load"))
return;
MonoClassEventInfo *ginfo = mono_class_get_event_info (gklass);
first = ginfo->first;
count = ginfo->count;
events = mono_class_new0 (klass, MonoEvent, count);
if (count)
context = mono_class_get_context (klass);
for (i = 0; i < count; i++) {
MonoError error;
MonoEvent *event = &events [i];
MonoEvent *gevent = &ginfo->events [i];
mono_error_init (&error); //since we do conditional calls, we must ensure the default value is ok
event->parent = klass;
event->name = gevent->name;
event->add = gevent->add ? mono_class_inflate_generic_method_full_checked (gevent->add, klass, context, &error) : NULL;
g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/
event->remove = gevent->remove ? mono_class_inflate_generic_method_full_checked (gevent->remove, klass, context, &error) : NULL;
g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/
event->raise = gevent->raise ? mono_class_inflate_generic_method_full_checked (gevent->raise, klass, context, &error) : NULL;
g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/
#ifndef MONO_SMALL_CONFIG
event->other = gevent->other ? inflate_method_listz (gevent->other, klass, context) : NULL;
#endif
event->attrs = gevent->attrs;
}
} else {
first = mono_metadata_events_from_typedef (klass->image, mono_metadata_token_index (klass->type_token) - 1, &last);
count = last - first;
if (count) {
mono_class_setup_methods (klass);
if (mono_class_has_failure (klass)) {
return;
}
}
events = (MonoEvent *)mono_class_alloc0 (klass, sizeof (MonoEvent) * count);
for (i = first; i < last; ++i) {
MonoEvent *event = &events [i - first];
mono_metadata_decode_table_row (klass->image, MONO_TABLE_EVENT, i, cols, MONO_EVENT_SIZE);
event->parent = klass;
event->attrs = cols [MONO_EVENT_FLAGS];
event->name = mono_metadata_string_heap (klass->image, cols [MONO_EVENT_NAME]);
startm = mono_metadata_methods_from_event (klass->image, i, &endm);
int first_idx = mono_class_get_first_method_idx (klass);
for (j = startm; j < endm; ++j) {
MonoMethod *method;
mono_metadata_decode_row (msemt, j, cols, MONO_METHOD_SEMA_SIZE);
if (klass->image->uncompressed_metadata) {
MonoError error;
/* It seems like the MONO_METHOD_SEMA_METHOD column needs no remapping */
method = mono_get_method_checked (klass->image, MONO_TOKEN_METHOD_DEF | cols [MONO_METHOD_SEMA_METHOD], klass, NULL, &error);
mono_error_cleanup (&error); /* FIXME don't swallow this error */
} else {
method = klass->methods [cols [MONO_METHOD_SEMA_METHOD] - 1 - first_idx];
}
switch (cols [MONO_METHOD_SEMA_SEMANTICS]) {
case METHOD_SEMANTIC_ADD_ON:
event->add = method;
break;
case METHOD_SEMANTIC_REMOVE_ON:
event->remove = method;
break;
case METHOD_SEMANTIC_FIRE:
event->raise = method;
break;
case METHOD_SEMANTIC_OTHER: {
#ifndef MONO_SMALL_CONFIG
int n = 0;
if (event->other == NULL) {
event->other = g_new0 (MonoMethod*, 2);
} else {
while (event->other [n])
n++;
event->other = (MonoMethod **)g_realloc (event->other, (n + 2) * sizeof (MonoMethod*));
}
event->other [n] = method;
/* NULL terminated */
event->other [n + 1] = NULL;
#endif
break;
}
default:
break;
}
}
}
}
info = mono_class_alloc0 (klass, sizeof (MonoClassEventInfo));
info->events = events;
info->first = first;
info->count = count;
mono_memory_barrier ();
mono_class_set_event_info (klass, info);
}
/*
* Global pool of interface IDs, represented as a bitset.
* LOCKING: Protected by the classes lock.
*/
static MonoBitSet *global_interface_bitset = NULL;
/*
* mono_unload_interface_ids:
* @bitset: bit set of interface IDs
*
* When an image is unloaded, the interface IDs associated with
* the image are put back in the global pool of IDs so the numbers
* can be reused.
*/
void
mono_unload_interface_ids (MonoBitSet *bitset)
{
classes_lock ();
mono_bitset_sub (global_interface_bitset, bitset);
classes_unlock ();
}
void
mono_unload_interface_id (MonoClass *klass)
{
if (global_interface_bitset && klass->interface_id) {
classes_lock ();
mono_bitset_clear (global_interface_bitset, klass->interface_id);
classes_unlock ();
}
}
/**
* mono_get_unique_iid:
* @class: interface
*
* Assign a unique integer ID to the interface represented by @class.
* The ID will positive and as small as possible.
* LOCKING: Acquires the classes lock.
* Returns: The new ID.
*/
static guint32
mono_get_unique_iid (MonoClass *klass)
{
int iid;
g_assert (MONO_CLASS_IS_INTERFACE (klass));
classes_lock ();
if (!global_interface_bitset) {
global_interface_bitset = mono_bitset_new (128, 0);
}
iid = mono_bitset_find_first_unset (global_interface_bitset, -1);
if (iid < 0) {
int old_size = mono_bitset_size (global_interface_bitset);
MonoBitSet *new_set = mono_bitset_clone (global_interface_bitset, old_size * 2);
mono_bitset_free (global_interface_bitset);
global_interface_bitset = new_set;
iid = old_size;
}
mono_bitset_set (global_interface_bitset, iid);
/* set the bit also in the per-image set */
if (!mono_class_is_ginst (klass)) {
if (klass->image->interface_bitset) {
if (iid >= mono_bitset_size (klass->image->interface_bitset)) {
MonoBitSet *new_set = mono_bitset_clone (klass->image->interface_bitset, iid + 1);
mono_bitset_free (klass->image->interface_bitset);
klass->image->interface_bitset = new_set;
}
} else {
klass->image->interface_bitset = mono_bitset_new (iid + 1, 0);
}
mono_bitset_set (klass->image->interface_bitset, iid);
}
classes_unlock ();
#ifndef MONO_SMALL_CONFIG
if (mono_print_vtable) {
int generic_id;
char *type_name = mono_type_full_name (&klass->byval_arg);
MonoGenericClass *gklass = mono_class_try_get_generic_class (klass);
if (gklass && !gklass->context.class_inst->is_open) {
generic_id = gklass->context.class_inst->id;
g_assert (generic_id != 0);
} else {
generic_id = 0;
}
printf ("Interface: assigned id %d to %s|%s|%d\n", iid, klass->image->name, type_name, generic_id);
g_free (type_name);
}
#endif
/* I've confirmed iids safe past 16 bits, however bitset code uses a signed int while testing.
* Once this changes, it should be safe for us to allow 2^32-1 interfaces, until then 2^31-2 is the max. */
g_assert (iid < INT_MAX);
return iid;
}
static void
collect_implemented_interfaces_aux (MonoClass *klass, GPtrArray **res, GHashTable **ifaces, MonoError *error)
{
int i;
MonoClass *ic;
mono_class_setup_interfaces (klass, error);
return_if_nok (error);
for (i = 0; i < klass->interface_count; i++) {
ic = klass->interfaces [i];
if (*res == NULL)
*res = g_ptr_array_new ();
if (*ifaces == NULL)
*ifaces = g_hash_table_new (NULL, NULL);
if (g_hash_table_lookup (*ifaces, ic))
continue;
g_ptr_array_add (*res, ic);
g_hash_table_insert (*ifaces, ic, ic);
mono_class_init (ic);
if (mono_class_has_failure (ic)) {
mono_error_set_type_load_class (error, ic, "Error Loading class");
return;
}
collect_implemented_interfaces_aux (ic, res, ifaces, error);
return_if_nok (error);
}
}
GPtrArray*
mono_class_get_implemented_interfaces (MonoClass *klass, MonoError *error)
{
GPtrArray *res = NULL;
GHashTable *ifaces = NULL;
collect_implemented_interfaces_aux (klass, &res, &ifaces, error);
if (ifaces)
g_hash_table_destroy (ifaces);
if (!mono_error_ok (error)) {
if (res)
g_ptr_array_free (res, TRUE);
return NULL;
}
return res;
}
static int
compare_interface_ids (const void *p_key, const void *p_element)
{
const MonoClass *key = (const MonoClass *)p_key;
const MonoClass *element = *(const MonoClass **)p_element;
return (key->interface_id - element->interface_id);
}
/*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 = (MonoClass **)mono_binary_search (
itf,
klass->interfaces_packed,
klass->interface_offsets_count,
sizeof (MonoClass *),
compare_interface_ids);
if (result) {
return klass->interface_offsets_packed [result - (klass->interfaces_packed)];
} else {
return -1;
}
}
/**
* mono_class_interface_offset_with_variance:
*
* Return the interface offset of @itf in @klass. Sets @non_exact_match to TRUE if the match required variance check
* If @itf is an interface with generic variant arguments, try to find the compatible one.
*
* Note that this function is responsible for resolving ambiguities. Right now we use whatever ordering interfaces_packed gives us.
*
* FIXME figure out MS disambiguation rules and fix this function.
*/
int
mono_class_interface_offset_with_variance (MonoClass *klass, MonoClass *itf, gboolean *non_exact_match)
{
int i = mono_class_interface_offset (klass, itf);
*non_exact_match = FALSE;
if (i >= 0)
return i;
if (itf->is_array_special_interface && klass->rank < 2) {
MonoClass *gtd = mono_class_get_generic_type_definition (itf);
for (i = 0; i < klass->interface_offsets_count; i++) {
// printf ("\t%s\n", mono_type_get_full_name (klass->interfaces_packed [i]));
if (mono_class_get_generic_type_definition (klass->interfaces_packed [i]) == gtd) {
*non_exact_match = TRUE;
return klass->interface_offsets_packed [i];
}
}
}
if (!mono_class_has_variant_generic_params (itf))
return -1;
for (i = 0; i < klass->interface_offsets_count; i++) {
if (mono_class_is_variant_compatible (itf, klass->interfaces_packed [i], FALSE)) {
*non_exact_match = TRUE;
return klass->interface_offsets_packed [i];
}
}
return -1;
}
static void
print_implemented_interfaces (MonoClass *klass)
{
char *name;
MonoError error;
GPtrArray *ifaces = NULL;
int i;
int ancestor_level = 0;
name = mono_type_get_full_name (klass);
printf ("Packed interface table for class %s has size %d\n", name, klass->interface_offsets_count);
g_free (name);
for (i = 0; i < klass->interface_offsets_count; i++)
printf (" [%03d][UUID %03d][SLOT %03d][SIZE %03d] interface %s.%s\n", i,
klass->interfaces_packed [i]->interface_id,
klass->interface_offsets_packed [i],
mono_class_get_method_count (klass->interfaces_packed [i]),
klass->interfaces_packed [i]->name_space,
klass->interfaces_packed [i]->name );
printf ("Interface flags: ");
for (i = 0; i <= klass->max_interface_id; i++)
if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, i))
printf ("(%d,T)", i);
else
printf ("(%d,F)", i);
printf ("\n");
printf ("Dump interface flags:");
#ifdef COMPRESSED_INTERFACE_BITMAP
{
const uint8_t* p = klass->interface_bitmap;
i = klass->max_interface_id;
while (i > 0) {
printf (" %d x 00 %02X", p [0], p [1]);
i -= p [0] * 8;
i -= 8;
}
}
#else
for (i = 0; i < ((((klass->max_interface_id + 1) >> 3)) + (((klass->max_interface_id + 1) & 7)? 1 :0)); i++)
printf (" %02X", klass->interface_bitmap [i]);
#endif
printf ("\n");
while (klass != NULL) {
printf ("[LEVEL %d] Implemented interfaces by class %s:\n", ancestor_level, klass->name);
ifaces = mono_class_get_implemented_interfaces (klass, &error);
if (!mono_error_ok (&error)) {
printf (" Type failed due to %s\n", mono_error_get_message (&error));
mono_error_cleanup (&error);
} else if (ifaces) {
for (i = 0; i < ifaces->len; i++) {
MonoClass *ic = (MonoClass *)g_ptr_array_index (ifaces, i);
printf (" [UIID %d] interface %s\n", ic->interface_id, ic->name);
printf (" [%03d][UUID %03d][SLOT %03d][SIZE %03d] interface %s.%s\n", i,
ic->interface_id,
mono_class_interface_offset (klass, ic),
mono_class_get_method_count (ic),
ic->name_space,
ic->name );
}
g_ptr_array_free (ifaces, TRUE);
}
ancestor_level ++;
klass = klass->parent;
}
}
/*
* Return the number of virtual methods.
* Even for interfaces we can't simply return the number of methods as all CLR types are allowed to have static methods.
* Return -1 on failure.
* FIXME It would be nice if this information could be cached somewhere.
*/
static int
count_virtual_methods (MonoClass *klass)
{
int i, mcount, vcount = 0;
guint32 flags;
klass = mono_class_get_generic_type_definition (klass); /*We can find this information by looking at the GTD*/
if (klass->methods || !MONO_CLASS_HAS_STATIC_METADATA (klass)) {
mono_class_setup_methods (klass);
if (mono_class_has_failure (klass))
return -1;
mcount = mono_class_get_method_count (klass);
for (i = 0; i < mcount; ++i) {
flags = klass->methods [i]->flags;
if (flags & METHOD_ATTRIBUTE_VIRTUAL)
++vcount;
}
} else {
int first_idx = mono_class_get_first_method_idx (klass);
mcount = mono_class_get_method_count (klass);
for (i = 0; i < mcount; ++i) {
flags = mono_metadata_decode_table_row_col (klass->image, MONO_TABLE_METHOD, first_idx + i, MONO_METHOD_FLAGS);