Permalink
Switch branches/tags
Find file
Fetching contributors…
Cannot retrieve contributors at this time
8570 lines (7376 sloc) 247 KB
/**
* \file
* 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)
* 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 <stdlib.h>
#include <stdio.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-internals.h>
#include <mono/metadata/exception.h>
#include <mono/metadata/exception-internals.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/marshal.h>
#include "mono/metadata/debug-helpers.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/verify-internals.h>
#include <mono/metadata/reflection-internals.h>
#include <mono/metadata/w32event.h>
#include <mono/metadata/custom-attrs-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 <mono/utils/checked-build.h>
#include <mono/utils/mono-threads.h>
#include <mono/utils/mono-threads-coop.h>
#include <mono/utils/mono-logger-internals.h>
#include "cominterop.h"
#include <mono/utils/w32api.h>
#include <mono/utils/unlocked.h>
static void
get_default_field_value (MonoDomain* domain, MonoClassField *field, void *value, MonoError *error);
static MonoString*
mono_ldstr_metadata_sig (MonoDomain *domain, const char* sig, MonoError *error);
static void
free_main_args (void);
static char *
mono_string_to_utf8_internal (MonoMemPool *mp, MonoImage *image, MonoString *s, MonoError *error);
static char *
mono_string_to_utf8_mp (MonoMemPool *mp, MonoString *s, MonoError *error);
static void
array_full_copy_unchecked_size (MonoArray *src, MonoArray *dest, MonoClass *klass, uintptr_t size);
static MonoMethod*
class_get_virtual_method (MonoClass *klass, MonoMethod *method, gboolean is_proxy, MonoError *error);
/* Class lazy loading functions */
static GENERATE_GET_CLASS_WITH_CACHE (pointer, "System.Reflection", "Pointer")
static GENERATE_GET_CLASS_WITH_CACHE (remoting_services, "System.Runtime.Remoting", "RemotingServices")
static GENERATE_GET_CLASS_WITH_CACHE (unhandled_exception_event_args, "System", "UnhandledExceptionEventArgs")
static GENERATE_GET_CLASS_WITH_CACHE (sta_thread_attribute, "System", "STAThreadAttribute")
static GENERATE_GET_CLASS_WITH_CACHE (activation_services, "System.Runtime.Remoting.Activation", "ActivationServices")
#define ldstr_lock() mono_os_mutex_lock (&ldstr_section)
#define ldstr_unlock() mono_os_mutex_unlock (&ldstr_section)
static mono_mutex_t ldstr_section;
/**
* mono_runtime_object_init:
* \param this_obj the object to initialize
* This function calls the zero-argument constructor (which must
* exist) for the given object.
*/
void
mono_runtime_object_init (MonoObject *this_obj)
{
ERROR_DECL (error);
mono_runtime_object_init_checked (this_obj, error);
mono_error_assert_ok (error);
}
/**
* mono_runtime_object_init_checked:
* \param this_obj the object to initialize
* \param error set on error.
* This function calls the zero-argument constructor (which must
* exist) for the given object and returns TRUE on success, or FALSE
* on error and sets \p error.
*/
gboolean
mono_runtime_object_init_checked (MonoObject *this_obj, MonoError *error)
{
MONO_REQ_GC_UNSAFE_MODE;
MonoMethod *method = NULL;
MonoClass *klass = this_obj->vtable->klass;
error_init (error);
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_obj = (MonoObject *)mono_object_unbox (this_obj);
mono_runtime_invoke_checked (method, this_obj, NULL, error);
return is_ok (error);
}
/* 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
{
MonoNativeThreadId initializing_tid;
guint32 waiting_count;
gboolean done;
MonoCoopMutex mutex;
/* condvar used to wait for 'done' becoming TRUE */
MonoCoopCond cond;
} TypeInitializationLock;
/* for locking access to type_initialization_hash and blocked_thread_hash */
static MonoCoopMutex type_initialization_section;
static inline void
mono_type_initialization_lock (void)
{
/* The critical sections protected by this lock in mono_runtime_class_init_full () can block */
mono_coop_mutex_lock (&type_initialization_section);
}
static inline void
mono_type_initialization_unlock (void)
{
mono_coop_mutex_unlock (&type_initialization_section);
}
static void
mono_type_init_lock (TypeInitializationLock *lock)
{
MONO_REQ_GC_NEUTRAL_MODE;
mono_coop_mutex_lock (&lock->mutex);
}
static void
mono_type_init_unlock (TypeInitializationLock *lock)
{
mono_coop_mutex_unlock (&lock->mutex);
}
/* 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:
* \param thread thread to set as the main thread
* This function can be used to instruct the runtime to treat \p thread
* as the main thread, ie, the thread that would normally execute the \c Main
* method. This basically means that at the end of \p thread, the runtime will
* wait for the existing foreground threads to quit and other such details.
*/
void
mono_thread_set_main (MonoThread *thread)
{
MONO_REQ_GC_UNSAFE_MODE;
static gboolean registered = FALSE;
if (!registered) {
void *key = thread->internal_thread ? (void *) MONO_UINT_TO_NATIVE_THREAD_ID (thread->internal_thread->tid) : NULL;
MONO_GC_REGISTER_ROOT_SINGLE (main_thread, MONO_ROOT_SOURCE_THREADING, key, "Thread Main Object");
registered = TRUE;
}
main_thread = thread;
}
/**
* mono_thread_get_main:
*/
MonoThread*
mono_thread_get_main (void)
{
MONO_REQ_GC_UNSAFE_MODE;
return main_thread;
}
void
mono_type_initialization_init (void)
{
mono_coop_mutex_init_recursive (&type_initialization_section);
type_initialization_hash = g_hash_table_new (NULL, NULL);
blocked_thread_hash = g_hash_table_new (NULL, NULL);
mono_os_mutex_init_recursive (&ldstr_section);
mono_register_jit_icall (ves_icall_string_alloc, "ves_icall_string_alloc", mono_create_icall_signature ("object int"), FALSE);
}
void
mono_type_initialization_cleanup (void)
{
#if 0
/* This is causing race conditions with
* mono_release_type_locks
*/
mono_coop_mutex_destroy (&type_initialization_section);
g_hash_table_destroy (type_initialization_hash);
type_initialization_hash = NULL;
#endif
mono_os_mutex_destroy (&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)
{
MONO_REQ_GC_UNSAFE_MODE;
ERROR_DECL (error);
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 = (MonoException *)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_checked (full_name, NULL, error);
g_free (full_name);
return_val_if_nok (error, NULL);
}
return ex;
}
/**
* mono_runtime_class_init:
* \param vtable vtable that needs to be initialized
* This routine calls the class constructor for \p vtable.
*/
void
mono_runtime_class_init (MonoVTable *vtable)
{
MONO_REQ_GC_UNSAFE_MODE;
ERROR_DECL (error);
mono_runtime_class_init_full (vtable, error);
mono_error_assert_ok (error);
}
/*
* Returns TRUE if the lock was freed.
* LOCKING: Caller should hold type_initialization_lock.
*/
static gboolean
unref_type_lock (TypeInitializationLock *lock)
{
--lock->waiting_count;
if (lock->waiting_count == 0) {
mono_coop_mutex_destroy (&lock->mutex);
mono_coop_cond_destroy (&lock->cond);
g_free (lock);
return TRUE;
} else {
return FALSE;
}
}
/**
* mono_runtime_class_init_full:
* \param vtable that neeeds to be initialized
* \param error set on error
* \returns TRUE if class constructor \c .cctor has been initialized successfully, or FALSE otherwise and sets \p error.
*/
gboolean
mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
{
MONO_REQ_GC_UNSAFE_MODE;
MonoMethod *method = NULL;
MonoClass *klass;
gchar *full_name;
MonoDomain *domain = vtable->domain;
TypeInitializationLock *lock;
MonoNativeThreadId tid;
int do_initialization = 0;
MonoDomain *last_domain = NULL;
gboolean pending_tae = FALSE;
error_init (error);
if (vtable->initialized)
return TRUE;
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;
MonoVTable *module_vtable;
module_klass = mono_class_get_checked (klass->image, MONO_TOKEN_TYPE_DEF | 1, error);
if (!module_klass) {
return FALSE;
}
module_vtable = mono_class_vtable_checked (vtable->domain, module_klass, error);
if (!module_vtable)
return FALSE;
if (!mono_runtime_class_init_full (module_vtable, error))
return FALSE;
}
}
method = mono_class_get_cctor (klass);
if (!method) {
vtable->initialized = 1;
return TRUE;
}
tid = mono_native_thread_id_get ();
/*
* Due some preprocessing inside a global lock. If we are the first thread
* trying to initialize this class, create a separate lock+cond var, and
* acquire it before leaving the global lock. The other threads will wait
* on this cond var.
*/
mono_type_initialization_lock ();
/* double check... */
if (vtable->initialized) {
mono_type_initialization_unlock ();
return TRUE;
}
if (vtable->init_failed) {
mono_type_initialization_unlock ();
/* The type initialization already failed once, rethrow the same exception */
mono_error_set_exception_instance (error, get_type_init_exception_for_vtable (vtable));
return FALSE;
}
lock = (TypeInitializationLock *)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 ();
mono_error_set_exception_instance (error, mono_get_exception_appdomain_unloaded ());
return FALSE;
}
}
lock = (TypeInitializationLock *)g_malloc0 (sizeof (TypeInitializationLock));
mono_coop_mutex_init_recursive (&lock->mutex);
mono_coop_cond_init (&lock->cond);
lock->initializing_tid = tid;
lock->waiting_count = 1;
lock->done = FALSE;
g_hash_table_insert (type_initialization_hash, vtable, lock);
do_initialization = 1;
} else {
gpointer blocked;
TypeInitializationLock *pending_lock;
if (mono_native_thread_id_equals (lock->initializing_tid, tid)) {
mono_type_initialization_unlock ();
return TRUE;
}
/* see if the thread doing the initialization is already blocked on this thread */
gboolean is_blocked = TRUE;
blocked = GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (lock->initializing_tid));
while ((pending_lock = (TypeInitializationLock*) g_hash_table_lookup (blocked_thread_hash, blocked))) {
if (mono_native_thread_id_equals (pending_lock->initializing_tid, tid)) {
if (!pending_lock->done) {
mono_type_initialization_unlock ();
return TRUE;
} 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 */
is_blocked = FALSE;
break;
}
}
blocked = GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (pending_lock->initializing_tid));
}
++lock->waiting_count;
/* record the fact that we are waiting on the initializing thread */
if (is_blocked)
g_hash_table_insert (blocked_thread_hash, GUINT_TO_POINTER (tid), lock);
}
mono_type_initialization_unlock ();
if (do_initialization) {
MonoException *exc = NULL;
/* We are holding the per-vtable lock, do the actual initialization */
mono_threads_begin_abort_protected_block ();
mono_runtime_try_invoke (method, NULL, NULL, (MonoObject**) &exc, error);
mono_threads_end_abort_protected_block ();
//exception extracted, error will be set to the right value later
if (exc == NULL && !mono_error_ok (error))//invoking failed but exc was not set
exc = mono_error_convert_to_exception (error);
else
mono_error_cleanup (error);
error_init (error);
/* If the initialization failed, mark the class as unusable. */
/* Avoid infinite loops */
if (!(!exc ||
(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);
MonoException *exc_to_throw = mono_get_exception_type_initialization_checked (full_name, exc, error);
g_free (full_name);
mono_error_assert_ok (error); //We can't recover from this, no way to fail a type we can't alloc a failure.
/*
* 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_ROOT_SOURCE_DOMAIN, domain, "Domain Type Initialization Exception Table");
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);
/* Signal to the other threads that we are done */
mono_type_init_lock (lock);
lock->done = TRUE;
mono_coop_cond_broadcast (&lock->cond);
mono_type_init_unlock (lock);
/*
* This can happen if the cctor self-aborts. We need to reactivate tae
* (next interruption checkpoint will throw it) and make sure we won't
* throw tie for the type.
*/
if (exc && mono_object_class (exc) == mono_defaults.threadabortexception_class) {
pending_tae = TRUE;
mono_thread_resume_interruption (FALSE);
}
} else {
/* this just blocks until the initializing thread is done */
mono_type_init_lock (lock);
while (!lock->done)
mono_coop_cond_wait (&lock->cond, &lock->mutex);
mono_type_init_unlock (lock);
}
/* Do cleanup and setting vtable->initialized inside the global lock again */
mono_type_initialization_lock ();
if (!do_initialization)
g_hash_table_remove (blocked_thread_hash, GUINT_TO_POINTER (tid));
gboolean deleted = unref_type_lock (lock);
if (deleted)
g_hash_table_remove (type_initialization_hash, vtable);
/* Have to set this here since we check it inside the global lock */
if (do_initialization && !vtable->init_failed)
vtable->initialized = 1;
mono_type_initialization_unlock ();
/* If vtable init fails because of TAE, we don't throw TIE, only the TAE */
if (vtable->init_failed && !pending_tae) {
/* Either we were the initializing thread or we waited for the initialization */
mono_error_set_exception_instance (error, get_type_init_exception_for_vtable (vtable));
return FALSE;
}
return TRUE;
}
MonoDomain *
mono_vtable_domain (MonoVTable *vtable)
{
return vtable->domain;
}
MonoClass *
mono_vtable_class (MonoVTable *vtable)
{
return vtable->klass;
}
static
gboolean release_type_locks (gpointer key, gpointer value, gpointer user)
{
MONO_REQ_GC_NEUTRAL_MODE;
MonoVTable *vtable = (MonoVTable*)key;
TypeInitializationLock *lock = (TypeInitializationLock*) value;
if (mono_native_thread_id_equals (lock->initializing_tid, MONO_UINT_TO_NATIVE_THREAD_ID (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.
*/
mono_type_init_lock (lock);
vtable->init_failed = 1;
mono_coop_cond_broadcast (&lock->cond);
mono_type_init_unlock (lock);
gboolean deleted = unref_type_lock (lock);
if (deleted)
return TRUE;
}
return FALSE;
}
void
mono_release_type_locks (MonoInternalThread *thread)
{
MONO_REQ_GC_UNSAFE_MODE;
mono_type_initialization_lock ();
g_hash_table_foreach_remove (type_initialization_hash, release_type_locks, GUINT_TO_POINTER (thread->tid));
mono_type_initialization_unlock ();
}
#ifndef DISABLE_REMOTING
static gpointer
create_remoting_trampoline (MonoDomain *domain, MonoMethod *method, MonoRemotingTarget target, MonoError *error)
{
if (!callbacks.create_remoting_trampoline)
g_error ("remoting not installed");
return callbacks.create_remoting_trampoline (domain, method, target, error);
}
#endif
static MonoImtTrampolineBuilder imt_trampoline_builder;
static gboolean always_build_imt_trampolines;
#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_imt_trampoline_builder (MonoImtTrampolineBuilder func)
{
imt_trampoline_builder = func;
}
void
mono_set_always_build_imt_trampolines (gboolean value)
{
always_build_imt_trampolines = value;
}
/**
* mono_compile_method:
* \param 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)
{
ERROR_DECL (error);
gpointer result = mono_compile_method_checked (method, error);
mono_error_cleanup (error);
return result;
}
/**
* mono_compile_method_checked:
* \param method The method to compile.
* \param error set on error.
* This JIT-compiles the method, and returns the pointer to the native code
* produced. On failure returns NULL and sets \p error.
*/
gpointer
mono_compile_method_checked (MonoMethod *method, MonoError *error)
{
gpointer res;
MONO_REQ_GC_NEUTRAL_MODE
error_init (error);
g_assert (callbacks.compile_method);
res = callbacks.compile_method (method, error);
return res;
}
gpointer
mono_runtime_create_jump_trampoline (MonoDomain *domain, MonoMethod *method, gboolean add_sync_wrapper, MonoError *error)
{
gpointer res;
MONO_REQ_GC_NEUTRAL_MODE;
error_init (error);
res = callbacks.create_jump_trampoline (domain, method, add_sync_wrapper, error);
return res;
}
gpointer
mono_runtime_create_delegate_trampoline (MonoClass *klass)
{
MONO_REQ_GC_NEUTRAL_MODE
g_assert (callbacks.create_delegate_trampoline);
return callbacks.create_delegate_trampoline (mono_domain_get (), klass);
}
/**
* mono_runtime_free_method:
* \param domain domain where the method is hosted
* \param 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)
{
MONO_REQ_GC_NEUTRAL_MODE
if (callbacks.free_method)
callbacks.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 *klass, gsize *bitmap, int size, int offset, int *max_set, gboolean static_fields)
{
MONO_REQ_GC_NEUTRAL_MODE;
MonoClassField *field;
MonoClass *p;
guint32 pos;
int max_size;
if (static_fields)
max_size = mono_class_data_size (klass) / sizeof (gpointer);
else
max_size = klass->instance_size / sizeof (gpointer);
if (max_size > size) {
g_assert (offset <= 0);
bitmap = (gsize *)g_malloc0 ((max_size + BITMAP_EL_SIZE - 1) / BITMAP_EL_SIZE * sizeof (gsize));
size = max_size;
}
/* An Ephemeron cannot be marked by sgen */
if (mono_gc_is_moving () && !static_fields && klass->image == mono_defaults.corlib && !strcmp ("Ephemeron", klass->name)) {
*max_set = 0;
memset (bitmap, 0, size / 8);
return bitmap;
}
for (p = klass; 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_U:
case MONO_TYPE_I:
case MONO_TYPE_PTR:
case MONO_TYPE_FNPTR:
break;
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 *klass, gsize *bitmap, int size, int offset, int *max_set, gboolean static_fields)
{
MONO_REQ_GC_NEUTRAL_MODE;
return compute_class_bitmap (klass, 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 *klass, 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*
ves_icall_string_alloc (int length)
{
ERROR_DECL (error);
MonoString *str = mono_string_new_size_checked (mono_domain_get (), length, error);
mono_error_set_pending_exception (error);
return str;
}
#define BITMAP_EL_SIZE (sizeof (gsize) * 8)
/* LOCKING: Acquires the loader lock */
/*
* Sets the following fields in KLASS:
* - gc_desc
* - gc_descr_inited
*/
void
mono_class_compute_gc_descriptor (MonoClass *klass)
{
MONO_REQ_GC_NEUTRAL_MODE;
int max_set = 0;
gsize *bitmap;
gsize default_bitmap [4] = {0};
MonoGCDescriptor gc_descr;
if (!klass->inited)
mono_class_init (klass);
if (klass->gc_descr_inited)
return;
bitmap = default_bitmap;
if (klass == mono_defaults.string_class) {
gc_descr = mono_gc_make_descr_for_string (bitmap, 2);
} else if (klass->rank) {
mono_class_compute_gc_descriptor (klass->element_class);
if (MONO_TYPE_IS_REFERENCE (&klass->element_class->byval_arg)) {
gsize abm = 1;
gc_descr = mono_gc_make_descr_for_array (klass->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 (klass->element_class, default_bitmap, sizeof (default_bitmap) * 8, - (int)(sizeof (MonoObject) / sizeof (gpointer)), &max_set, FALSE);
gc_descr = mono_gc_make_descr_for_array (klass->byval_arg.type == MONO_TYPE_SZARRAY, bitmap, mono_array_element_size (klass) / sizeof (gpointer), mono_array_element_size (klass));
/*printf ("new vt array descriptor: 0x%x for %s.%s\n", class->gc_descr,
class->name_space, class->name);*/
}
} else {
/*static int count = 0;
if (count++ > 58)
return;*/
bitmap = compute_class_bitmap (klass, default_bitmap, sizeof (default_bitmap) * 8, 0, &max_set, FALSE);
/*
if (class->gc_descr == MONO_GC_DESCRIPTOR_NULL)
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 (klass->has_weak_fields) {
gsize *weak_bitmap = NULL;
int weak_bitmap_nbits = 0;
weak_bitmap = (gsize *)mono_class_alloc0 (klass, klass->instance_size / sizeof (gsize));
if (mono_class_has_static_metadata (klass)) {
for (MonoClass *p = klass; p != NULL; p = p->parent) {
gpointer iter = NULL;
guint32 first_field_idx = mono_class_get_first_field_idx (p);
MonoClassField *field;
while ((field = mono_class_get_fields (p, &iter))) {
guint32 field_idx = first_field_idx + (field - p->fields);
if (MONO_TYPE_IS_REFERENCE (field->type) && mono_assembly_is_weak_field (p->image, field_idx + 1)) {
int pos = field->offset / sizeof (gpointer);
if (pos + 1 > weak_bitmap_nbits)
weak_bitmap_nbits = pos + 1;
weak_bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE);
}
}
}
}
for (int pos = 0; pos < weak_bitmap_nbits; ++pos) {
if (weak_bitmap [pos / BITMAP_EL_SIZE] & ((gsize)1) << (pos % BITMAP_EL_SIZE)) {
/* Clear the normal bitmap so these refs don't keep an object alive */
bitmap [pos / BITMAP_EL_SIZE] &= ~((gsize)1) << (pos % BITMAP_EL_SIZE);
}
}
mono_loader_lock ();
mono_class_set_weak_bitmap (klass, weak_bitmap_nbits, weak_bitmap);
mono_loader_unlock ();
}
gc_descr = mono_gc_make_descr_for_object (bitmap, max_set + 1, klass->instance_size);
}
if (bitmap != default_bitmap)
g_free (bitmap);
/* Publish the data */
mono_loader_lock ();
klass->gc_descr = gc_descr;
mono_memory_barrier ();
klass->gc_descr_inited = TRUE;
mono_loader_unlock ();
}
/**
* 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)
{
MONO_REQ_GC_NEUTRAL_MODE;
ERROR_DECL (error);
MonoCustomAttrInfo *ainfo;
int i;
ainfo = mono_custom_attrs_from_field_checked (fklass, field, error);
mono_error_cleanup (error); /* FIXME don't swallow the error? */
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)
{
MONO_REQ_GC_NEUTRAL_MODE;
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 = (guint32 *)g_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;
}
g_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) {
MONO_REQ_GC_NEUTRAL_MODE;
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 = (MonoImtBuilderEntry *)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) {
UnlockedIncrement (&mono_stats.imt_slots_with_collisions);
*imt_collisions_bitmap |= (1 << imt_slot);
}
} else {
entry->children = 0;
UnlockedIncrement (&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)
{
MONO_REQ_GC_NEUTRAL_MODE;
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) {
MONO_REQ_GC_NEUTRAL_MODE;
int number_of_entries = entries->children + 1;
MonoImtBuilderEntry **sorted_array = (MonoImtBuilderEntry **)g_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);
g_free (sorted_array);
return result;
}
static gpointer
initialize_imt_slot (MonoVTable *vtable, MonoDomain *domain, MonoImtBuilderEntry *imt_builder_entry, gpointer fail_tramp)
{
MONO_REQ_GC_NEUTRAL_MODE;
if (imt_builder_entry != NULL) {
if (imt_builder_entry->children == 0 && !fail_tramp && !always_build_imt_trampolines) {
/* No collision, return the vtable slot contents */
return vtable->vtable [imt_builder_entry->value.vtable_slot];
} else {
/* Collision, build the trampoline */
GPtrArray *imt_ir = imt_sort_slot_entries (imt_builder_entry);
gpointer result;
int i;
result = imt_trampoline_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)
{
MONO_REQ_GC_NEUTRAL_MODE;
int i;
GSList *list_item;
guint32 imt_collisions_bitmap = 0;
MonoImtBuilderEntry **imt_builder = (MonoImtBuilderEntry **)g_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;
mono_class_setup_methods (iface);
vt_slot = interface_offset;
int mcount = mono_class_get_method_count (iface);
for (method_slot_in_interface = 0; method_slot_in_interface < mcount; method_slot_in_interface++) {
MonoMethod *method;
if (slot_num >= 0 && mono_class_is_ginst (iface)) {
/*
* 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 (mono_class_get_generic_class (iface)->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 = (MonoClass *)list_item->data;
int method_slot_in_interface;
int mcount = mono_class_get_method_count (iface);
for (method_slot_in_interface = 0; method_slot_in_interface < mcount; method_slot_in_interface++) {
MonoMethod *method = mono_class_get_method_by_index (iface, method_slot_in_interface);
if (method->is_generic)
has_generic_virtual = TRUE;
add_imt_builder_entry (imt_builder, method, &imt_collisions_bitmap, interface_offset + method_slot_in_interface, slot_num);
}
interface_offset += mcount;
}
}
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 trampoline is expanded.
*/
imt_collisions_bitmap |= (1 << i);
/*
* The IMT trampoline 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 (vt, i));
} 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 > UnlockedRead (&mono_stats.imt_max_collisions_in_slot)) {
UnlockedWrite (&mono_stats.imt_max_collisions_in_slot, methods_in_slot);
record_method_count_for_max_collisions = TRUE;
}
method_count += methods_in_slot;
}
}
UnlockedAdd (&mono_stats.imt_number_of_methods, method_count);
if (record_method_count_for_max_collisions) {
UnlockedWrite (&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;
}
}
g_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) {
MONO_REQ_GC_NEUTRAL_MODE;
build_imt_slots (klass, vt, domain, imt, extra_interfaces, -1);
}
/**
* mono_vtable_build_imt_slot:
* \param vtable virtual object table struct
* \param imt_slot slot in the IMT table
* Fill the given \p imt_slot in the IMT table of \p vtable with
* a trampoline or a trampoline 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)
{
MONO_REQ_GC_NEUTRAL_MODE;
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 (!callbacks.imt_entry_inited (vtable, imt_slot))
build_imt_slots (vtable->klass, vtable, vtable->domain, imt, NULL, imt_slot);
mono_domain_unlock (vtable->domain);
mono_loader_unlock ();
}
#define THUNK_THRESHOLD 10
/**
* mono_method_alloc_generic_virtual_trampoline:
* \param domain a domain
* \param size size in bytes
* Allocs \p size bytes to be used for the code of a generic virtual
* trampoline. 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_trampoline (MonoDomain *domain, int size)
{
MONO_REQ_GC_NEUTRAL_MODE;
static gboolean inited = FALSE;
static int generic_virtual_trampolines_size = 0;
if (!inited) {
mono_counters_register ("Generic virtual trampoline bytes",
MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &generic_virtual_trampolines_size);
inited = TRUE;
}
generic_virtual_trampolines_size += size;
return mono_domain_code_reserve (domain, size);
}
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)
{
MONO_REQ_GC_NEUTRAL_MODE;
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 = (GenericVirtualCase *)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->value.target_code = mono_get_addr_from_ftnptr (list->code);
entry->has_target_code = 1;
if (entries)
entry->children = entries->children + 1;
entry->next = entries;
entries = entry;
}
mono_domain_unlock (domain);
/* FIXME: Leaking memory ? */
return entries;
}
/**
* \param domain a domain
* \param vtable_slot pointer to the vtable slot
* \param method the inflated generic virtual method
* \param code the method's code
*
* Registers a call via unmanaged code to a generic virtual method
* instantiation or variant interface method. If the number of calls reaches a threshold
* (THUNK_THRESHOLD), the method is added to the vtable slot's generic
* virtual method trampoline.
*/
void
mono_method_add_generic_virtual_invocation (MonoDomain *domain, MonoVTable *vtable,
gpointer *vtable_slot,
MonoMethod *method, gpointer code)
{
MONO_REQ_GC_NEUTRAL_MODE;
static gboolean inited = FALSE;
static int num_added = 0;
static int num_freed = 0;
GenericVirtualCase *gvc, *list;
MonoImtBuilderEntry *entries;
int i;
GPtrArray *sorted;
mono_domain_lock (domain);
if (!domain->generic_virtual_cases)
domain->generic_virtual_cases = g_hash_table_new (mono_aligned_addr_hash, NULL);
if (!inited) {
mono_counters_register ("Generic virtual cases", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_added);
mono_counters_register ("Freed IMT trampolines", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_freed);
inited = TRUE;
}
/* Check whether the case was already added */
list = (GenericVirtualCase *)g_hash_table_lookup (domain->generic_virtual_cases, vtable_slot);
gvc = list;
while (gvc) {
if (gvc->method == method)
break;
gvc = gvc->next;
}
/* If not found, make a new one */
if (!gvc) {
gvc = (GenericVirtualCase *)mono_domain_alloc (domain, sizeof (GenericVirtualCase));
gvc->method = method;
gvc->code = code;
gvc->count = 0;
gvc->next = (GenericVirtualCase *)g_hash_table_lookup (domain->generic_virtual_cases, vtable_slot);
g_hash_table_insert (domain->generic_virtual_cases, vtable_slot, gvc);
num_added++;
}
if (++gvc->count == THUNK_THRESHOLD) {
gpointer *old_thunk = (void **)*vtable_slot;
gpointer vtable_trampoline = NULL;
gpointer imt_trampoline = NULL;
if ((gpointer)vtable_slot < (gpointer)vtable) {
int displacement = (gpointer*)vtable_slot - (gpointer*)vtable;
int imt_slot = MONO_IMT_SIZE + displacement;
/* Force the rebuild of the trampoline at the next call */
imt_trampoline = callbacks.get_imt_trampoline (vtable, imt_slot);
*vtable_slot = imt_trampoline;
} else {
vtable_trampoline = callbacks.get_vtable_trampoline ? callbacks.get_vtable_trampoline (vtable, (gpointer*)vtable_slot - (gpointer*)vtable->vtable) : NULL;
entries = get_generic_virtual_entries (domain, vtable_slot);
sorted = imt_sort_slot_entries (entries);
*vtable_slot = imt_trampoline_builder (NULL, domain, (MonoIMTCheckItem**)sorted->pdata, sorted->len,
vtable_trampoline);
while (entries) {
MonoImtBuilderEntry *next = entries->next;
g_free (entries);
entries = next;
}
for (i = 0; i < sorted->len; ++i)
g_free (g_ptr_array_index (sorted, i));
g_ptr_array_free (sorted, TRUE);
if (old_thunk != vtable_trampoline && old_thunk != imt_trampoline)
num_freed ++;
}
}
mono_domain_unlock (domain);
}
static MonoVTable *mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *klass, MonoError *error);
/**
* mono_class_vtable:
* \param domain the application domain
* \param class the class to initialize
* VTables are domain specific because we create domain specific code, and
* they contain the domain specific static class data.
* On failure, NULL is returned, and \c class->exception_type is set.
*/
MonoVTable *
mono_class_vtable (MonoDomain *domain, MonoClass *klass)
{
ERROR_DECL (error);
MonoVTable* vtable = mono_class_vtable_checked (domain, klass, error);
mono_error_cleanup (error);
return vtable;
}
/**
* mono_class_vtable_checked:
* \param domain the application domain
* \param class the class to initialize
* \param error set on failure.
* VTables are domain specific because we create domain specific code, and
* they contain the domain specific static class data.
*/
MonoVTable *
mono_class_vtable_checked (MonoDomain *domain, MonoClass *klass, MonoError *error)
{
MONO_REQ_GC_UNSAFE_MODE;
MonoClassRuntimeInfo *runtime_info;
error_init (error);
g_assert (klass);
if (mono_class_has_failure (klass)) {
mono_error_set_for_class_failure (error, klass);
return NULL;
}
/* this check can be inlined in jitted code, too */
runtime_info = klass->runtime_info;
if (runtime_info && runtime_info->max_domain >= domain->domain_id && runtime_info->domain_vtables [domain->domain_id])
return runtime_info->domain_vtables [domain->domain_id];
return mono_class_create_runtime_vtable (domain, klass, error);
}
/**
* mono_class_try_get_vtable:
* \param domain the application domain
* \param class the class to initialize
* This function tries to get the associated vtable from \p class if
* it was already created.
*/
MonoVTable *
mono_class_try_get_vtable (MonoDomain *domain, MonoClass *klass)
{
MONO_REQ_GC_NEUTRAL_MODE;
MonoClassRuntimeInfo *runtime_info;
g_assert (klass);
runtime_info = klass->runtime_info;
if (runtime_info && runtime_info->max_domain >= domain->domain_id && runtime_info->domain_vtables [domain->domain_id])
return runtime_info->domain_vtables [domain->domain_id];
return NULL;
}
static gpointer*
alloc_vtable (MonoDomain *domain, size_t vtable_size, size_t imt_table_bytes)
{
MONO_REQ_GC_NEUTRAL_MODE;
size_t alloc_offset;
/*
* We want the pointer to the MonoVTable aligned to 8 bytes because SGen uses three
* address bits. The IMT has an odd number of entries, however, so on 32 bits the
* alignment will be off. In that case we allocate 4 more bytes and skip over them.
*/
if (sizeof (gpointer) == 4 && (imt_table_bytes & 7)) {
g_assert ((imt_table_bytes & 7) == 4);
vtable_size += 4;
alloc_offset = 4;
} else {
alloc_offset = 0;
}
return (gpointer*) ((char*)mono_domain_alloc0 (domain, vtable_size) + alloc_offset);
}
static MonoVTable *
mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *klass, MonoError *error)
{
MONO_REQ_GC_UNSAFE_MODE;
MonoVTable *vt;
MonoClassRuntimeInfo *runtime_info, *old_info;
MonoClassField *field;
char *t;
int i, vtable_slots;
size_t imt_table_bytes;
int gc_bits;
guint32 vtable_size, class_size;
gpointer iter;
gpointer *interface_offsets;
error_init (error);
mono_loader_lock (); /*FIXME mono_class_init acquires it*/
mono_domain_lock (domain);
runtime_info = klass->runtime_info;
if (runtime_info && runtime_info->max_domain >= domain->domain_id && runtime_info->domain_vtables [domain->domain_id]) {
mono_domain_unlock (domain);
mono_loader_unlock ();
return runtime_info->domain_vtables [domain->domain_id];
}
if (!klass->inited || mono_class_has_failure (klass)) {
if (!mono_class_init (klass) || mono_class_has_failure (klass)) {
mono_domain_unlock (domain);
mono_loader_unlock ();
mono_error_set_for_class_failure (error, klass);
return NULL;
}
}
/* Array types require that their element type be valid*/
if (klass->byval_arg.type == MONO_TYPE_ARRAY || klass->byval_arg.type == MONO_TYPE_SZARRAY) {
MonoClass *element_class = klass->element_class;
if (!element_class->inited)
mono_class_init (element_class);
/*mono_class_init can leave the vtable layout to be lazily done and we can't afford this here*/
if (!mono_class_has_failure (element_class) && !element_class->vtable_size)
mono_class_setup_vtable (element_class);
if (mono_class_has_failure (element_class)) {
/*Can happen if element_class only got bad after mono_class_setup_vtable*/
if (!mono_class_has_failure (klass))
mono_class_set_type_load_failure (klass, "");
mono_domain_unlock (domain);
mono_loader_unlock ();
mono_error_set_for_class_failure (error, klass);
return NULL;
}
}
/*
* For some classes, mono_class_init () already computed klass->vtable_size, and
* that is all that is needed because of the vtable trampolines.
*/
if (!klass->vtable_size)
mono_class_setup_vtable (klass);
if (mono_class_is_ginst (klass) && !klass->vtable)
mono_class_check_vtable_constraints (klass, NULL);
/* Initialize klass->has_finalize */
mono_class_has_finalizer (klass);
if (mono_class_has_failure (klass)) {
mono_domain_unlock (domain);
mono_loader_unlock ();
mono_error_set_for_class_failure (error, klass);
return NULL;
}
vtable_slots = klass->vtable_size;
/* we add an additional vtable slot to store the pointer to static field data only when needed */
class_size = mono_class_data_size (klass);
if (class_size)
vtable_slots++;
if (klass->interface_offsets_count) {
imt_table_bytes = sizeof (gpointer) * (MONO_IMT_SIZE);
UnlockedIncrement (&mono_stats.imt_number_of_tables);
UnlockedAdd (&mono_stats.imt_tables_size, imt_table_bytes);
} else {
imt_table_bytes = 0;
}
vtable_size = imt_table_bytes + MONO_SIZEOF_VTABLE + vtable_slots * sizeof (gpointer);
UnlockedIncrement (&mono_stats.used_class_count);
UnlockedAdd (&mono_stats.class_vtable_size, vtable_size);
interface_offsets = alloc_vtable (domain, vtable_size, imt_table_bytes);
vt = (MonoVTable*) ((char*)interface_offsets + imt_table_bytes);
g_assert (!((gsize)vt & 7));
vt->klass = klass;
vt->rank = klass->rank;
vt->domain = domain;
MONO_PROFILER_RAISE (vtable_loading, (vt));
mono_class_compute_gc_descriptor (klass);
/*
* For Boehm:
* We can't use typed allocation in the non-root domains, since the
* collector needs the GC descriptor stored in the vtable even after
* the mempool containing the vtable is destroyed when the domain is
* unloaded. An alternative might be to allocate vtables in the GC
* heap, but this does not seem to work (it leads to crashes inside
* libgc). If that approach is tried, two gc descriptors need to be
* allocated for each class: one for the root domain, and one for all
* other domains. The second descriptor should contain a bit for the
* vtable field in MonoObject, since we can no longer assume the
* vtable is reachable by other roots after the appdomain is unloaded.
*/
if (!mono_gc_is_moving () && domain != mono_get_root_domain () && !mono_dont_free_domains)
vt->gc_descr = MONO_GC_DESCRIPTOR_NULL;
else
vt->gc_descr = klass->gc_descr;
gc_bits = mono_gc_get_vtable_bits (klass);
g_assert (!(gc_bits & ~((1 << MONO_VTABLE_AVAILABLE_GC_BITS) - 1)));
vt->gc_bits = gc_bits;
if (class_size) {
/* we store the static field pointer at the end of the vtable: vt->vtable [class->vtable_size] */
if (klass->has_static_refs) {
MonoGCDescriptor statics_gc_descr;
int max_set = 0;
gsize default_bitmap [4] = {0};
gsize *bitmap;
bitmap = compute_class_bitmap (klass, default_bitmap, sizeof (default_bitmap) * 8, 0, &max_set, TRUE);
/*g_print ("bitmap 0x%x for %s.%s (size: %d)\n", bitmap [0], klass->name_space, klass->name, class_size);*/
statics_gc_descr = mono_gc_make_descr_from_bitmap (bitmap, max_set + 1);
vt->vtable [klass->vtable_size] = mono_gc_alloc_fixed (class_size, statics_gc_descr, MONO_ROOT_SOURCE_STATIC, vt, "Static Fields");
if (bitmap != default_bitmap)
g_free (bitmap);
} else {
vt->vtable [klass->vtable_size] = mono_domain_alloc0 (domain, class_size);
}
vt->has_static_fields = TRUE;
UnlockedAdd (&mono_stats.class_static_data_size, class_size);
}
iter = NULL;
while ((field = mono_class_get_fields (klass, &iter))) {
if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
continue;
if (mono_field_is_deleted (field))
continue;
if (!(field->type->attrs & FIELD_ATTRIBUTE_LITERAL)) {
gint32 special_static = klass->no_special_static_fields ? SPECIAL_STATIC_NONE : field_is_special_static (klass, field);
if (special_static != SPECIAL_STATIC_NONE) {
guint32 size, offset;
gint32 align;
gsize default_bitmap [4] = {0};
gsize *bitmap;
int max_set = 0;
int numbits;
MonoClass *fclass;
if (mono_type_is_reference (field->type)) {
default_bitmap [0] = 1;
numbits = 1;
bitmap = default_bitmap;
} else if (mono_type_is_struct (field->type)) {
fclass = mono_class_from_mono_type (field->type);
bitmap = compute_class_bitmap (fclass, default_bitmap, sizeof (default_bitmap) * 8, - (int)(sizeof (MonoObject) / sizeof (gpointer)), &max_set, FALSE);
numbits = max_set + 1;
} else {
default_bitmap [0] = 0;
numbits = 0;
bitmap = default_bitmap;
}
size = mono_type_size (field->type, &align);
offset = mono_alloc_special_static_data (special_static, size, align, (uintptr_t*)bitmap, numbits);
if (!domain->special_static_fields)
domain->special_static_fields = g_hash_table_new (NULL, NULL);
g_hash_table_insert (domain->special_static_fields, field, GUINT_TO_POINTER (offset));
if (bitmap != default_bitmap)
g_free (bitmap);
/*
* This marks the field as special static to speed up the
* checks in mono_field_static_get/set_value ().
*/
field->offset = -1;
continue;
}
}
if ((field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA)) {
MonoClass *fklass = mono_class_from_mono_type (field->type);
const char *data = mono_field_get_data (field);
g_assert (!(field->type->attrs & FIELD_ATTRIBUTE_HAS_DEFAULT));
t = (char*)mono_vtable_get_static_field_data (vt) + field->offset;
/* some fields don't really have rva, they are just zeroed (bss? bug #343083) */
if (!data)
continue;
if (fklass->valuetype) {
memcpy (t, data, mono_class_value_size (fklass, NULL));
} else {
/* it's a pointer type: add check */
g_assert ((fklass->byval_arg.type == MONO_TYPE_PTR) || (fklass->byval_arg.type == MONO_TYPE_FNPTR));
*t = *(char *)data;
}
continue;
}
}
vt->max_interface_id = klass->max_interface_id;
vt->interface_bitmap = klass->interface_bitmap;
//printf ("Initializing VT for class %s (interface_offsets_count = %d)\n",
// class->name, klass->interface_offsets_count);
/* Initialize vtable */
if (callbacks.get_vtable_trampoline) {
// This also covers the AOT case
for (i = 0; i < klass->vtable_size; ++i) {
vt->vtable [i] = callbacks.get_vtable_trampoline (vt, i);
}
} else {
mono_class_setup_vtable (klass);
for (i = 0; i < klass->vtable_size; ++i) {
MonoMethod *cm;
cm = klass->vtable [i];
if (cm) {
vt->vtable [i] = callbacks.create_jit_trampoline (domain, cm, error);
if (!is_ok (error)) {
mono_domain_unlock (domain);
mono_loader_unlock ();
MONO_PROFILER_RAISE (vtable_failed, (vt));
return NULL;
}
}
}
}
if (imt_table_bytes) {
/* Now that the vtable is full, we can actually fill up the IMT */
for (i = 0; i < MONO_IMT_SIZE; ++i)
interface_offsets [i] = callbacks.get_imt_trampoline (vt, i);
}
/*
* FIXME: Is it ok to allocate while holding the domain/loader locks ? If not, we can release them, allocate, then
* re-acquire them and check if another thread has created the vtable in the meantime.
*/
/* Special case System.MonoType to avoid infinite recursion */
if (klass != mono_defaults.runtimetype_class) {
vt->type = mono_type_get_object_checked (domain, &klass->byval_arg, error);
if (!is_ok (error)) {
mono_domain_unlock (domain);
mono_loader_unlock ();
MONO_PROFILER_RAISE (vtable_failed, (vt));
return NULL;
}
if (mono_object_get_class ((MonoObject *)vt->type) != mono_defaults.runtimetype_class)
/* This is unregistered in
unregister_vtable_reflection_type() in
domain.c. */
MONO_GC_REGISTER_ROOT_IF_MOVING (vt->type, MONO_ROOT_SOURCE_REFLECTION, vt, "Reflection Type Object");
}
mono_vtable_set_is_remote (vt, mono_class_is_contextbound (klass));
/* class_vtable_array keeps an array of created vtables
*/
g_ptr_array_add (domain->class_vtable_array, vt);
/* klass->runtime_info is protected by the loader lock, both when
* it it enlarged and when it is stored info.
*/
/*
* Store the vtable in klass->runtime_info.
* klass->runtime_info is accessed without locking, so this do this last after the vtable has been constructed.
*/
mono_memory_barrier ();
old_info = klass->runtime_info;
if (old_info && old_info->max_domain >= domain->domain_id) {
/* someone already created a large enough runtime info */
old_info->domain_vtables [domain->domain_id] = vt;
} else {
int new_size = domain->domain_id;
if (old_info)
new_size = MAX (new_size, old_info->max_domain);
new_size++;
/* make the new size a power of two */
i = 2;
while (new_size > i)
i <<= 1;
new_size = i;
/* this is a bounded memory retention issue: may want to
* handle it differently when we'll have a rcu-like system.
*/
runtime_info = (MonoClassRuntimeInfo *)mono_image_alloc0 (klass->image, MONO_SIZEOF_CLASS_RUNTIME_INFO + new_size * sizeof (gpointer));
runtime_info->max_domain = new_size - 1;
/* copy the stuff from the older info */
if (old_info) {
memcpy (runtime_info->domain_vtables, old_info->domain_vtables, (old_info->max_domain + 1) * sizeof (gpointer));
}
runtime_info->domain_vtables [domain->domain_id] = vt;
/* keep this last*/
mono_memory_barrier ();
klass->runtime_info = runtime_info;
}
if (klass == mono_defaults.runtimetype_class) {
vt->type = mono_type_get_object_checked (domain, &klass->byval_arg, error);
if (!is_ok (error)) {
mono_domain_unlock (domain);
mono_loader_unlock ();
MONO_PROFILER_RAISE (vtable_failed, (vt));
return NULL;
}
if (mono_object_get_class ((MonoObject *)vt->type) != mono_defaults.runtimetype_class)
/* This is unregistered in
unregister_vtable_reflection_type() in
domain.c. */
MONO_GC_REGISTER_ROOT_IF_MOVING(vt->type, MONO_ROOT_SOURCE_REFLECTION, vt, "Reflection Type Object");
}
mono_domain_unlock (domain);
mono_loader_unlock ();
/* make sure the parent is initialized */
/*FIXME shouldn't this fail the current type?*/
if (klass->parent)
mono_class_vtable_checked (domain, klass->parent, error);
MONO_PROFILER_RAISE (vtable_loaded, (vt));
return vt;
}
#ifndef DISABLE_REMOTING
/**
* mono_remote_class_is_interface_proxy:
* \param remote_class
*
* Returns TRUE if the given remote class is a proxying an interface (as
* opposed to a class deriving from MarshalByRefObject).
*/
gboolean
mono_remote_class_is_interface_proxy (MonoRemoteClass *remote_class)
{
/* This if condition is taking advantage of how mono_remote_class ()
* works: if that code changes, this needs to change too. */
return (remote_class->interface_count >= 1 &&
remote_class->proxy_class == mono_defaults.marshalbyrefobject_class);
}
/**
* mono_class_proxy_vtable:
* \param domain the application domain
* \param remove_class the remote class
* \param error set on error
* Creates a vtable for transparent proxies. It is basically
* a copy of the real vtable of the class wrapped in \p remote_class,
* but all function pointers invoke the remoting functions, and
* \c vtable->klass points to the transparent proxy class, and not to \p class.
*
* On failure returns NULL and sets \p error
*/
static MonoVTable *
mono_class_proxy_vtable (MonoDomain *domain, MonoRemoteClass *remote_class, MonoRemotingTarget target_type, MonoError *error)
{
MONO_REQ_GC_UNSAFE_MODE;
MonoVTable *vt, *pvt;
int i, j, vtsize, extra_interface_vtsize = 0;
guint32 max_interface_id;
MonoClass *k;
GSList *extra_interfaces = NULL;
MonoClass *klass = remote_class->proxy_class;
gpointer *interface_offsets;
uint8_t *bitmap = NULL;
int bsize;
size_t imt_table_bytes;
#ifdef COMPRESSED_INTERFACE_BITMAP
int bcsize;
#endif
error_init (error);
vt = mono_class_vtable_checked (domain, klass, error);
if (!is_ok (error))
return NULL;
max_interface_id = vt->max_interface_id;
/* Calculate vtable space for extra interfaces */
for (j = 0; j < remote_class->interface_count; j++) {
MonoClass* iclass = remote_class->interfaces[j];
GPtrArray *ifaces;
int method_count;
/*FIXME test for interfaces with variant generic arguments*/
if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, iclass->interface_id))
continue; /* interface implemented by the class */
if (g_slist_find (extra_interfaces, iclass))
continue;
extra_interfaces = g_slist_prepend (extra_interfaces, iclass);
method_count = mono_class_num_methods (iclass);
ifaces = mono_class_get_implemented_interfaces (iclass, error);
goto_if_nok (error, failure);
if (ifaces) {
for (i = 0; i < ifaces->len; ++i) {
MonoClass *ic = (MonoClass *)g_ptr_array_index (ifaces, i);
/*FIXME test for interfaces with variant generic arguments*/
if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass, ic->interface_id))
continue; /* interface implemented by the class */
if (g_slist_find (extra_interfaces, ic))
continue;
extra_interfaces = g_slist_prepend (extra_interfaces, ic);
method_count += mono_class_num_methods (ic);
}
g_ptr_array_free (ifaces, TRUE);
ifaces = NULL;
}
extra_interface_vtsize += method_count * sizeof (gpointer);
if (iclass->max_interface_id > max_interface_id) max_interface_id = iclass->max_interface_id;
}
imt_table_bytes = sizeof (gpointer) * MONO_IMT_SIZE;
UnlockedIncrement (&mono_stats.imt_number_of_tables);
UnlockedAdd (&mono_stats.imt_tables_size, imt_table_bytes);
vtsize = imt_table_bytes + MONO_SIZEOF_VTABLE + klass->vtable_size * sizeof (gpointer);
UnlockedAdd (&mono_stats.class_vtable_size, vtsize + extra_interface_vtsize);
interface_offsets = alloc_vtable (domain, vtsize + extra_interface_vtsize, imt_table_bytes);
pvt = (MonoVTable*) ((char*)interface_offsets + imt_table_bytes);
g_assert (!((gsize)pvt & 7));
memcpy (pvt, vt, MONO_SIZEOF_VTABLE + klass->vtable_size * sizeof (gpointer));
pvt->klass = mono_defaults.transparent_proxy_class;
MONO_PROFILER_RAISE (vtable_loading, (pvt));
/* we need to keep the GC descriptor for a transparent proxy or we confuse the precise GC */
pvt->gc_descr = mono_defaults.transparent_proxy_class->gc_descr;
if (mono_remote_class_is_interface_proxy (remote_class)) {
/* If it's a transparent proxy for an interface, set the
* MonoVTable:type to the interface type, not the placeholder
* MarshalByRefObject class. This is used when mini JITs calls
* to Object.GetType ()
*/
MonoType *itf_proxy_type = &remote_class->interfaces[0]->byval_arg;
pvt->type = mono_type_get_object_checked (domain, itf_proxy_type, error);
goto_if_nok (error, failure);
}
/* initialize vtable */
mono_class_setup_vtable (klass);
for (i = 0; i < klass->vtable_size; ++i) {
MonoMethod *cm;
if ((cm = klass->vtable [i])) {
pvt->vtable [i] = create_remoting_trampoline (domain, cm, target_type, error);
goto_if_nok (error, failure);
} else
pvt->vtable [i] = NULL;
}
if (mono_class_is_abstract (klass)) {
/* create trampolines for abstract methods */
for (k = klass; k; k = k->parent) {
MonoMethod* m;
gpointer iter = NULL;
while ((m = mono_class_get_methods (k, &iter)))
if (!pvt->vtable [m->slot]) {
pvt->vtable [m->slot] = create_remoting_trampoline (domain, m, target_type, error);
goto_if_nok (error, failure);
}
}
}
pvt->max_interface_id = max_interface_id;
bsize = sizeof (guint8) * (max_interface_id/8 + 1 );
#ifdef COMPRESSED_INTERFACE_BITMAP
bitmap = (uint8_t *)g_malloc0 (bsize);
#else
bitmap = (uint8_t *)mono_domain_alloc0 (domain, bsize);
#endif
for (i = 0; i < klass->interface_offsets_count; ++i) {
int interface_id = klass->interfaces_packed [i]->interface_id;
bitmap [interface_id >> 3] |= (1 << (interface_id & 7));
}
if (extra_interfaces) {
int slot = klass->vtable_size;
MonoClass* interf;
gpointer iter;
MonoMethod* cm;
GSList *list_item;
/* Create trampolines for the methods of the interfaces */
for (list_item = extra_interfaces; list_item != NULL; list_item=list_item->next) {
interf = (MonoClass *)list_item->data;
bitmap [interf->interface_id >> 3] |= (1 << (interf->interface_id & 7));
iter = NULL;
j = 0;
while ((cm = mono_class_get_methods (interf, &iter))) {
pvt->vtable [slot + j++] = create_remoting_trampoline (domain, cm, target_type, error);
goto_if_nok (error, failure);
}
slot += mono_class_num_methods (interf);
}
}
/* Now that the vtable is full, we can actually fill up the IMT */
build_imt (klass, pvt, domain, interface_offsets, extra_interfaces);
if (extra_interfaces) {
g_slist_free (extra_interfaces);
}
#ifdef COMPRESSED_INTERFACE_BITMAP
bcsize = mono_compress_bitmap (NULL, bitmap, bsize);
pvt->interface_bitmap = mono_domain_alloc0 (domain, bcsize);
mono_compress_bitmap (pvt->interface_bitmap, bitmap, bsize);
g_free (bitmap);
#else
pvt->interface_bitmap = bitmap;
#endif
MONO_PROFILER_RAISE (vtable_loaded, (pvt));
return pvt;
failure:
if (extra_interfaces)
g_slist_free (extra_interfaces);
#ifdef COMPRESSED_INTERFACE_BITMAP
g_free (bitmap);
#endif
MONO_PROFILER_RAISE (vtable_failed, (pvt));
return NULL;
}
#endif /* DISABLE_REMOTING */
/**
* mono_class_field_is_special_static:
* \returns whether \p field is a thread/context static field.
*/
gboolean
mono_class_field_is_special_static (MonoClassField *field)
{
MONO_REQ_GC_NEUTRAL_MODE
if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
return FALSE;
if (mono_field_is_deleted (field))
return FALSE;
if (!(field->type->attrs & FIELD_ATTRIBUTE_LITERAL)) {
if (field_is_special_static (field->parent, field) != SPECIAL_STATIC_NONE)
return TRUE;
}
return FALSE;
}
/**
* mono_class_field_get_special_static_type:
* \param field The \c MonoClassField describing the field.
* \returns \c SPECIAL_STATIC_THREAD if the field is thread static, \c SPECIAL_STATIC_CONTEXT if it is context static,
* \c SPECIAL_STATIC_NONE otherwise.
*/
guint32
mono_class_field_get_special_static_type (MonoClassField *field)
{
MONO_REQ_GC_NEUTRAL_MODE
if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
return SPECIAL_STATIC_NONE;
if (mono_field_is_deleted (field))
return SPECIAL_STATIC_NONE;
if (!(field->type->attrs & FIELD_ATTRIBUTE_LITERAL))
return field_is_special_static (field->parent, field);
return SPECIAL_STATIC_NONE;
}
/**
* mono_class_has_special_static_fields:
* \returns whether \p klass has any thread/context static fields.
*/
gboolean
mono_class_has_special_static_fields (MonoClass *klass)
{
MONO_REQ_GC_NEUTRAL_MODE
MonoClassField *field;
gpointer iter;
iter = NULL;
while ((field = mono_class_get_fields (klass, &iter))) {
g_assert (field->parent == klass);
if (mono_class_field_is_special_static (field))
return TRUE;
}
return FALSE;
}
#ifndef DISABLE_REMOTING
/**
* create_remote_class_key:
* Creates an array of pointers that can be used as a hash key for a remote class.
* The first element of the array is the number of pointers.
*/
static gpointer*
create_remote_class_key (MonoRemoteClass *remote_class, MonoClass *extra_class)
{
MONO_REQ_GC_NEUTRAL_MODE;
gpointer *key;
int i, j;
if (remote_class == NULL) {
if (mono_class_is_interface (extra_class)) {
key = (void **)g_malloc (sizeof(gpointer) * 3);
key [0] = GINT_TO_POINTER (2);
key [1] = mono_defaults.marshalbyrefobject_class;
key [2] = extra_class;
} else {
key = (void **)g_malloc (sizeof(gpointer) * 2);
key [0] = GINT_TO_POINTER (1);
key [1] = extra_class;
}
} else {
if (extra_class != NULL && mono_class_is_interface (extra_class)) {
key = (void **)g_malloc (sizeof(gpointer) * (remote_class->interface_count + 3));
key [0] = GINT_TO_POINTER (remote_class->interface_count + 2);
key [1] = remote_class->proxy_class;
// Keep the list of interfaces sorted
for (i = 0, j = 2; i < remote_class->interface_count; i++, j++) {
if (extra_class && remote_class->interfaces [i] > extra_class) {
key [j++] = extra_class;
extra_class = NULL;
}
key [j] = remote_class->interfaces [i];
}
if (extra_class)
key [j] = extra_class;
} else {
// Replace the old class. The interface list is the same
key = (void **)g_malloc (sizeof(gpointer) * (remote_class->interface_count + 2));
key [0] = GINT_TO_POINTER (remote_class->interface_count + 1);
key [1] = extra_class != NULL ? extra_class : remote_class->proxy_class;
for (i = 0; i < remote_class->interface_count; i++)
key [2 + i] = remote_class->interfaces [i];
}
}
return key;
}
/**
* copy_remote_class_key:
*
* Make a copy of KEY in the domain and return the copy.
*/
static gpointer*
copy_remote_class_key (MonoDomain *domain, gpointer *key)
{
MONO_REQ_GC_NEUTRAL_MODE
int key_size = (GPOINTER_TO_UINT (key [0]) + 1) * sizeof (gpointer);
gpointer *mp_key = (gpointer *)mono_domain_alloc (domain, key_size);
memcpy (mp_key, key, key_size);
return mp_key;
}
/**
* mono_remote_class:
* \param domain the application domain
* \param class_name name of the remote class
* \param error set on error
* Creates and initializes a \c MonoRemoteClass object for a remote type.
* On failure returns NULL and sets \p error
*/
MonoRemoteClass*
mono_remote_class (MonoDomain *domain, MonoStringHandle class_name, MonoClass *proxy_class, MonoError *error)
{
MONO_REQ_GC_UNSAFE_MODE;
MonoRemoteClass *rc;
gpointer* key, *mp_key;
char *name;
error_init (error);
key = create_remote_class_key (NULL, proxy_class);
mono_domain_lock (domain);
rc = (MonoRemoteClass *)g_hash_table_lookup (domain->proxy_vtable_hash, key);
if (rc) {
g_free (key);
mono_domain_unlock (domain);
return rc;
}
name = mono_string_to_utf8_mp (domain->mp, MONO_HANDLE_RAW (class_name), error);
if (!is_ok (error)) {
g_free (key);
mono_domain_unlock (domain);
return NULL;
}
mp_key = copy_remote_class_key (domain, key);
g_free (key);
key = mp_key;
if (mono_class_is_interface (proxy_class)) {
/* If we need to proxy an interface, we use this stylized
* representation (interface_count >= 1, proxy_class is
* MarshalByRefObject). The code in
* mono_remote_class_is_interface_proxy () depends on being
* able to detect that we're doing this, so if this
* representation changes, change GetType, too. */
rc = (MonoRemoteClass *)mono_domain_alloc (domain, MONO_SIZEOF_REMOTE_CLASS + sizeof(MonoClass*));
rc->interface_count = 1;
rc->interfaces [0] = proxy_class;
rc->proxy_class = mono_defaults.marshalbyrefobject_class;
} else {
rc = (MonoRemoteClass *)mono_domain_alloc (domain, MONO_SIZEOF_REMOTE_CLASS);
rc->interface_count = 0;
rc->proxy_class = proxy_class;
}
rc->default_vtable = NULL;
rc->xdomain_vtable = NULL;
rc->proxy_class_name = name;
#ifndef DISABLE_PERFCOUNTERS
mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, mono_string_length (MONO_HANDLE_RAW (class_name)) + 1);
#endif
g_hash_table_insert (domain->proxy_vtable_hash, key, rc);
mono_domain_unlock (domain);
return rc;
}
/**
* clone_remote_class:
* Creates a copy of the remote_class, adding the provided class or interface
*/
static MonoRemoteClass*
clone_remote_class (MonoDomain *domain, MonoRemoteClass* remote_class, MonoClass *extra_class)
{
MONO_REQ_GC_NEUTRAL_MODE;
MonoRemoteClass *rc;
gpointer* key, *mp_key;
key = create_remote_class_key (remote_class, extra_class);
rc = (MonoRemoteClass *)g_hash_table_lookup (domain->proxy_vtable_hash, key);
if (rc != NULL) {
g_free (key);
return rc;
}
mp_key = copy_remote_class_key (domain, key);
g_free (key);
key = mp_key;
if (mono_class_is_interface (extra_class)) {
int i,j;
rc = (MonoRemoteClass *)mono_domain_alloc (domain, MONO_SIZEOF_REMOTE_CLASS + sizeof(MonoClass*) * (remote_class->interface_count + 1));
rc->proxy_class = remote_class->proxy_class;
rc->interface_count = remote_class->interface_count + 1;
// Keep the list of interfaces sorted, since the hash key of
// the remote class depends on this
for (i = 0, j = 0; i < remote_class->interface_count; i++, j++) {
if (remote_class->interfaces [i] > extra_class && i == j)
rc->interfaces [j++] = extra_class;
rc->interfaces [j] = remote_class->interfaces [i];
}
if (i == j)
rc->interfaces [j] = extra_class;
} else {
// Replace the old class. The interface array is the same
rc = (MonoRemoteClass *)mono_domain_alloc (domain, MONO_SIZEOF_REMOTE_CLASS + sizeof(MonoClass*) * remote_class->interface_count);
rc->proxy_class = extra_class;
rc->interface_count = remote_class->interface_count;
if (rc->interface_count > 0)
memcpy (rc->interfaces, remote_class->interfaces, rc->interface_count * sizeof (MonoClass*));
}
rc->default_vtable = NULL;
rc->xdomain_vtable = NULL;
rc->proxy_class_name = remote_class->proxy_class_name;
g_hash_table_insert (domain->proxy_vtable_hash, key, rc);
return rc;
}
gpointer
mono_remote_class_vtable (MonoDomain *domain, MonoRemoteClass *remote_class, MonoRealProxyHandle rp, MonoError *error)
{
MONO_REQ_GC_UNSAFE_MODE;
gpointer result = NULL;
error_init (error);
mono_loader_lock (); /*FIXME mono_class_from_mono_type and mono_class_proxy_vtable take it*/
mono_domain_lock (domain);
gint32 target_domain_id = MONO_HANDLE_GETVAL (rp, target_domain_id);
if (target_domain_id != -1) {
if (remote_class->xdomain_vtable == NULL)
remote_class->xdomain_vtable = mono_class_proxy_vtable (domain, remote_class, MONO_REMOTING_TARGET_APPDOMAIN, error);
goto_if_nok (error, leave);
result = remote_class->xdomain_vtable;
goto leave;
}
if (remote_class->default_vtable == NULL) {
MonoReflectionTypeHandle reftype = MONO_HANDLE_NEW (MonoReflectionType, NULL);
MONO_HANDLE_GET (reftype, rp, class_to_proxy);
MonoType *type = MONO_HANDLE_GETVAL (reftype, type);
MonoClass *klass = mono_class_from_mono_type (type);
#ifndef DISABLE_COM
gboolean target_is_com = FALSE;
if (mono_class_is_com_object (klass) || (mono_class_get_com_object_class () && klass == mono_class_get_com_object_class ())) {
MonoVTable *klass_vtable = mono_class_vtable_checked (mono_domain_get (), klass, error);
goto_if_nok (error, leave);
if (!mono_vtable_is_remote (klass_vtable))
target_is_com = TRUE;
}
if (target_is_com)
remote_class->default_vtable = mono_class_proxy_vtable (domain, remote_class, MONO_REMOTING_TARGET_COMINTEROP, error);
else
#endif
remote_class->default_vtable = mono_class_proxy_vtable (domain, remote_class, MONO_REMOTING_TARGET_UNKNOWN, error);
/* N.B. both branches of the if modify error */
goto_if_nok (error, leave);
}
result = remote_class->default_vtable;
leave:
mono_domain_unlock (domain);
mono_loader_unlock ();
return result;
}
/**
* mono_upgrade_remote_class:
* \param domain the application domain
* \param tproxy the proxy whose remote class has to be upgraded.
* \param klass class to which the remote class can be casted.
* \param error set on error
* Updates the vtable of the remote class by adding the necessary method slots
* and interface offsets so it can be safely casted to klass. klass can be a
* class or an interface. On success returns TRUE, on failure returns FALSE and sets \p error.
*/
gboolean
mono_upgrade_remote_class (MonoDomain *domain, MonoObjectHandle proxy_object, MonoClass *klass, MonoError *error)
{
MONO_REQ_GC_UNSAFE_MODE;
error_init (error);
MonoTransparentProxyHandle tproxy = MONO_HANDLE_CAST (MonoTransparentProxy, proxy_object);
MonoRemoteClass *remote_class = MONO_HANDLE_GETVAL (tproxy, remote_class);
gboolean redo_vtable;
if (mono_class_is_interface (klass)) {
int i;
redo_vtable = TRUE;
for (i = 0; i < remote_class->interface_count && redo_vtable; i++)
if (remote_class->interfaces [i] == klass)
redo_vtable = FALSE;
}
else {
redo_vtable = (remote_class->proxy_class != klass);
}
mono_loader_lock (); /*FIXME mono_remote_class_vtable requires it.*/
mono_domain_lock (domain);
if (redo_vtable) {
MonoRemoteClass *fresh_remote_class = clone_remote_class (domain, remote_class, klass);
MONO_HANDLE_SETVAL (tproxy, remote_class, MonoRemoteClass*, fresh_remote_class);
MonoRealProxyHandle real_proxy = MONO_HANDLE_NEW (MonoRealProxy, NULL);
MONO_HANDLE_GET (real_proxy, tproxy, rp);
MONO_HANDLE_SETVAL (proxy_object, vtable, MonoVTable*, mono_remote_class_vtable (domain, fresh_remote_class, real_proxy, error));
goto_if_nok (error, leave);
}
leave:
mono_domain_unlock (domain);
mono_loader_unlock ();
return is_ok (error);
}
#endif /* DISABLE_REMOTING */
/**
* mono_object_get_virtual_method:
* \param obj object to operate on.
* \param method method
* Retrieves the \c MonoMethod that would be called on \p obj if \p obj is passed as
* the instance of a callvirt of \p method.
*/
MonoMethod*
mono_object_get_virtual_method (MonoObject *obj_raw, MonoMethod *method)
{
MONO_REQ_GC_UNSAFE_MODE;
HANDLE_FUNCTION_ENTER ();
ERROR_DECL (error);
MONO_HANDLE_DCL (MonoObject, obj);
MonoMethod *result = mono_object_handle_get_virtual_method (obj, method, error);
mono_error_assert_ok (error);
HANDLE_FUNCTION_RETURN_VAL (result);
}
/**
* mono_object_handle_get_virtual_method:
* \param obj object to operate on.
* \param method method
* Retrieves the \c MonoMethod that would be called on \p obj if \p obj is passed as
* the instance of a callvirt of \p method.
*/
MonoMethod*
mono_object_handle_get_virtual_method (MonoObjectHandle obj, MonoMethod *method, MonoError *error)
{
error_init (error);
gboolean is_proxy = FALSE;
MonoClass *klass = mono_handle_class (obj);
if (mono_class_is_transparent_proxy (klass)) {
MonoRemoteClass *remote_class = MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoTransparentProxy, obj), remote_class);
klass = remote_class->proxy_class;
is_proxy = TRUE;
}
return class_get_virtual_method (klass, method, is_proxy, error);
}
static MonoMethod*
class_get_virtual_method (MonoClass *klass, MonoMethod *method, gboolean is_proxy, MonoError *error)
{
error_init (error);
if (!is_proxy && ((method->flags & METHOD_ATTRIBUTE_FINAL) || !(method->flags & METHOD_ATTRIBUTE_VIRTUAL)))
return method;
mono_class_setup_vtable (klass);
MonoMethod **vtable = klass->vtable;
if (method->slot == -1) {
/* method->slot might not be set for instances of generic methods */
if (method->is_inflated) {
g_assert (((MonoMethodInflated*)method)->declaring->slot != -1);
method->slot = ((MonoMethodInflated*)method)->declaring->slot;
} else {
if (!is_proxy)
g_assert_not_reached ();
}
}
MonoMethod *res = NULL;
/* check method->slot is a valid index: perform isinstance? */
if (method->slot != -1) {
if (mono_class_is_interface (method->klass)) {
if (!is_proxy) {
gboolean variance_used = FALSE;
int iface_offset = mono_class_interface_offset_with_variance (klass, method->klass, &variance_used);
g_assert (iface_offset > 0);
res = vtable [iface_offset + method->slot];
}
} else {
res = vtable [method->slot];
}
}
#ifndef DISABLE_REMOTING
if (is_proxy) {
/* It may be an interface, abstract class method or generic method */
if (!res || mono_method_signature (res)->generic_param_count)
res = method;
/* generic methods demand invoke_with_check */
if (mono_method_signature (res)->generic_param_count)
res = mono_marshal_get_remoting_invoke_with_check (res, error);
else {
#ifndef DISABLE_COM
if (klass == mono_class_get_com_object_class () || mono_class_is_com_object (klass))
res = mono_cominterop_get_invoke (res);
else
#endif
res = mono_marshal_get_remoting_invoke (res, error);
}
} else
#endif
{
if (method->is_inflated) {
/* Have to inflate the result */
res = mono_class_inflate_generic_method_checked (res, &((MonoMethodInflated*)method)->context, error);
}
}
return res;
}
static MonoObject*
do_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError *error)
{
MONO_REQ_GC_UNSAFE_MODE;
MonoObject *result = NULL;
g_assert (callbacks.runtime_invoke);
error_init (error);
MONO_PROFILER_RAISE (method_begin_invoke, (method));
result = callbacks.runtime_invoke (method, obj, params, exc, error);
MONO_PROFILER_RAISE (method_end_invoke, (method));
if (!mono_error_ok (error))
return NULL;
return result;
}
/**
* mono_runtime_invoke:
* \param method method to invoke
* \param obj object instance
* \param params arguments to the method
* \param exc exception information.
* Invokes the method represented by \p method on the object \p obj.
* \p obj is the \c this pointer, it should be NULL for static
* methods, a \c MonoObject* for object instances and a pointer to
* the value type for value types.
*
* The params array contains the arguments to the method with the
* same convention: \c MonoObject* pointers for object instances and
* pointers to the value type otherwise.
*
* From unmanaged code you'll usually use the
* \c mono_runtime_invoke variant.
*
* Note that this function doesn't handle virtual methods for
* you, it will exec the exact method you pass: we still need to
* expose a function to lookup the derived class implementation
* of a virtual method (there are examples of this in the code,
* though).
*
* You can pass NULL as the \p exc argument if you don't want to
* catch exceptions, otherwise, \c *exc will be set to the exception
* thrown, if any. if an exception is thrown, you can't use the
* \c MonoObject* result from the function.
*
* If the method returns a value type, it is boxed in an object
* reference.
*/
MonoObject*
mono_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc)
{
ERROR_DECL (error);
MonoObject *res;
if (exc) {
res = mono_runtime_try_invoke (method, obj, params, exc, error);
if (*exc == NULL && !mono_error_ok(error)) {
*exc = (MonoObject*) mono_error_convert_to_exception (error);
} else
mono_error_cleanup (error);
} else {
res = mono_runtime_invoke_checked (method, obj, params, error);
mono_error_raise_exception_deprecated (error); /* OK to throw, external only without a good alternative */
}
return res;
}
/**
* mono_runtime_try_invoke:
* \param method method to invoke
* \param obj object instance
* \param params arguments to the method
* \param exc exception information.
* \param error set on error
* Invokes the method represented by \p method on the object \p obj.
*
* \p obj is the \c this pointer, it should be NULL for static
* methods, a \c MonoObject* for object instances and a pointer to
* the value type for value types.
*
* The params array contains the arguments to the method with the
* same convention: \c MonoObject* pointers for object instances and
* pointers to the value type otherwise.
*
* From unmanaged code you'll usually use the
* mono_runtime_invoke() variant.
*
* Note that this function doesn't handle virtual methods for
* you, it will exec the exact method you pass: we still need to
* expose a function to lookup the derived class implementation
* of a virtual method (there are examples of this in the code,
* though).
*
* For this function, you must not pass NULL as the \p exc argument if
* you don't want to catch exceptions, use
* mono_runtime_invoke_checked(). If an exception is thrown, you
* can't use the \c MonoObject* result from the function.
*
* If this method cannot be invoked, \p error will be set and \p exc and
* the return value must not be used.
*
* If the method returns a value type, it is boxed in an object
* reference.
*/
MonoObject*
mono_runtime_try_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError* error)
{
MONO_REQ_GC_UNSAFE_MODE;
g_assert (exc != NULL);
if (mono_runtime_get_no_exec ())
g_warning ("Invoking method '%s' when running in no-exec mode.\n", mono_method_full_name (method, TRUE));
return do_runtime_invoke (method, obj, params, exc, error);
}
/**
* mono_runtime_invoke_checked:
* \param method method to invoke
* \param obj object instance
* \param params arguments to the method
* \param error set on error
* Invokes the method represented by \p method on the object \p obj.
*
* \p obj is the \c this pointer, it should be NULL for static
* methods, a \c MonoObject* for object instances and a pointer to
* the value type for value types.
*
* The \p params array contains the arguments to the method with the
* same convention: \c MonoObject* pointers for object instances and
* pointers to the value type otherwise.
*
* From unmanaged code you'll usually use the
* mono_runtime_invoke() variant.
*
* Note that this function doesn't handle virtual methods for
* you, it will exec the exact method you pass: we still need to
* expose a function to lookup the derived class implementation
* of a virtual method (there are examples of this in the code,
* though).
*
* If an exception is thrown, you can't use the \c MonoObject* result
* from the function.
*
* If this method cannot be invoked, \p error will be set. If the
* method throws an exception (and we're in coop mode) the exception
* will be set in \p error.
*
* If the method returns a value type, it is boxed in an object
* reference.
*/
MonoObject*
mono_runtime_invoke_checked (MonoMethod *method, void *obj, void **params, MonoError* error)
{
MONO_REQ_GC_UNSAFE_MODE;
if (mono_runtime_get_no_exec ())
g_warning ("Invoking method '%s' when running in no-exec mode.\n", mono_method_full_name (method, TRUE));
return do_runtime_invoke (method, obj, params, NULL, error);
}
/**
* mono_method_get_unmanaged_thunk:
* \param method method to generate a thunk for.
*
* Returns an \c unmanaged->managed thunk that can be used to call
* a managed method directly from C.
*
* The thunk's C signature closely matches the managed signature:
*
* C#: <code>public bool Equals (object obj);</code>
*
* C: <code>typedef MonoBoolean (*Equals)(MonoObject*, MonoObject*, MonoException**);</code>
*
* The 1st (<code>this</code>) parameter must not be used with static methods:
*
* C#: <code>public static bool ReferenceEquals (object a, object b);</code>
*
* C: <code>typedef MonoBoolean (*ReferenceEquals)(MonoObject*, MonoObject*, MonoException**);</code>
*
* The last argument must be a non-null \c MonoException* pointer.
* It has "out" semantics. After invoking the thunk, \c *ex will be NULL if no
* exception has been thrown in managed code. Otherwise it will point
* to the \c MonoException* caught by the thunk. In this case, the result of
* the thunk is undefined:
*
* <pre>
* MonoMethod *method = ... // MonoMethod* of System.Object.Equals
*
* MonoException *ex = NULL;
*
* Equals func = mono_method_get_unmanaged_thunk (method);
*
* MonoBoolean res = func (thisObj, objToCompare, &ex);
*
* if (ex) {
*
* // handle exception
*
* }
* </pre>
*
* The calling convention of the thunk matches the platform's default
* convention. This means that under Windows, C declarations must
* contain the \c __stdcall attribute:
*
* C: <code>typedef MonoBoolean (__stdcall *Equals)(MonoObject*, MonoObject*, MonoException**);</code>
*
* LIMITATIONS
*
* Value type arguments and return values are treated as they were objects:
*
* C#: <code>public static Rectangle Intersect (Rectangle a, Rectangle b);</code>
* C: <code>typedef MonoObject* (*Intersect)(MonoObject*, MonoObject*, MonoException**);</code>
*
* Arguments must be properly boxed upon trunk's invocation, while return
* values must be unboxed.
*/
gpointer
mono_method_get_unmanaged_thunk (MonoMethod *method)
{
MONO_REQ_GC_NEUTRAL_MODE;
MONO_REQ_API_ENTRYPOINT;
ERROR_DECL (error);
gpointer res;
g_assert (!mono_threads_is_coop_enabled ());
MONO_ENTER_GC_UNSAFE;
method = mono_marshal_get_thunk_invoke_wrapper (method);
res = mono_compile_method_checked (method, error);
mono_error_cleanup (error);
MONO_EXIT_GC_UNSAFE;
return res;
}
void
mono_copy_value (MonoType *type, void *dest, void *value, int deref_pointer)
{
MONO_REQ_GC_UNSAFE_MODE;
int t;
if (type->byref) {
/* object fields cannot be byref, so we don't need a
wbarrier here */
gpointer *p = (gpointer*)dest;
*p = value;
return;
}
t = type->type;
handle_enum:
switch (t) {
case MONO_TYPE_BOOLEAN:
case MONO_TYPE_I1:
case MONO_TYPE_U1: {
guint8 *p = (guint8*)dest;
*p = value ? *(guint8*)value : 0;
return;
}
case MONO_TYPE_I2:
case MONO_TYPE_U2:
case MONO_TYPE_CHAR: {
guint16 *p = (guint16*)dest;
*p = value ? *(guint16*)value : 0;
return;
}
#if SIZEOF_VOID_P == 4
case MONO_TYPE_I:
case MONO_TYPE_U:
#endif
case MONO_TYPE_I4:
case MONO_TYPE_U4: {
gint32 *p = (gint32*)dest;
*p = value ? *(gint32*)value : 0;
return;
}
#if SIZEOF_VOID_P == 8
case MONO_TYPE_I:
case MONO_TYPE_U:
#endif
case MONO_TYPE_I8:
case MONO_TYPE_U8: {
gint64 *p = (gint64*)dest;
*p = value ? *(gint64*)value : 0;
return;
}
case MONO_TYPE_R4: {
float *p = (float*)dest;
*p = value ? *(float*)value : 0;
return;
}
case MONO_TYPE_R8: {
double *p = (double*)dest;
*p = value ? *(double*)value : 0;
return;
}
case MONO_TYPE_STRING:
case MONO_TYPE_SZARRAY:
case MONO_TYPE_CLASS:
case MONO_TYPE_OBJECT:
case MONO_TYPE_ARRAY:
mono_gc_wbarrier_generic_store (dest, deref_pointer ? *(MonoObject **)value : (MonoObject *)value);
return;
case MONO_TYPE_FNPTR:
case MONO_TYPE_PTR: {
gpointer *p = (gpointer*)dest;
*p = deref_pointer? *(gpointer*)value: value;
return;
}
case MONO_TYPE_VALUETYPE:
/* note that 't' and 'type->type' can be different */
if (type->type == MONO_TYPE_VALUETYPE && type->data.klass->enumtype) {
t = mono_class_enum_basetype (type->data.klass)->type;
goto handle_enum;
} else {
MonoClass *klass = mono_class_from_mono_type (type);
int size = mono_class_value_size (klass, NULL);
if (value == NULL)
mono_gc_bzero_atomic (dest, size);
else
mono_gc_wbarrier_value_copy (dest, value, 1, klass);
}
return;
case MONO_TYPE_GENERICINST:
t = type->data.generic_class->container_class->byval_arg.type;
goto handle_enum;
default:
g_error ("got type %x", type->type);
}
}
/**
* mono_field_set_value:
* \param obj Instance object