/mono/metadata/object.c
C | 6619 lines | 4349 code | 831 blank | 1439 comment | 913 complexity | cfd99e509ea9c9a7059291fd6f301eeb MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
Large files files are truncated, but you can click here to view the full file
- /*
- * object.c: Object creation for the Mono runtime
- *
- * Author:
- * Miguel de Icaza (miguel@ximian.com)
- * Paolo Molaro (lupus@ximian.com)
- *
- * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
- * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
- * Copyright 2001 Xamarin Inc (http://www.xamarin.com)
- */
- #include <config.h>
- #ifdef HAVE_ALLOCA_H
- #include <alloca.h>
- #endif
- #include <stdlib.h>
- #include <stdio.h>
- #include <signal.h>
- #include <string.h>
- #include <mono/metadata/mono-endian.h>
- #include <mono/metadata/tabledefs.h>
- #include <mono/metadata/tokentype.h>
- #include <mono/metadata/loader.h>
- #include <mono/metadata/object.h>
- #include <mono/metadata/gc-internal.h>
- #include <mono/metadata/exception.h>
- #include <mono/metadata/domain-internals.h>
- #include "mono/metadata/metadata-internals.h"
- #include "mono/metadata/class-internals.h"
- #include <mono/metadata/assembly.h>
- #include <mono/metadata/threadpool.h>
- #include <mono/metadata/marshal.h>
- #include "mono/metadata/debug-helpers.h"
- #include "mono/metadata/marshal.h"
- #include <mono/metadata/threads.h>
- #include <mono/metadata/threads-types.h>
- #include <mono/metadata/environment.h>
- #include "mono/metadata/profiler-private.h"
- #include "mono/metadata/security-manager.h"
- #include "mono/metadata/mono-debug-debugger.h"
- #include <mono/metadata/gc-internal.h>
- #include <mono/metadata/verify-internals.h>
- #include <mono/utils/strenc.h>
- #include <mono/utils/mono-counters.h>
- #include <mono/utils/mono-error-internals.h>
- #include <mono/utils/mono-memory-model.h>
- #include "cominterop.h"
- #ifdef HAVE_BOEHM_GC
- #define NEED_TO_ZERO_PTRFREE 1
- #define ALLOC_PTRFREE(obj,vt,size) do { (obj) = GC_MALLOC_ATOMIC ((size)); (obj)->vtable = (vt); (obj)->synchronisation = NULL;} while (0)
- #define ALLOC_OBJECT(obj,vt,size) do { (obj) = GC_MALLOC ((size)); (obj)->vtable = (vt);} while (0)
- #ifdef HAVE_GC_GCJ_MALLOC
- #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
- #define ALLOC_TYPED(dest,size,type) do { (dest) = GC_GCJ_MALLOC ((size),(type)); } while (0)
- #else
- #define GC_NO_DESCRIPTOR (NULL)
- #define ALLOC_TYPED(dest,size,type) do { (dest) = GC_MALLOC ((size)); *(gpointer*)dest = (type);} while (0)
- #endif
- #else
- #ifdef HAVE_SGEN_GC
- #define GC_NO_DESCRIPTOR (NULL)
- #define ALLOC_PTRFREE(obj,vt,size) do { (obj) = mono_gc_alloc_obj (vt, size);} while (0)
- #define ALLOC_OBJECT(obj,vt,size) do { (obj) = mono_gc_alloc_obj (vt, size);} while (0)
- #define ALLOC_TYPED(dest,size,type) do { (dest) = mono_gc_alloc_obj (type, size);} while (0)
- #else
- #define NEED_TO_ZERO_PTRFREE 1
- #define GC_NO_DESCRIPTOR (NULL)
- #define ALLOC_PTRFREE(obj,vt,size) do { (obj) = malloc ((size)); (obj)->vtable = (vt); (obj)->synchronisation = NULL;} while (0)
- #define ALLOC_OBJECT(obj,vt,size) do { (obj) = calloc (1, (size)); (obj)->vtable = (vt);} while (0)
- #define ALLOC_TYPED(dest,size,type) do { (dest) = calloc (1, (size)); *(gpointer*)dest = (type);} while (0)
- #endif
- #endif
- static MonoObject* mono_object_new_ptrfree (MonoVTable *vtable);
- static MonoObject* mono_object_new_ptrfree_box (MonoVTable *vtable);
- static void
- get_default_field_value (MonoDomain* domain, MonoClassField *field, void *value);
- static MonoString*
- mono_ldstr_metadata_sig (MonoDomain *domain, const char* sig);
- static void
- free_main_args (void);
- static char *
- mono_string_to_utf8_internal (MonoMemPool *mp, MonoImage *image, MonoString *s, gboolean ignore_error, MonoError *error);
- #define ldstr_lock() EnterCriticalSection (&ldstr_section)
- #define ldstr_unlock() LeaveCriticalSection (&ldstr_section)
- static CRITICAL_SECTION ldstr_section;
- static gboolean profile_allocs = TRUE;
- void
- mono_runtime_object_init (MonoObject *this)
- {
- MonoMethod *method = NULL;
- MonoClass *klass = this->vtable->klass;
- method = mono_class_get_method_from_name (klass, ".ctor", 0);
- if (!method)
- g_error ("Could not lookup zero argument constructor for class %s", mono_type_get_full_name (klass));
- if (method->klass->valuetype)
- this = mono_object_unbox (this);
- mono_runtime_invoke (method, this, NULL, NULL);
- }
- /* The pseudo algorithm for type initialization from the spec
- Note it doesn't say anything about domains - only threads.
- 2. If the type is initialized you are done.
- 2.1. If the type is not yet initialized, try to take an
- initialization lock.
- 2.2. If successful, record this thread as responsible for
- initializing the type and proceed to step 2.3.
- 2.2.1. If not, see whether this thread or any thread
- waiting for this thread to complete already holds the lock.
- 2.2.2. If so, return since blocking would create a deadlock. This thread
- will now see an incompletely initialized state for the type,
- but no deadlock will arise.
- 2.2.3 If not, block until the type is initialized then return.
- 2.3 Initialize the parent type and then all interfaces implemented
- by this type.
- 2.4 Execute the type initialization code for this type.
- 2.5 Mark the type as initialized, release the initialization lock,
- awaken any threads waiting for this type to be initialized,
- and return.
- */
- typedef struct
- {
- guint32 initializing_tid;
- guint32 waiting_count;
- gboolean done;
- CRITICAL_SECTION initialization_section;
- } TypeInitializationLock;
- /* for locking access to type_initialization_hash and blocked_thread_hash */
- #define mono_type_initialization_lock() EnterCriticalSection (&type_initialization_section)
- #define mono_type_initialization_unlock() LeaveCriticalSection (&type_initialization_section)
- static CRITICAL_SECTION type_initialization_section;
- /* from vtable to lock */
- static GHashTable *type_initialization_hash;
- /* from thread id to thread id being waited on */
- static GHashTable *blocked_thread_hash;
- /* Main thread */
- static MonoThread *main_thread;
- /* Functions supplied by the runtime */
- static MonoRuntimeCallbacks callbacks;
- /**
- * mono_thread_set_main:
- * @thread: thread to set as the main thread
- *
- * This function can be used to instruct the runtime to treat @thread
- * as the main thread, ie, the thread that would normally execute the Main()
- * method. This basically means that at the end of @thread, the runtime will
- * wait for the existing foreground threads to quit and other such details.
- */
- void
- mono_thread_set_main (MonoThread *thread)
- {
- static gboolean registered = FALSE;
- if (!registered) {
- MONO_GC_REGISTER_ROOT_SINGLE (main_thread);
- registered = TRUE;
- }
- main_thread = thread;
- }
- MonoThread*
- mono_thread_get_main (void)
- {
- return main_thread;
- }
- void
- mono_type_initialization_init (void)
- {
- InitializeCriticalSection (&type_initialization_section);
- type_initialization_hash = g_hash_table_new (NULL, NULL);
- blocked_thread_hash = g_hash_table_new (NULL, NULL);
- InitializeCriticalSection (&ldstr_section);
- }
- void
- mono_type_initialization_cleanup (void)
- {
- #if 0
- /* This is causing race conditions with
- * mono_release_type_locks
- */
- DeleteCriticalSection (&type_initialization_section);
- g_hash_table_destroy (type_initialization_hash);
- type_initialization_hash = NULL;
- #endif
- DeleteCriticalSection (&ldstr_section);
- g_hash_table_destroy (blocked_thread_hash);
- blocked_thread_hash = NULL;
- free_main_args ();
- }
- /**
- * get_type_init_exception_for_vtable:
- *
- * Return the stored type initialization exception for VTABLE.
- */
- static MonoException*
- get_type_init_exception_for_vtable (MonoVTable *vtable)
- {
- MonoDomain *domain = vtable->domain;
- MonoClass *klass = vtable->klass;
- MonoException *ex;
- gchar *full_name;
- if (!vtable->init_failed)
- g_error ("Trying to get the init exception for a non-failed vtable of class %s", mono_type_get_full_name (klass));
-
- /*
- * If the initializing thread was rudely aborted, the exception is not stored
- * in the hash.
- */
- ex = NULL;
- mono_domain_lock (domain);
- if (domain->type_init_exception_hash)
- ex = mono_g_hash_table_lookup (domain->type_init_exception_hash, klass);
- mono_domain_unlock (domain);
- if (!ex) {
- if (klass->name_space && *klass->name_space)
- full_name = g_strdup_printf ("%s.%s", klass->name_space, klass->name);
- else
- full_name = g_strdup (klass->name);
- ex = mono_get_exception_type_initialization (full_name, NULL);
- g_free (full_name);
- }
- return ex;
- }
- /*
- * mono_runtime_class_init:
- * @vtable: vtable that needs to be initialized
- *
- * This routine calls the class constructor for @vtable.
- */
- void
- mono_runtime_class_init (MonoVTable *vtable)
- {
- mono_runtime_class_init_full (vtable, TRUE);
- }
- /*
- * mono_runtime_class_init_full:
- * @vtable that neeeds to be initialized
- * @raise_exception is TRUE, exceptions are raised intead of returned
- *
- */
- MonoException *
- mono_runtime_class_init_full (MonoVTable *vtable, gboolean raise_exception)
- {
- MonoException *exc;
- MonoException *exc_to_throw;
- MonoMethod *method = NULL;
- MonoClass *klass;
- gchar *full_name;
- MONO_ARCH_SAVE_REGS;
- if (vtable->initialized)
- return NULL;
- exc = NULL;
- klass = vtable->klass;
- if (!klass->image->checked_module_cctor) {
- mono_image_check_for_module_cctor (klass->image);
- if (klass->image->has_module_cctor) {
- MonoClass *module_klass = mono_class_get (klass->image, MONO_TOKEN_TYPE_DEF | 1);
- MonoVTable *module_vtable = mono_class_vtable_full (vtable->domain, module_klass, raise_exception);
- if (!module_vtable)
- return NULL;
- exc = mono_runtime_class_init_full (module_vtable, raise_exception);
- if (exc)
- return exc;
- }
- }
- method = mono_class_get_cctor (klass);
- if (method) {
- MonoDomain *domain = vtable->domain;
- TypeInitializationLock *lock;
- guint32 tid = GetCurrentThreadId();
- int do_initialization = 0;
- MonoDomain *last_domain = NULL;
- mono_type_initialization_lock ();
- /* double check... */
- if (vtable->initialized) {
- mono_type_initialization_unlock ();
- return NULL;
- }
- if (vtable->init_failed) {
- mono_type_initialization_unlock ();
- /* The type initialization already failed once, rethrow the same exception */
- if (raise_exception)
- mono_raise_exception (get_type_init_exception_for_vtable (vtable));
- return get_type_init_exception_for_vtable (vtable);
- }
- lock = g_hash_table_lookup (type_initialization_hash, vtable);
- if (lock == NULL) {
- /* This thread will get to do the initialization */
- if (mono_domain_get () != domain) {
- /* Transfer into the target domain */
- last_domain = mono_domain_get ();
- if (!mono_domain_set (domain, FALSE)) {
- vtable->initialized = 1;
- mono_type_initialization_unlock ();
- if (raise_exception)
- mono_raise_exception (mono_get_exception_appdomain_unloaded ());
- return mono_get_exception_appdomain_unloaded ();
- }
- }
- lock = g_malloc (sizeof(TypeInitializationLock));
- InitializeCriticalSection (&lock->initialization_section);
- lock->initializing_tid = tid;
- lock->waiting_count = 1;
- lock->done = FALSE;
- /* grab the vtable lock while this thread still owns type_initialization_section */
- EnterCriticalSection (&lock->initialization_section);
- g_hash_table_insert (type_initialization_hash, vtable, lock);
- do_initialization = 1;
- } else {
- gpointer blocked;
- TypeInitializationLock *pending_lock;
- if (lock->initializing_tid == tid || lock->done) {
- mono_type_initialization_unlock ();
- return NULL;
- }
- /* see if the thread doing the initialization is already blocked on this thread */
- blocked = GUINT_TO_POINTER (lock->initializing_tid);
- while ((pending_lock = (TypeInitializationLock*) g_hash_table_lookup (blocked_thread_hash, blocked))) {
- if (pending_lock->initializing_tid == tid) {
- if (!pending_lock->done) {
- mono_type_initialization_unlock ();
- return NULL;
- } else {
- /* the thread doing the initialization is blocked on this thread,
- but on a lock that has already been freed. It just hasn't got
- time to awake */
- break;
- }
- }
- blocked = GUINT_TO_POINTER (pending_lock->initializing_tid);
- }
- ++lock->waiting_count;
- /* record the fact that we are waiting on the initializing thread */
- g_hash_table_insert (blocked_thread_hash, GUINT_TO_POINTER (tid), lock);
- }
- mono_type_initialization_unlock ();
- if (do_initialization) {
- mono_runtime_invoke (method, NULL, NULL, (MonoObject **) &exc);
- /* If the initialization failed, mark the class as unusable. */
- /* Avoid infinite loops */
- if (!(exc == NULL ||
- (klass->image == mono_defaults.corlib &&
- !strcmp (klass->name_space, "System") &&
- !strcmp (klass->name, "TypeInitializationException")))) {
- vtable->init_failed = 1;
- if (klass->name_space && *klass->name_space)
- full_name = g_strdup_printf ("%s.%s", klass->name_space, klass->name);
- else
- full_name = g_strdup (klass->name);
- exc_to_throw = mono_get_exception_type_initialization (full_name, exc);
- g_free (full_name);
- /*
- * Store the exception object so it could be thrown on subsequent
- * accesses.
- */
- mono_domain_lock (domain);
- if (!domain->type_init_exception_hash)
- domain->type_init_exception_hash = mono_g_hash_table_new_type (mono_aligned_addr_hash, NULL, MONO_HASH_VALUE_GC);
- mono_g_hash_table_insert (domain->type_init_exception_hash, klass, exc_to_throw);
- mono_domain_unlock (domain);
- }
- if (last_domain)
- mono_domain_set (last_domain, TRUE);
- lock->done = TRUE;
- LeaveCriticalSection (&lock->initialization_section);
- } else {
- /* this just blocks until the initializing thread is done */
- EnterCriticalSection (&lock->initialization_section);
- LeaveCriticalSection (&lock->initialization_section);
- }
- mono_type_initialization_lock ();
- if (lock->initializing_tid != tid)
- g_hash_table_remove (blocked_thread_hash, GUINT_TO_POINTER (tid));
- --lock->waiting_count;
- if (lock->waiting_count == 0) {
- DeleteCriticalSection (&lock->initialization_section);
- g_hash_table_remove (type_initialization_hash, vtable);
- g_free (lock);
- }
- mono_memory_barrier ();
- if (!vtable->init_failed)
- vtable->initialized = 1;
- mono_type_initialization_unlock ();
- if (vtable->init_failed) {
- /* Either we were the initializing thread or we waited for the initialization */
- if (raise_exception)
- mono_raise_exception (get_type_init_exception_for_vtable (vtable));
- return get_type_init_exception_for_vtable (vtable);
- }
- } else {
- vtable->initialized = 1;
- return NULL;
- }
- return NULL;
- }
- static
- gboolean release_type_locks (gpointer key, gpointer value, gpointer user)
- {
- MonoVTable *vtable = (MonoVTable*)key;
- TypeInitializationLock *lock = (TypeInitializationLock*) value;
- if (lock->initializing_tid == GPOINTER_TO_UINT (user) && !lock->done) {
- lock->done = TRUE;
- /*
- * Have to set this since it cannot be set by the normal code in
- * mono_runtime_class_init (). In this case, the exception object is not stored,
- * and get_type_init_exception_for_class () needs to be aware of this.
- */
- vtable->init_failed = 1;
- LeaveCriticalSection (&lock->initialization_section);
- --lock->waiting_count;
- if (lock->waiting_count == 0) {
- DeleteCriticalSection (&lock->initialization_section);
- g_free (lock);
- return TRUE;
- }
- }
- return FALSE;
- }
- void
- mono_release_type_locks (MonoInternalThread *thread)
- {
- mono_type_initialization_lock ();
- g_hash_table_foreach_remove (type_initialization_hash, release_type_locks, (gpointer)(gsize)(thread->tid));
- mono_type_initialization_unlock ();
- }
- static gpointer
- default_trampoline (MonoMethod *method)
- {
- return method;
- }
- static gpointer
- default_jump_trampoline (MonoDomain *domain, MonoMethod *method, gboolean add_sync_wrapper)
- {
- g_assert_not_reached ();
- return NULL;
- }
- #ifndef DISABLE_REMOTING
- static gpointer
- default_remoting_trampoline (MonoDomain *domain, MonoMethod *method, MonoRemotingTarget target)
- {
- g_error ("remoting not installed");
- return NULL;
- }
- static MonoRemotingTrampoline arch_create_remoting_trampoline = default_remoting_trampoline;
- #endif
- static gpointer
- default_delegate_trampoline (MonoDomain *domain, MonoClass *klass)
- {
- g_assert_not_reached ();
- return NULL;
- }
- static MonoTrampoline arch_create_jit_trampoline = default_trampoline;
- static MonoJumpTrampoline arch_create_jump_trampoline = default_jump_trampoline;
- static MonoDelegateTrampoline arch_create_delegate_trampoline = default_delegate_trampoline;
- static MonoImtThunkBuilder imt_thunk_builder = NULL;
- #define ARCH_USE_IMT (imt_thunk_builder != NULL)
- #if (MONO_IMT_SIZE > 32)
- #error "MONO_IMT_SIZE cannot be larger than 32"
- #endif
- void
- mono_install_callbacks (MonoRuntimeCallbacks *cbs)
- {
- memcpy (&callbacks, cbs, sizeof (*cbs));
- }
- MonoRuntimeCallbacks*
- mono_get_runtime_callbacks (void)
- {
- return &callbacks;
- }
- void
- mono_install_trampoline (MonoTrampoline func)
- {
- arch_create_jit_trampoline = func? func: default_trampoline;
- }
- void
- mono_install_jump_trampoline (MonoJumpTrampoline func)
- {
- arch_create_jump_trampoline = func? func: default_jump_trampoline;
- }
- #ifndef DISABLE_REMOTING
- void
- mono_install_remoting_trampoline (MonoRemotingTrampoline func)
- {
- arch_create_remoting_trampoline = func? func: default_remoting_trampoline;
- }
- #endif
- void
- mono_install_delegate_trampoline (MonoDelegateTrampoline func)
- {
- arch_create_delegate_trampoline = func? func: default_delegate_trampoline;
- }
- void
- mono_install_imt_thunk_builder (MonoImtThunkBuilder func) {
- imt_thunk_builder = func;
- }
- static MonoCompileFunc default_mono_compile_method = NULL;
- /**
- * mono_install_compile_method:
- * @func: function to install
- *
- * This is a VM internal routine
- */
- void
- mono_install_compile_method (MonoCompileFunc func)
- {
- default_mono_compile_method = func;
- }
- /**
- * mono_compile_method:
- * @method: The method to compile.
- *
- * This JIT-compiles the method, and returns the pointer to the native code
- * produced.
- */
- gpointer
- mono_compile_method (MonoMethod *method)
- {
- if (!default_mono_compile_method) {
- g_error ("compile method called on uninitialized runtime");
- return NULL;
- }
- return default_mono_compile_method (method);
- }
- gpointer
- mono_runtime_create_jump_trampoline (MonoDomain *domain, MonoMethod *method, gboolean add_sync_wrapper)
- {
- return arch_create_jump_trampoline (domain, method, add_sync_wrapper);
- }
- gpointer
- mono_runtime_create_delegate_trampoline (MonoClass *klass)
- {
- return arch_create_delegate_trampoline (mono_domain_get (), klass);
- }
- static MonoFreeMethodFunc default_mono_free_method = NULL;
- /**
- * mono_install_free_method:
- * @func: pointer to the MonoFreeMethodFunc used to release a method
- *
- * This is an internal VM routine, it is used for the engines to
- * register a handler to release the resources associated with a method.
- *
- * Methods are freed when no more references to the delegate that holds
- * them are left.
- */
- void
- mono_install_free_method (MonoFreeMethodFunc func)
- {
- default_mono_free_method = func;
- }
- /**
- * mono_runtime_free_method:
- * @domain; domain where the method is hosted
- * @method: method to release
- *
- * This routine is invoked to free the resources associated with
- * a method that has been JIT compiled. This is used to discard
- * methods that were used only temporarily (for example, used in marshalling)
- *
- */
- void
- mono_runtime_free_method (MonoDomain *domain, MonoMethod *method)
- {
- if (default_mono_free_method != NULL)
- default_mono_free_method (domain, method);
- mono_method_clear_object (domain, method);
- mono_free_method (method);
- }
- /*
- * The vtables in the root appdomain are assumed to be reachable by other
- * roots, and we don't use typed allocation in the other domains.
- */
- /* The sync block is no longer a GC pointer */
- #define GC_HEADER_BITMAP (0)
- #define BITMAP_EL_SIZE (sizeof (gsize) * 8)
- static gsize*
- compute_class_bitmap (MonoClass *class, gsize *bitmap, int size, int offset, int *max_set, gboolean static_fields)
- {
- MonoClassField *field;
- MonoClass *p;
- guint32 pos;
- int max_size;
- if (static_fields)
- max_size = mono_class_data_size (class) / sizeof (gpointer);
- else
- max_size = class->instance_size / sizeof (gpointer);
- if (max_size > size) {
- g_assert (offset <= 0);
- bitmap = g_malloc0 ((max_size + BITMAP_EL_SIZE - 1) / BITMAP_EL_SIZE * sizeof (gsize));
- size = max_size;
- }
- #ifdef HAVE_SGEN_GC
- /*An Ephemeron cannot be marked by sgen*/
- if (!static_fields && class->image == mono_defaults.corlib && !strcmp ("Ephemeron", class->name)) {
- *max_set = 0;
- memset (bitmap, 0, size / 8);
- return bitmap;
- }
- #endif
- for (p = class; p != NULL; p = p->parent) {
- gpointer iter = NULL;
- while ((field = mono_class_get_fields (p, &iter))) {
- MonoType *type;
- if (static_fields) {
- if (!(field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA)))
- continue;
- if (field->type->attrs & FIELD_ATTRIBUTE_LITERAL)
- continue;
- } else {
- if (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA))
- continue;
- }
- /* FIXME: should not happen, flag as type load error */
- if (field->type->byref)
- break;
- if (static_fields && field->offset == -1)
- /* special static */
- continue;
- pos = field->offset / sizeof (gpointer);
- pos += offset;
- type = mono_type_get_underlying_type (field->type);
- switch (type->type) {
- case MONO_TYPE_I:
- case MONO_TYPE_PTR:
- case MONO_TYPE_FNPTR:
- break;
- /* only UIntPtr is allowed to be GC-tracked and only in mscorlib */
- case MONO_TYPE_U:
- #ifdef HAVE_SGEN_GC
- break;
- #else
- if (class->image != mono_defaults.corlib)
- break;
- #endif
- case MONO_TYPE_STRING:
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_OBJECT:
- case MONO_TYPE_ARRAY:
- g_assert ((field->offset % sizeof(gpointer)) == 0);
- g_assert (pos < size || pos <= max_size);
- bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE);
- *max_set = MAX (*max_set, pos);
- break;
- case MONO_TYPE_GENERICINST:
- if (!mono_type_generic_inst_is_valuetype (type)) {
- g_assert ((field->offset % sizeof(gpointer)) == 0);
- bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE);
- *max_set = MAX (*max_set, pos);
- break;
- } else {
- /* fall through */
- }
- case MONO_TYPE_VALUETYPE: {
- MonoClass *fclass = mono_class_from_mono_type (field->type);
- if (fclass->has_references) {
- /* remove the object header */
- compute_class_bitmap (fclass, bitmap, size, pos - (sizeof (MonoObject) / sizeof (gpointer)), max_set, FALSE);
- }
- break;
- }
- case MONO_TYPE_I1:
- case MONO_TYPE_U1:
- case MONO_TYPE_I2:
- case MONO_TYPE_U2:
- case MONO_TYPE_I4:
- case MONO_TYPE_U4:
- case MONO_TYPE_I8:
- case MONO_TYPE_U8:
- case MONO_TYPE_R4:
- case MONO_TYPE_R8:
- case MONO_TYPE_BOOLEAN:
- case MONO_TYPE_CHAR:
- break;
- default:
- g_error ("compute_class_bitmap: Invalid type %x for field %s:%s\n", type->type, mono_type_get_full_name (field->parent), field->name);
- break;
- }
- }
- if (static_fields)
- break;
- }
- return bitmap;
- }
- /**
- * mono_class_compute_bitmap:
- *
- * Mono internal function to compute a bitmap of reference fields in a class.
- */
- gsize*
- mono_class_compute_bitmap (MonoClass *class, gsize *bitmap, int size, int offset, int *max_set, gboolean static_fields)
- {
- return compute_class_bitmap (class, bitmap, size, offset, max_set, static_fields);
- }
- #if 0
- /*
- * similar to the above, but sets the bits in the bitmap for any non-ref field
- * and ignores static fields
- */
- static gsize*
- compute_class_non_ref_bitmap (MonoClass *class, gsize *bitmap, int size, int offset)
- {
- MonoClassField *field;
- MonoClass *p;
- guint32 pos, pos2;
- int max_size;
- max_size = class->instance_size / sizeof (gpointer);
- if (max_size >= size) {
- bitmap = g_malloc0 (sizeof (gsize) * ((max_size) + 1));
- }
- for (p = class; p != NULL; p = p->parent) {
- gpointer iter = NULL;
- while ((field = mono_class_get_fields (p, &iter))) {
- MonoType *type;
- if (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA))
- continue;
- /* FIXME: should not happen, flag as type load error */
- if (field->type->byref)
- break;
- pos = field->offset / sizeof (gpointer);
- pos += offset;
- type = mono_type_get_underlying_type (field->type);
- switch (type->type) {
- #if SIZEOF_VOID_P == 8
- case MONO_TYPE_I:
- case MONO_TYPE_U:
- case MONO_TYPE_PTR:
- case MONO_TYPE_FNPTR:
- #endif
- case MONO_TYPE_I8:
- case MONO_TYPE_U8:
- case MONO_TYPE_R8:
- if ((((field->offset + 7) / sizeof (gpointer)) + offset) != pos) {
- pos2 = ((field->offset + 7) / sizeof (gpointer)) + offset;
- bitmap [pos2 / BITMAP_EL_SIZE] |= ((gsize)1) << (pos2 % BITMAP_EL_SIZE);
- }
- /* fall through */
- #if SIZEOF_VOID_P == 4
- case MONO_TYPE_I:
- case MONO_TYPE_U:
- case MONO_TYPE_PTR:
- case MONO_TYPE_FNPTR:
- #endif
- case MONO_TYPE_I4:
- case MONO_TYPE_U4:
- case MONO_TYPE_R4:
- if ((((field->offset + 3) / sizeof (gpointer)) + offset) != pos) {
- pos2 = ((field->offset + 3) / sizeof (gpointer)) + offset;
- bitmap [pos2 / BITMAP_EL_SIZE] |= ((gsize)1) << (pos2 % BITMAP_EL_SIZE);
- }
- /* fall through */
- case MONO_TYPE_CHAR:
- case MONO_TYPE_I2:
- case MONO_TYPE_U2:
- if ((((field->offset + 1) / sizeof (gpointer)) + offset) != pos) {
- pos2 = ((field->offset + 1) / sizeof (gpointer)) + offset;
- bitmap [pos2 / BITMAP_EL_SIZE] |= ((gsize)1) << (pos2 % BITMAP_EL_SIZE);
- }
- /* fall through */
- case MONO_TYPE_BOOLEAN:
- case MONO_TYPE_I1:
- case MONO_TYPE_U1:
- bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE);
- break;
- case MONO_TYPE_STRING:
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_OBJECT:
- case MONO_TYPE_ARRAY:
- break;
- case MONO_TYPE_GENERICINST:
- if (!mono_type_generic_inst_is_valuetype (type)) {
- break;
- } else {
- /* fall through */
- }
- case MONO_TYPE_VALUETYPE: {
- MonoClass *fclass = mono_class_from_mono_type (field->type);
- /* remove the object header */
- compute_class_non_ref_bitmap (fclass, bitmap, size, pos - (sizeof (MonoObject) / sizeof (gpointer)));
- break;
- }
- default:
- g_assert_not_reached ();
- break;
- }
- }
- }
- return bitmap;
- }
- /**
- * mono_class_insecure_overlapping:
- * check if a class with explicit layout has references and non-references
- * fields overlapping.
- *
- * Returns: TRUE if it is insecure to load the type.
- */
- gboolean
- mono_class_insecure_overlapping (MonoClass *klass)
- {
- int max_set = 0;
- gsize *bitmap;
- gsize default_bitmap [4] = {0};
- gsize *nrbitmap;
- gsize default_nrbitmap [4] = {0};
- int i, insecure = FALSE;
- return FALSE;
- bitmap = compute_class_bitmap (klass, default_bitmap, sizeof (default_bitmap) * 8, 0, &max_set, FALSE);
- nrbitmap = compute_class_non_ref_bitmap (klass, default_nrbitmap, sizeof (default_nrbitmap) * 8, 0);
- for (i = 0; i <= max_set; i += sizeof (bitmap [0]) * 8) {
- int idx = i % (sizeof (bitmap [0]) * 8);
- if (bitmap [idx] & nrbitmap [idx]) {
- insecure = TRUE;
- break;
- }
- }
- if (bitmap != default_bitmap)
- g_free (bitmap);
- if (nrbitmap != default_nrbitmap)
- g_free (nrbitmap);
- if (insecure) {
- g_print ("class %s.%s in assembly %s has overlapping references\n", klass->name_space, klass->name, klass->image->name);
- return FALSE;
- }
- return insecure;
- }
- #endif
- MonoString*
- mono_string_alloc (int length)
- {
- return mono_string_new_size (mono_domain_get (), length);
- }
- void
- mono_class_compute_gc_descriptor (MonoClass *class)
- {
- int max_set = 0;
- gsize *bitmap;
- gsize default_bitmap [4] = {0};
- static gboolean gcj_inited = FALSE;
- if (!gcj_inited) {
- mono_loader_lock ();
- mono_register_jit_icall (mono_object_new_ptrfree, "mono_object_new_ptrfree", mono_create_icall_signature ("object ptr"), FALSE);
- mono_register_jit_icall (mono_object_new_ptrfree_box, "mono_object_new_ptrfree_box", mono_create_icall_signature ("object ptr"), FALSE);
- mono_register_jit_icall (mono_object_new_fast, "mono_object_new_fast", mono_create_icall_signature ("object ptr"), FALSE);
- mono_register_jit_icall (mono_string_alloc, "mono_string_alloc", mono_create_icall_signature ("object int"), FALSE);
- #ifdef HAVE_GC_GCJ_MALLOC
- /*
- * This is not needed unless the relevant code in mono_get_allocation_ftn () is
- * turned on.
- */
- #if 0
- #ifdef GC_REDIRECT_TO_LOCAL
- mono_register_jit_icall (GC_local_gcj_malloc, "GC_local_gcj_malloc", mono_create_icall_signature ("object int ptr"), FALSE);
- mono_register_jit_icall (GC_local_gcj_fast_malloc, "GC_local_gcj_fast_malloc", mono_create_icall_signature ("object int ptr"), FALSE);
- #endif
- mono_register_jit_icall (GC_gcj_malloc, "GC_gcj_malloc", mono_create_icall_signature ("object int ptr"), FALSE);
- mono_register_jit_icall (GC_gcj_fast_malloc, "GC_gcj_fast_malloc", mono_create_icall_signature ("object int ptr"), FALSE);
- #endif
- #endif
- gcj_inited = TRUE;
- mono_loader_unlock ();
- }
- if (!class->inited)
- mono_class_init (class);
- if (class->gc_descr_inited)
- return;
- class->gc_descr_inited = TRUE;
- class->gc_descr = GC_NO_DESCRIPTOR;
- bitmap = default_bitmap;
- if (class == mono_defaults.string_class) {
- class->gc_descr = (gpointer)mono_gc_make_descr_for_string (bitmap, 2);
- } else if (class->rank) {
- mono_class_compute_gc_descriptor (class->element_class);
- if (!class->element_class->valuetype) {
- gsize abm = 1;
- class->gc_descr = mono_gc_make_descr_for_array (class->byval_arg.type == MONO_TYPE_SZARRAY, &abm, 1, sizeof (gpointer));
- /*printf ("new array descriptor: 0x%x for %s.%s\n", class->gc_descr,
- class->name_space, class->name);*/
- } else {
- /* remove the object header */
- bitmap = compute_class_bitmap (class->element_class, default_bitmap, sizeof (default_bitmap) * 8, - (int)(sizeof (MonoObject) / sizeof (gpointer)), &max_set, FALSE);
- class->gc_descr = mono_gc_make_descr_for_array (class->byval_arg.type == MONO_TYPE_SZARRAY, bitmap, mono_array_element_size (class) / sizeof (gpointer), mono_array_element_size (class));
- /*printf ("new vt array descriptor: 0x%x for %s.%s\n", class->gc_descr,
- class->name_space, class->name);*/
- if (bitmap != default_bitmap)
- g_free (bitmap);
- }
- } else {
- /*static int count = 0;
- if (count++ > 58)
- return;*/
- bitmap = compute_class_bitmap (class, default_bitmap, sizeof (default_bitmap) * 8, 0, &max_set, FALSE);
- class->gc_descr = (gpointer)mono_gc_make_descr_for_object (bitmap, max_set + 1, class->instance_size);
- /*
- if (class->gc_descr == GC_NO_DESCRIPTOR)
- g_print ("disabling typed alloc (%d) for %s.%s\n", max_set, class->name_space, class->name);
- */
- /*printf ("new descriptor: %p 0x%x for %s.%s\n", class->gc_descr, bitmap [0], class->name_space, class->name);*/
- if (bitmap != default_bitmap)
- g_free (bitmap);
- }
- }
- /**
- * field_is_special_static:
- * @fklass: The MonoClass to look up.
- * @field: The MonoClassField describing the field.
- *
- * Returns: SPECIAL_STATIC_THREAD if the field is thread static, SPECIAL_STATIC_CONTEXT if it is context static,
- * SPECIAL_STATIC_NONE otherwise.
- */
- static gint32
- field_is_special_static (MonoClass *fklass, MonoClassField *field)
- {
- MonoCustomAttrInfo *ainfo;
- int i;
- ainfo = mono_custom_attrs_from_field (fklass, field);
- if (!ainfo)
- return FALSE;
- for (i = 0; i < ainfo->num_attrs; ++i) {
- MonoClass *klass = ainfo->attrs [i].ctor->klass;
- if (klass->image == mono_defaults.corlib) {
- if (strcmp (klass->name, "ThreadStaticAttribute") == 0) {
- mono_custom_attrs_free (ainfo);
- return SPECIAL_STATIC_THREAD;
- }
- else if (strcmp (klass->name, "ContextStaticAttribute") == 0) {
- mono_custom_attrs_free (ainfo);
- return SPECIAL_STATIC_CONTEXT;
- }
- }
- }
- mono_custom_attrs_free (ainfo);
- return SPECIAL_STATIC_NONE;
- }
- #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
- #define mix(a,b,c) { \
- a -= c; a ^= rot(c, 4); c += b; \
- b -= a; b ^= rot(a, 6); a += c; \
- c -= b; c ^= rot(b, 8); b += a; \
- a -= c; a ^= rot(c,16); c += b; \
- b -= a; b ^= rot(a,19); a += c; \
- c -= b; c ^= rot(b, 4); b += a; \
- }
- #define final(a,b,c) { \
- c ^= b; c -= rot(b,14); \
- a ^= c; a -= rot(c,11); \
- b ^= a; b -= rot(a,25); \
- c ^= b; c -= rot(b,16); \
- a ^= c; a -= rot(c,4); \
- b ^= a; b -= rot(a,14); \
- c ^= b; c -= rot(b,24); \
- }
- /*
- * mono_method_get_imt_slot:
- *
- * The IMT slot is embedded into AOTed code, so this must return the same value
- * for the same method across all executions. This means:
- * - pointers shouldn't be used as hash values.
- * - mono_metadata_str_hash () should be used for hashing strings.
- */
- guint32
- mono_method_get_imt_slot (MonoMethod *method)
- {
- MonoMethodSignature *sig;
- int hashes_count;
- guint32 *hashes_start, *hashes;
- guint32 a, b, c;
- int i;
- /* This can be used to stress tests the collision code */
- //return 0;
- /*
- * We do this to simplify generic sharing. It will hurt
- * performance in cases where a class implements two different
- * instantiations of the same generic interface.
- * The code in build_imt_slots () depends on this.
- */
- if (method->is_inflated)
- method = ((MonoMethodInflated*)method)->declaring;
- sig = mono_method_signature (method);
- hashes_count = sig->param_count + 4;
- hashes_start = malloc (hashes_count * sizeof (guint32));
- hashes = hashes_start;
- if (! MONO_CLASS_IS_INTERFACE (method->klass)) {
- g_error ("mono_method_get_imt_slot: %s.%s.%s is not an interface MonoMethod",
- method->klass->name_space, method->klass->name, method->name);
- }
-
- /* Initialize hashes */
- hashes [0] = mono_metadata_str_hash (method->klass->name);
- hashes [1] = mono_metadata_str_hash (method->klass->name_space);
- hashes [2] = mono_metadata_str_hash (method->name);
- hashes [3] = mono_metadata_type_hash (sig->ret);
- for (i = 0; i < sig->param_count; i++) {
- hashes [4 + i] = mono_metadata_type_hash (sig->params [i]);
- }
- /* Setup internal state */
- a = b = c = 0xdeadbeef + (((guint32)hashes_count)<<2);
- /* Handle most of the hashes */
- while (hashes_count > 3) {
- a += hashes [0];
- b += hashes [1];
- c += hashes [2];
- mix (a,b,c);
- hashes_count -= 3;
- hashes += 3;
- }
- /* Handle the last 3 hashes (all the case statements fall through) */
- switch (hashes_count) {
- case 3 : c += hashes [2];
- case 2 : b += hashes [1];
- case 1 : a += hashes [0];
- final (a,b,c);
- case 0: /* nothing left to add */
- break;
- }
-
- free (hashes_start);
- /* Report the result */
- return c % MONO_IMT_SIZE;
- }
- #undef rot
- #undef mix
- #undef final
- #define DEBUG_IMT 0
- static void
- add_imt_builder_entry (MonoImtBuilderEntry **imt_builder, MonoMethod *method, guint32 *imt_collisions_bitmap, int vtable_slot, int slot_num) {
- guint32 imt_slot = mono_method_get_imt_slot (method);
- MonoImtBuilderEntry *entry;
- if (slot_num >= 0 && imt_slot != slot_num) {
- /* we build just a single imt slot and this is not it */
- return;
- }
- entry = g_malloc0 (sizeof (MonoImtBuilderEntry));
- entry->key = method;
- entry->value.vtable_slot = vtable_slot;
- entry->next = imt_builder [imt_slot];
- if (imt_builder [imt_slot] != NULL) {
- entry->children = imt_builder [imt_slot]->children + 1;
- if (entry->children == 1) {
- mono_stats.imt_slots_with_collisions++;
- *imt_collisions_bitmap |= (1 << imt_slot);
- }
- } else {
- entry->children = 0;
- mono_stats.imt_used_slots++;
- }
- imt_builder [imt_slot] = entry;
- #if DEBUG_IMT
- {
- char *method_name = mono_method_full_name (method, TRUE);
- printf ("Added IMT slot for method (%p) %s: imt_slot = %d, vtable_slot = %d, colliding with other %d entries\n",
- method, method_name, imt_slot, vtable_slot, entry->children);
- g_free (method_name);
- }
- #endif
- }
- #if DEBUG_IMT
- static void
- print_imt_entry (const char* message, MonoImtBuilderEntry *e, int num) {
- if (e != NULL) {
- MonoMethod *method = e->key;
- printf (" * %s [%d]: (%p) '%s.%s.%s'\n",
- message,
- num,
- method,
- method->klass->name_space,
- method->klass->name,
- method->name);
- } else {
- printf (" * %s: NULL\n", message);
- }
- }
- #endif
- static int
- compare_imt_builder_entries (const void *p1, const void *p2) {
- MonoImtBuilderEntry *e1 = *(MonoImtBuilderEntry**) p1;
- MonoImtBuilderEntry *e2 = *(MonoImtBuilderEntry**) p2;
-
- return (e1->key < e2->key) ? -1 : ((e1->key > e2->key) ? 1 : 0);
- }
- static int
- imt_emit_ir (MonoImtBuilderEntry **sorted_array, int start, int end, GPtrArray *out_array)
- {
- int count = end - start;
- int chunk_start = out_array->len;
- if (count < 4) {
- int i;
- for (i = start; i < end; ++i) {
- MonoIMTCheckItem *item = g_new0 (MonoIMTCheckItem, 1);
- item->key = sorted_array [i]->key;
- item->value = sorted_array [i]->value;
- item->has_target_code = sorted_array [i]->has_target_code;
- item->is_equals = TRUE;
- if (i < end - 1)
- item->check_target_idx = out_array->len + 1;
- else
- item->check_target_idx = 0;
- g_ptr_array_add (out_array, item);
- }
- } else {
- int middle = start + count / 2;
- MonoIMTCheckItem *item = g_new0 (MonoIMTCheckItem, 1);
- item->key = sorted_array [middle]->key;
- item->is_equals = FALSE;
- g_ptr_array_add (out_array, item);
- imt_emit_ir (sorted_array, start, middle, out_array);
- item->check_target_idx = imt_emit_ir (sorted_array, middle, end, out_array);
- }
- return chunk_start;
- }
- static GPtrArray*
- imt_sort_slot_entries (MonoImtBuilderEntry *entries) {
- int number_of_entries = entries->children + 1;
- MonoImtBuilderEntry **sorted_array = malloc (sizeof (MonoImtBuilderEntry*) * number_of_entries);
- GPtrArray *result = g_ptr_array_new ();
- MonoImtBuilderEntry *current_entry;
- int i;
-
- for (current_entry = entries, i = 0; current_entry != NULL; current_entry = current_entry->next, i++) {
- sorted_array [i] = current_entry;
- }
- qsort (sorted_array, number_of_entries, sizeof (MonoImtBuilderEntry*), compare_imt_builder_entries);
- /*for (i = 0; i < number_of_entries; i++) {
- print_imt_entry (" sorted array:", sorted_array [i], i);
- }*/
- imt_emit_ir (sorted_array, 0, number_of_entries, result);
- free (sorted_array);
- return result;
- }
- static gpointer
- initialize_imt_slot (MonoVTable *vtable, MonoDomain *domain, MonoImtBuilderEntry *imt_builder_entry, gpointer fail_tramp)
- {
- if (imt_builder_entry != NULL) {
- if (imt_builder_entry->children == 0 && !fail_tramp) {
- /* No collision, return the vtable slot contents */
- return vtable->vtable [imt_builder_entry->value.vtable_slot];
- } else {
- /* Collision, build the thunk */
- GPtrArray *imt_ir = imt_sort_slot_entries (imt_builder_entry);
- gpointer result;
- int i;
- result = imt_thunk_builder (vtable, domain,
- (MonoIMTCheckItem**)imt_ir->pdata, imt_ir->len, fail_tramp);
- for (i = 0; i < imt_ir->len; ++i)
- g_free (g_ptr_array_index (imt_ir, i));
- g_ptr_array_free (imt_ir, TRUE);
- return result;
- }
- } else {
- if (fail_tramp)
- return fail_tramp;
- else
- /* Empty slot */
- return NULL;
- }
- }
- static MonoImtBuilderEntry*
- get_generic_virtual_entries (MonoDomain *domain, gpointer *vtable_slot);
- /*
- * LOCKING: requires the loader and domain locks.
- *
- */
- static void
- build_imt_slots (MonoClass *klass, MonoVTable *vt, MonoDomain *domain, gpointer* imt, GSList *extra_interfaces, int slot_num)
- {
- int i;
- GSList *list_item;
- guint32 imt_collisions_bitmap = 0;
- MonoImtBuilderEntry **imt_builder = calloc (MONO_IMT_SIZE, sizeof (MonoImtBuilderEntry*));
- int method_count = 0;
- gboolean record_method_count_for_max_collisions = FALSE;
- gboolean has_generic_virtual = FALSE, has_variant_iface = FALSE;
- #if DEBUG_IMT
- printf ("Building IMT for class %s.%s slot %d\n", klass->name_space, klass->name, slot_num);
- #endif
- for (i = 0; i < klass->interface_offsets_count; ++i) {
- MonoClass *iface = klass->interfaces_packed [i];
- int interface_offset = klass->interface_offsets_packed [i];
- int method_slot_in_interface, vt_slot;
- if (mono_class_has_variant_generic_params (iface))
- has_variant_iface = TRUE;
- vt_slot = interface_offset;
- for (method_slot_in_interface = 0; method_slot_in_interface < iface->method.count; method_slot_in_interface++) {
- MonoMethod *method;
- if (slot_num >= 0 && iface->is_inflated) {
- /*
- * The imt slot of the method is the same as for its declaring method,
- * see the comment in mono_method_get_imt_slot (), so we can
- * avoid inflating methods which will be discarded by
- * add_imt_builder_entry anyway.
- */
- method = mono_class_get_method_by_index (iface->generic_class->container_class, method_slot_in_interface);
- if (mono_method_get_imt_slot (method) != slot_num) {
- vt_slot ++;
- continue;
- }
- }
- method = mono_class_get_method_by_index (iface, method_slot_in_interface);
- if (method->is_generic) {
- has_generic_virtual = TRUE;
- vt_slot ++;
- continue;
- }
- if (!(method->flags & METHOD_ATTRIBUTE_STATIC)) {
- add_imt_builder_entry (imt_builder, method, &imt_collisions_bitmap, vt_slot, slot_num);
- vt_slot ++;
- }
- }
- }
- if (extra_interfaces) {
- int interface_offset = klass->vtable_size;
- for (list_item = extra_interfaces; list_item != NULL; list_item=list_item->next) {
- MonoClass* iface = list_item->data;
- int method_slot_in_interface;
- for (method_slot_in_interface = 0; method_slot_in_interface < iface->method.count; method_slot_in_interface++) {
- MonoMethod *method = mono_class_get_method_by_index (iface, method_slot_in_interface);
- add_imt_builder_entry (imt_builder, method, &imt_collisions_bitmap, interface_offset + method_slot_in_interface, slot_num);
- }
- interface_offset += iface->method.count;
- }
- }
- for (i = 0; i < MONO_IMT_SIZE; ++i) {
- /* overwrite the imt slot only if we're building all the entries or if
- * we're building this specific one
- */
- if (slot_num < 0 || i == slot_num) {
- MonoImtBuilderEntry *entries = get_generic_virtual_entries (domain, &imt [i]);
- if (entries) {
- if (imt_builder [i]) {
- MonoImtBuilderEntry *entry;
- /* Link entries with imt_builder [i] */
- for (entry = entries; entry->next; entry = entry->next) {
- #if DEBUG_IMT
- MonoMethod *method = (MonoMethod*)entry->key;
- char *method_name = mono_method_full_name (method, TRUE);
- printf ("Added extra entry for method (%p) %s: imt_slot = %d\n", method, method_name, i);
- g_free (method_name);
- #endif
- }
- entry->next = imt_builder [i];
- entries->children += imt_builder [i]->children + 1;
- }
- imt_builder [i] = entries;
- }
- if (has_generic_virtual || has_variant_iface) {
- /*
- * There might be collisions later when the the thunk is expanded.
- */
- imt_collisions_bitmap |= (1 << i);
- /*
- * The IMT thunk might be called with an instance of one of the
- * generic virtual methods, so has to fallback to the IMT trampoline.
- */
- imt [i] = initialize_imt_slot (vt, domain, imt_builder [i], callbacks.get_imt_trampoline ? callbacks.get_imt_trampoline (i) : NULL);
- } else {
- imt [i] = initialize_imt_slot (vt, domain, imt_builder [i], NULL);
- }
- #if DEBUG_IMT
- printf ("initialize_imt_slot[%d]: %p methods %d\n", i, imt [i], imt_builder [i]->children + 1);
- #endif
- }
- if (imt_builder [i] != NULL) {
- int methods_in_slot = imt_builder [i]->children + 1;
- if (methods_in_slot > mono_stats.imt_max_collisions_in_slot) {
- mono_stats.imt_max_collisions_in_slot = methods_in_slot;
- record_method_count_for_max_collisions = TRUE;
- }
- method_count += methods_in_slot;
- }
- }
-
- mono_stats.imt_number_of_methods += method_count;
- if (record_method_count_for_max_collisions) {
- mono_stats.imt_method_count_when_max_collisions = method_count;
- }
-
- for (i = 0; i < MONO_IMT_SIZE; i++) {
- MonoImtBuilderEntry* entry = imt_builder [i];
- while (entry != NULL) {
- MonoImtBuilderEntry* next = entry->next;
- g_free (entry);
- entry = next;
- }
- }
- free (imt_builder);
- /* we OR the bitmap since we may build just a single imt slot at a time */
- vt->imt_collisions_bitmap |= imt_collisions_bitmap;
- }
- static void
- build_imt (MonoClass *klass, MonoVTable *vt, MonoDomain *domain, gpointer* imt, GSList *extra_interfaces) {
- build_imt_slots (klass, vt, domain, imt, extra_interfaces, -1);
- }
- /**
- * mono_vtable_build_imt_slot:
- * @vtable: virtual object table struct
- * @imt_slot: slot in the IMT table
- *
- * Fill the given @imt_slot in the IMT table of @vtable with
- * a trampoline or a thunk for the case of collisions.
- * This is part of the internal mono API.
- *
- * LOCKING: Take the domain lock.
- */
- void
- mono_vtable_build_imt_slot (MonoVTable* vtable, int imt_slot)
- {
- gpointer *imt = (gpointer*)vtable;
- imt -= MONO_IMT_SIZE;
- g_assert (imt_slot >= 0 && imt_slot < MONO_IMT_SIZE);
- /* no support for extra interfaces: the proxy objects will need
- * to build the complete IMT
- * Update and heck needs to ahppen inside the proper domain lock, as all
- * the changes made to a MonoVTable.
- */
- mono_loader_lock (); /*FIXME build_imt_slots requires the loader lock.*/
- mono_domain_lock (vtable->domain);
- /* we change the slot only if it wasn't changed from the generic imt trampoline already */
- if (imt [imt_slot] == callbacks.get_imt_trampoline (imt_slot))
- build_imt_slots (vtable->klass, vtable, vtable->domain, imt, NULL, imt_slot);
- mono_domain_unlock (vtable->domain);
- mono_loader_unlock ();
- }
- /*
- * The first two free list entries both belong to the wait list: The
- * first entry is the pointer to the head of the list and the second
- * entry points to the last element. That way appending and removing
- * the first element are both O(1) operations.
- */
- #ifdef MONO_SMALL_CONFIG
- #define NUM_FREE_LISTS 6
- #else
- #define NUM_FREE_LISTS 12
- #endif
- #define FIRST_FREE_LIST_SIZE 64
- #define MAX_WAIT_LENGTH 50
- #define THUNK_THRESHOLD 10
- /*
- * LOCKING: The domain lock must be held.
- */
- static void
- init_thunk_free_lists (MonoDomain *domain)
- {
- if (domain->thunk_free_lists)
- return;
- domain->thunk_free_lists = mono_domain_alloc0 (domain, sizeof (gpointer) * NUM_FREE_LISTS);
- }
- static int
- list_index_for_size (int item_size)
- {
- int i = 2;
- int size = FIRST_FREE_LIST_SIZE;
- while (item_size > size && i < NUM_FREE_LISTS - 1) {
- i++;
- size <<= 1;
- }
- return i;
- }
- /**
- * mono_method_alloc_generic_virtual_thunk:
- * @domain: a domain
- * @size: size in bytes
- *
- * Allocs size bytes to be used for the code of a generic virtual
- * thunk. It's either allocated from the domain's code manager or
- * reused from a previously invalidated piece.
- *
- * LOCKING: The domain lock must be held.
- */
- gpointer
- mono_method_alloc_generic_virtual_thunk (MonoDomain *domain, int size)
- {
- static gboolean inited = FALSE;
- static int generic_virtual_thunks_size = 0;
- guint32 *p;
- int i;
- MonoThunkFreeList **l;
- init_thunk_free_lists (domain);
- size += sizeof (guint32);
- if (size < sizeof (MonoThunkFreeList))
- size = sizeof (MonoThunkFreeList);
- i = list_index_for_size (size);
- for (l = &domain->thunk_free_lists [i]; *l; l = &(*l)->next) {
- if ((*l)->size >= size) {
- MonoThunkFreeList *item = *l;
- *l = item->next;
- return ((guint32*)item) + 1;
- }
- }
- /* no suitable item found - search lists of larger sizes */
- while (++i < NUM_FREE_LISTS) {
- MonoThunkFreeList *item = domain->thunk_free_lists [i];
- if (!item)
- continue;
- g_assert (item->size > size);
- domain->thunk_free_lists [i] = item->next;
- return ((guint32*)item) + 1;
- }
- /* still nothing found - allocate it */
- if (!inited) {
- mono_counters_register ("Generic virtual thunk bytes",
- MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &generic_virtual_thunks_size);
- inited = TRUE;
- }
- generic_virtual_thunks_size += size;
- p = mono_domain_code_reserve (domain, size);
- *p = size;
- mono_domain_lock (domain);
- if (!domain->generic_virtual_thunks)
- domain->generic_virtual_thunks = g_hash_table_new (NULL, NULL);
- g_hash_table_insert (domain->generic_virtual_thunks, p, p);
- mono_domain_unlock (domain);
- return p + 1;
- }
- /*
- * LOCKING: The domain lock must be held.
- */
- static void
- invalidate_generic_virtual_thunk (MonoDomain *domain, gpointer code)
- {
- guint32 *p = code;
- MonoThunkFreeList *l = (MonoThunkFreeList*)(p - 1);
- gboolean found = FALSE;
- mono_domain_lock (domain);
- if (!domain->generic_virtual_thunks)
- domain->generic_virtual_thunks = g_hash_table_new (NULL, NULL);
- if (g_hash_table_lookup (domain->generic_virtual_thunks, l))
- found = TRUE;
- mono_domain_unlock (domain);
- if (!found)
- /* Not allocated by mono_method_alloc_generic_virtual_thunk (), i.e. AOT */
- return;
- init_thunk_free_lists (domain);
- while (domain->thunk_free_lists [0] && domain->thunk_free_lists [0]->length >= MAX_WAIT_LENGTH) {
- MonoThunkFreeList *item = domain->thunk_free_lists [0];
- int length = item->length;
- int i;
- /* unlink the first item from the wait list */
- domain->thunk_free_lists [0] = item->next;
- domain->thunk_free_lists [0]->length = length - 1;
- i = list_index_for_size (item->size);
- /* put it in the free list */
- item->next = domain->thunk_free_lists [i];
- domain->thunk_free_lists [i] = item;
- }
- l->next = NULL;
- if (domain->thunk_free_lists [1]) {
- domain->thunk_free_lists [1] = domain->thunk_free_lists [1]->next = l;
- domain->thunk_free_lists [0]->length++;
- } else {
- g_assert (!domain->thunk_free_lists [0]);
- domain->thunk_free_lists [0] = domain->thunk_free_lists [1] = l;
- domain->thunk_free_lists [0]->length = 1;
- }
- }
- typedef struct _GenericVirtualCase {
- MonoMethod *method;
- gpointer code;
- int count;
- struct _GenericVirtualCase *next;
- } GenericVirtualCase;
- /*
- * get_generic_virtual_entries:
- *
- * Return IMT entries for the generic virtual method instances and
- * variant interface methods for vtable slot
- * VTABLE_SLOT.
- */
- static MonoImtBuilderEntry*
- get_generic_virtual_entries (MonoDomain *domain, gpointer *vtable_slot)
- {
- GenericVirtualCase *list;
- MonoImtBuilderEntry *entries;
-
- mono_domain_lock (domain);
- if (!domain->generic_virtual_cases)
- domain->generic_virtual_cases = g_hash_table_new (mono_aligned_addr_hash, NULL);
-
- list = g_hash_table_lookup (domain->generic_virtual_cases, vtable_slot);
-
- entries = NULL;
- for (; list; list = list->next) {
- MonoImtBuilderEntry *entry;
-
- if (list->count < THUNK_THRESHOLD)
- continue;
-
- entry = g_new0 (MonoImtBuilderEntry, 1);
- entry->key = list->method;
- entry->v…
Large files files are truncated, but you can click here to view the full file