2004-07-05 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / metadata / object.c
index 7dc38a1c4e55109b2c376a631c5093c0a9eed737..bdf832baa6dcb0a122d98130410123ff6c39076e 100644 (file)
 #include <mono/metadata/object.h>
 #include <mono/metadata/gc-internal.h>
 #include <mono/metadata/exception.h>
-#include <mono/metadata/appdomain.h>
+#include <mono/metadata/domain-internals.h>
+#include "mono/metadata/metadata-internals.h"
+#include "mono/metadata/class-internals.h"
 #include <mono/metadata/assembly.h>
 #include <mono/metadata/threadpool.h>
 #include <mono/metadata/marshal.h>
 #include "mono/metadata/debug-helpers.h"
 #include "mono/metadata/marshal.h"
 #include <mono/metadata/threads.h>
+#include <mono/metadata/threads-types.h>
 #include <mono/metadata/environment.h>
 #include "mono/metadata/profiler-private.h"
 #include <mono/os/gc_wrapper.h>
@@ -55,9 +58,61 @@ mono_runtime_object_init (MonoObject *this)
 
        g_assert (method);
 
+       if (method->klass->valuetype)
+               this = mono_object_unbox (this);
        mono_runtime_invoke (method, this, NULL, NULL);
 }
 
+/* The pseudo algorithm for type initialization from the spec
+Note it doesn't say anything about domains - only threads.
+
+2. If the type is initialized you are done.
+2.1. If the type is not yet initialized, try to take an 
+     initialization lock.  
+2.2. If successful, record this thread as responsible for 
+     initializing the type and proceed to step 2.3.
+2.2.1. If not, see whether this thread or any thread 
+     waiting for this thread to complete already holds the lock.
+2.2.2. If so, return since blocking would create a deadlock.  This thread 
+     will now see an incompletely initialized state for the type, 
+     but no deadlock will arise.
+2.2.3  If not, block until the type is initialized then return.
+2.3 Initialize the parent type and then all interfaces implemented 
+    by this type.
+2.4 Execute the type initialization code for this type.
+2.5 Mark the type as initialized, release the initialization lock, 
+    awaken any threads waiting for this type to be initialized, 
+    and return.
+
+*/
+
+typedef struct
+{
+       guint32 initializing_tid;
+       guint32 waiting_count;
+       CRITICAL_SECTION initialization_section;
+} TypeInitializationLock;
+
+/* for locking access to type_initialization_hash and blocked_thread_hash */
+static CRITICAL_SECTION type_initialization_section;
+
+/* from vtable to lock */
+static GHashTable *type_initialization_hash;
+
+/* from thread id to thread id being waited on */
+static GHashTable *blocked_thread_hash;
+
+/* Main thread */
+static MonoThread *main_thread;
+
+void
+mono_type_initialization_init (void)
+{
+       InitializeCriticalSection (&type_initialization_section);
+       type_initialization_hash = g_hash_table_new (NULL, NULL);
+       blocked_thread_hash = g_hash_table_new (NULL, NULL);
+}
+
 /*
  * mono_runtime_class_init:
  * @vtable: vtable that needs to be initialized
@@ -74,11 +129,10 @@ mono_runtime_class_init (MonoVTable *vtable)
        MonoClass *klass;
        gchar *full_name;
        gboolean found;
-       MonoDomain *last_domain = NULL;
 
        MONO_ARCH_SAVE_REGS;
 
-       if (vtable->initialized || vtable->initializing)
+       if (vtable->initialized)
                return;
 
        exc = NULL;
@@ -95,30 +149,82 @@ mono_runtime_class_init (MonoVTable *vtable)
        }
 
        if (found) {
-               mono_domain_lock (vtable->domain);
+               MonoDomain *domain = vtable->domain;
+               TypeInitializationLock *lock;
+               guint32 tid = GetCurrentThreadId();
+               int do_initialization = 0;
+               MonoDomain *last_domain = NULL;
+
+               EnterCriticalSection (&type_initialization_section);
                /* double check... */
-               if (vtable->initialized || vtable->initializing) {
-                       mono_domain_unlock (vtable->domain);
+               if (vtable->initialized) {
+                       LeaveCriticalSection (&type_initialization_section);
                        return;
                }
-               vtable->initializing = 1;
-               if (mono_domain_get () != vtable->domain) {
-                       /* Transfer into the target domain */
-                       last_domain = mono_domain_get ();
-                       if (!mono_domain_set (vtable->domain, FALSE)) {
-                               vtable->initialized = 1;
-                               vtable->initializing = 0;
-                               mono_domain_unlock (vtable->domain);
-                               mono_raise_exception (mono_get_exception_appdomain_unloaded ());
+               lock = g_hash_table_lookup (type_initialization_hash, vtable);
+               if (lock == NULL) {
+                       /* This thread will get to do the initialization */
+                       if (mono_domain_get () != domain) {
+                               /* Transfer into the target domain */
+                               last_domain = mono_domain_get ();
+                               if (!mono_domain_set (domain, FALSE)) {
+                                       vtable->initialized = 1;
+                                       LeaveCriticalSection (&type_initialization_section);
+                                       mono_raise_exception (mono_get_exception_appdomain_unloaded ());
+                               }
+                       }
+                       lock = g_malloc (sizeof(TypeInitializationLock));
+                       InitializeCriticalSection (&lock->initialization_section);
+                       lock->initializing_tid = tid;
+                       lock->waiting_count = 1;
+                       /* grab the vtable lock while this thread still owns type_initialization_section */
+                       EnterCriticalSection (&lock->initialization_section);
+                       g_hash_table_insert (type_initialization_hash, vtable, lock);
+                       do_initialization = 1;
+               } else {
+                       gpointer blocked;
+
+                       if (lock->initializing_tid == tid) {
+                               LeaveCriticalSection (&type_initialization_section);
+                               return;
                        }
+                       /* see if the thread doing the initialization is already blocked on this thread */
+                       blocked = GUINT_TO_POINTER (lock->initializing_tid);
+                       while ((blocked = g_hash_table_lookup (blocked_thread_hash, blocked))) {
+                               if (blocked == GUINT_TO_POINTER (tid)) {
+                                       LeaveCriticalSection (&type_initialization_section);
+                                       return;
+                               }
+                       }
+                       ++lock->waiting_count;
+                       /* record the fact that we are waiting on the initializing thread */
+                       g_hash_table_insert (blocked_thread_hash, GUINT_TO_POINTER (tid), GUINT_TO_POINTER (lock->initializing_tid));
+               }
+               LeaveCriticalSection (&type_initialization_section);
+
+               if (do_initialization) {
+                       mono_runtime_invoke (method, NULL, NULL, (MonoObject **) &exc);
+                       if (last_domain)
+                               mono_domain_set (last_domain, TRUE);
+                       LeaveCriticalSection (&lock->initialization_section);
+               } else {
+                       /* this just blocks until the initializing thread is done */
+                       EnterCriticalSection (&lock->initialization_section);
+                       LeaveCriticalSection (&lock->initialization_section);
+               }
+
+               EnterCriticalSection (&type_initialization_section);
+               if (lock->initializing_tid != tid)
+                       g_hash_table_remove (blocked_thread_hash, GUINT_TO_POINTER (tid));
+               --lock->waiting_count;
+               if (lock->waiting_count == 0) {
+                       DeleteCriticalSection (&lock->initialization_section);
+                       g_hash_table_remove (type_initialization_hash, vtable);
+                       g_free (lock);
                }
-               mono_runtime_invoke (method, NULL, NULL, (MonoObject **) &exc);
-               if (last_domain)
-                       mono_domain_set (last_domain, TRUE);
                vtable->initialized = 1;
-               vtable->initializing = 0;
                /* FIXME: if the cctor fails, the type must be marked as unusable */
-               mono_domain_unlock (vtable->domain);
+               LeaveCriticalSection (&type_initialization_section);
        } else {
                vtable->initialized = 1;
                return;
@@ -211,6 +317,7 @@ mono_class_compute_gc_descriptor (MonoClass *class)
 {
        MonoClassField *field;
        guint64 bitmap;
+       guint32 bm [2];
        int i;
        static gboolean gcj_inited = FALSE;
 
@@ -250,7 +357,7 @@ mono_class_compute_gc_descriptor (MonoClass *class)
 
                /* GC 6.1 has trouble handling 64 bit descriptors... */
                if ((class->instance_size / sizeof (gpointer)) > 30) {
-//                     printf ("TOO LARGE: %s %d.\n", class->name, class->instance_size / sizeof (gpointer));
+/*                     printf ("TOO LARGE: %s %d.\n", class->name, class->instance_size / sizeof (gpointer)); */
                        return;
                }
 
@@ -258,10 +365,10 @@ mono_class_compute_gc_descriptor (MonoClass *class)
 
                count ++;
 
-//             if (count > 442)
-//                     return;
+/*             if (count > 442) */
+/*                     return;  */
 
-//             printf("KLASS: %s.\n", class->name);
+/*             printf("KLASS: %s.\n", class->name); */
 
                for (p = class; p != NULL; p = p->parent) {
                for (i = 0; i < p->field.count; ++i) {
@@ -289,7 +396,7 @@ mono_class_compute_gc_descriptor (MonoClass *class)
                        case MONO_TYPE_U8:
                        case MONO_TYPE_R4:
                        case MONO_TYPE_R8:
-//                             printf ("F: %s %s %d %lld %llx.\n", class->name, field->name, field->offset, ((guint64)1) << pos, bitmap);
+/*                             printf ("F: %s %s %d %lld %llx.\n", class->name, field->name, field->offset, ((guint64)1) << pos, bitmap); */
                                break;
                        case MONO_TYPE_I:
                        case MONO_TYPE_STRING:
@@ -301,7 +408,7 @@ mono_class_compute_gc_descriptor (MonoClass *class)
                                g_assert ((field->offset % sizeof(gpointer)) == 0);
 
                                bitmap |= ((guint64)1) << pos;
-//                             printf ("F: %s %s %d %d %lld %llx.\n", class->name, field->name, field->offset, pos, ((guint64)(1)) << pos, bitmap);
+/*                             printf ("F: %s %s %d %d %lld %llx.\n", class->name, field->name, field->offset, pos, ((guint64)(1)) << pos, bitmap); */
                                break;
                        case MONO_TYPE_VALUETYPE: {
                                MonoClass *fclass = field->type->data.klass;
@@ -317,9 +424,12 @@ mono_class_compute_gc_descriptor (MonoClass *class)
                }
                }
 
-//             printf("CLASS: %s.%s -> %d %llx.\n", class->name_space, class->name, class->instance_size / sizeof (gpointer), bitmap);
+/*             printf("CLASS: %s.%s -> %d %llx.\n", class->name_space, class->name, class->instance_size / sizeof (gpointer), bitmap); */
                class->gc_bitmap = bitmap;
-               class->gc_descr = (gpointer)GC_make_descriptor ((GC_bitmap)&bitmap, class->instance_size / sizeof (gpointer));
+               /* Convert to the format expected by GC_make_descriptor */
+               bm [0] = (guint32)bitmap;
+               bm [1] = (guint32)(bitmap >> 32);
+               class->gc_descr = (gpointer)GC_make_descriptor ((GC_bitmap)&bm, class->instance_size / sizeof (gpointer));
        }
 }
 #endif /* CREATION_SPEEDUP */
@@ -402,7 +512,7 @@ mono_class_vtable (MonoDomain *domain, MonoClass *class)
 
 #if CREATION_SPEEDUP
        mono_class_compute_gc_descriptor (class);
-       if (domain != mono_root_domain)
+       if (domain != mono_get_root_domain ())
                /*
                 * We can't use typed allocation in the non-root domains, since the
                 * collector needs the GC descriptor stored in the vtable even after
@@ -586,47 +696,44 @@ mono_class_vtable (MonoDomain *domain, MonoClass *class)
 /**
  * mono_class_proxy_vtable:
  * @domain: the application domain
- * @class: the class to proxy
+ * @remove_class: the remote class
  *
  * Creates a vtable for transparent proxies. It is basically
- * a copy of the real vtable of @class, but all function pointers invoke
- * the remoting functions, and vtable->klass points to the 
- * transparent proxy class, and not to @class.
+ * a copy of the real vtable of the class wrapped in @remote_class,
+ * but all function pointers invoke the remoting functions, and
+ * vtable->klass points to the transparent proxy class, and not to @class.
  */
-MonoVTable *
-mono_class_proxy_vtable (MonoDomain *domain, MonoClass *class)
+static MonoVTable *
+mono_class_proxy_vtable (MonoDomain *domain, MonoRemoteClass *remote_class)
 {
        MonoVTable *vt, *pvt;
-       int i, j, vtsize, interface_vtsize = 0;
-       MonoClass* iclass = NULL;
-       MonoClass* k;
-
-       mono_domain_lock (domain);
-       pvt = mono_g_hash_table_lookup (domain->proxy_vtable_hash, class);
+       int i, j, vtsize, max_interface_id, extra_interface_vtsize = 0;
+       MonoClass *k;
+       MonoClass *class = remote_class->proxy_class;
 
-       if (pvt) {
-               mono_domain_unlock (domain);
-               return pvt;
-       }
+       vt = mono_class_vtable (domain, class);
+       max_interface_id = vt->max_interface_id;
 
-       if (class->flags & TYPE_ATTRIBUTE_INTERFACE) {
-               int method_count;
-               iclass = class;
-               class = mono_defaults.marshalbyrefobject_class;
+       /* Calculate vtable space for extra interfaces */
+       for (j = 0; j < remote_class->interface_count; j++) {
+               MonoClass* iclass = remote_class->interfaces[j];
+               int method_count = iclass->method.count;
+       
+               if (iclass->interface_id <= class->max_interface_id && class->interface_offsets[iclass->interface_id] != 0) 
+                       continue;       /* interface implemented by the class */
 
-               method_count = iclass->method.count;
                for (i = 0; i < iclass->interface_count; i++)
                        method_count += iclass->interfaces[i]->method.count;
 
-               interface_vtsize = method_count * sizeof (gpointer);
+               extra_interface_vtsize += method_count * sizeof (gpointer);
+               if (iclass->max_interface_id > max_interface_id) max_interface_id = iclass->max_interface_id;
        }
 
-       vt = mono_class_vtable (domain, class);
        vtsize = sizeof (MonoVTable) + class->vtable_size * sizeof (gpointer);
 
-       mono_stats.class_vtable_size += vtsize + interface_vtsize;
+       mono_stats.class_vtable_size += vtsize + extra_interface_vtsize;
 
-       pvt = mono_mempool_alloc (domain->mp, vtsize + interface_vtsize);
+       pvt = mono_mempool_alloc (domain->mp, vtsize + extra_interface_vtsize);
        memcpy (pvt, vt, vtsize);
 
        pvt->klass = mono_defaults.transparent_proxy_class;
@@ -651,57 +758,160 @@ mono_class_proxy_vtable (MonoDomain *domain, MonoClass *class)
                }
        }
 
-       if (iclass)
+       pvt->max_interface_id = max_interface_id;
+       pvt->interface_offsets = mono_mempool_alloc0 (domain->mp, 
+                       sizeof (gpointer) * (max_interface_id + 1));
+
+       /* initialize interface offsets */
+       for (i = 0; i <= class->max_interface_id; ++i) {
+               int slot = class->interface_offsets [i];
+               if (slot >= 0)
+                       pvt->interface_offsets [i] = &(pvt->vtable [slot]);
+       }
+
+       if (remote_class->interface_count > 0)
        {
-               int slot;
+               int slot = class->vtable_size;
                MonoClass* interf;
+               MonoClass* iclass;
+               int n;
 
-               pvt->max_interface_id = iclass->max_interface_id;
+               /* Create trampolines for the methods of the interfaces */
+               for (n = 0; n < remote_class->interface_count; n++) 
+               {
+                       iclass = remote_class->interfaces[n];
+                       if (iclass->interface_id <= class->max_interface_id && class->interface_offsets[iclass->interface_id] != 0) 
+                               continue;       /* interface implemented by the class */
                
-               pvt->interface_offsets = mono_mempool_alloc0 (domain->mp, 
-                               sizeof (gpointer) * (pvt->max_interface_id + 1));
+                       i = -1;
+                       interf = iclass;
+                       do {
+                               pvt->interface_offsets [interf->interface_id] = &pvt->vtable [slot];
+       
+                               for (j = 0; j < interf->method.count; ++j) {
+                                       MonoMethod *cm = interf->methods [j];
+                                       pvt->vtable [slot + j] = arch_create_remoting_trampoline (cm);
+                               }
+                               slot += interf->method.count;
+                               if (++i < iclass->interface_count) interf = iclass->interfaces[i];
+                               else interf = NULL;
+                               
+                       } while (interf);
+               }
+       }
 
-               /* Create trampolines for the methods of the interfaces */
-               slot = class->vtable_size;
-               interf = iclass;
-               i = -1;
-               do {
-                       pvt->interface_offsets [interf->interface_id] = &pvt->vtable [slot];
-
-                       for (j = 0; j < interf->method.count; ++j) {
-                               MonoMethod *cm = interf->methods [j];
-                               pvt->vtable [slot + j] = arch_create_remoting_trampoline (cm);
-                       }
-                       slot += interf->method.count;
-                       if (++i < iclass->interface_count) interf = iclass->interfaces[i];
-                       else interf = NULL;
-                       
-               } while (interf);
+       return pvt;
+}
 
-               class = iclass;
+/**
+ * mono_remote_class:
+ * @domain: the application domain
+ * @class_name: name of the remote class
+ *
+ * Creates and initializes a MonoRemoteClass object for a remote type. 
+ * 
+ */
+MonoRemoteClass*
+mono_remote_class (MonoDomain *domain, MonoString *class_name, MonoClass *proxy_class)
+{
+       MonoRemoteClass *rc;
+
+       mono_domain_lock (domain);
+       rc = mono_g_hash_table_lookup (domain->proxy_vtable_hash, class_name);
+
+       if (rc) {
+               mono_domain_unlock (domain);
+               return rc;
        }
-       else
+
+       rc = mono_mempool_alloc (domain->mp, sizeof(MonoRemoteClass));
+       rc->vtable = NULL;
+       rc->interface_count = 0;
+       rc->interfaces = NULL;
+       rc->proxy_class = mono_defaults.marshalbyrefobject_class;
+       rc->proxy_class_name = mono_string_to_utf8 (class_name);
+
+       mono_g_hash_table_insert (domain->proxy_vtable_hash, class_name, rc);
+       mono_upgrade_remote_class (domain, rc, proxy_class);
+
+       if (rc->vtable == NULL)
+               rc->vtable = mono_class_proxy_vtable (domain, rc);
+
+       mono_domain_unlock (domain);
+
+       return rc;
+}
+
+static void
+extend_interface_array (MonoDomain *domain, MonoRemoteClass *remote_class, int amount)
+{
+       /* Extends the array of interfaces. Memory is extended using blocks of 5 pointers */
+
+       int current_size = ((remote_class->interface_count / 5) + 1) * 5;
+       remote_class->interface_count += amount;
+
+       if (remote_class->interface_count > current_size || remote_class->interfaces == NULL) 
        {
-               pvt->interface_offsets = mono_mempool_alloc0 (domain->mp, 
-                               sizeof (gpointer) * (pvt->max_interface_id + 1));
-
-               /* initialize interface offsets */
-               for (i = 0; i <= class->max_interface_id; ++i) {
-                       int slot = class->interface_offsets [i];
-                       if (slot >= 0)
-                               pvt->interface_offsets [i] = &(pvt->vtable [slot]);
+               int new_size = ((remote_class->interface_count / 5) + 1) * 5;
+               MonoClass **new_array = mono_mempool_alloc (domain->mp, new_size * sizeof (MonoClass*));
+       
+               if (remote_class->interfaces != NULL)
+                       memcpy (new_array, remote_class->interfaces, current_size * sizeof (MonoClass*));
+               
+               remote_class->interfaces = new_array;
+       }
+}
+
+
+/**
+ * mono_upgrade_remote_class:
+ * @domain: the application domain
+ * @remote_class: the remote class
+ * @klass: class to which the remote class can be casted.
+ *
+ * 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.
+ */
+void mono_upgrade_remote_class (MonoDomain *domain, MonoRemoteClass *remote_class, MonoClass *klass)
+{
+       gboolean redo_vtable;
+
+       mono_domain_lock (domain);
+
+       if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
+               int i;
+               redo_vtable = TRUE;
+               for (i = 0; i < remote_class->interface_count; i++)
+                       if (remote_class->interfaces[i] == klass) redo_vtable = FALSE;
+                               
+               if (redo_vtable) {
+                       extend_interface_array (domain, remote_class, 1);
+                       remote_class->interfaces [remote_class->interface_count-1] = klass;
                }
        }
+       else {
+               redo_vtable = (remote_class->proxy_class != klass);
+               remote_class->proxy_class = klass;
+       }
 
-       mono_g_hash_table_insert (domain->proxy_vtable_hash, class, pvt);
+       if (redo_vtable)
+               remote_class->vtable = mono_class_proxy_vtable (domain, remote_class);
+/*
+       int n;
+       printf ("remote class upgrade - class:%s num-interfaces:%d\n", remote_class->proxy_class_name, remote_class->interface_count);
+       
+       for (n=0; n<remote_class->interface_count; n++)
+               printf ("  I:%s\n", remote_class->interfaces[n]->name);
+*/
 
        mono_domain_unlock (domain);
-
-       return pvt;
 }
 
-/*
- * Retrieve the MonoMethod that would to be called on obj if obj is passed as
+/**
+ * mono_object_get_virtual_method:
+ *
+ * Retrieve the MonoMethod that would be called on obj if obj is passed as
  * the instance of a callvirt of method.
  */
 MonoMethod*
@@ -713,7 +923,7 @@ mono_object_get_virtual_method (MonoObject *obj, MonoMethod *method) {
 
        klass = mono_object_class (obj);
        if (klass == mono_defaults.transparent_proxy_class) {
-               klass = ((MonoTransparentProxy *)obj)->klass;
+               klass = ((MonoTransparentProxy *)obj)->remote_class->proxy_class;
                is_proxy = TRUE;
        } else {
                is_proxy = FALSE;
@@ -729,7 +939,8 @@ mono_object_get_virtual_method (MonoObject *obj, MonoMethod *method) {
                if (!is_proxy)
                        res = vtable [klass->interface_offsets [method->klass->interface_id] + method->slot];
        } else {
-               res = vtable [method->slot];
+               if (method->slot != -1)
+                       res = vtable [method->slot];
        }
 
        if (is_proxy) {
@@ -751,6 +962,36 @@ dummy_mono_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObj
 
 static MonoInvokeFunc default_mono_runtime_invoke = dummy_mono_runtime_invoke;
 
+/**
+ * mono_runtime_invoke:
+ *
+ * Invokes the method represented by `method' on the object `obj'.
+ *
+ * obj is the 'this' pointer, it should be NULL for static
+ * methods, a 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: 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).
+ * 
+ * You can pass NULL as the exc argument if you don't want to
+ * catch exceptions, otherwise, *exc will be set to the exception
+ * thrown, if any.  if an exception is thrown, you can't use the
+ * 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)
 {
@@ -1016,7 +1257,7 @@ fire_process_exit_event (void)
        field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "ProcessExit");
        g_assert (field);
 
-       if (domain != mono_root_domain)
+       if (domain != mono_get_root_domain ())
                return;
 
        delegate = *(MonoObject **)(((char *)domain->domain) + field->offset); 
@@ -1042,7 +1283,9 @@ mono_runtime_run_main (MonoMethod *method, int argc, char* argv[],
        MonoDomain *domain = mono_domain_get ();
        gchar *utf8_fullpath;
        int result;
-       
+
+       main_thread = mono_thread_current ();
+
        main_args = (MonoArray*)mono_array_new (domain, mono_defaults.string_class, argc);
 
        if (!g_path_is_absolute (argv [0])) {
@@ -1172,9 +1415,10 @@ mono_unhandled_exception (MonoObject *exc)
        if (exc->vtable->klass != mono_defaults.threadabortexception_class) {
                delegate = *(MonoObject **)(((char *)domain->domain) + field->offset); 
 
-               /* set exitcode only in the main thread? */
-               mono_environment_exitcode_set (1);
-               if (domain != mono_root_domain || !delegate) {
+               /* set exitcode only in the main thread */
+               if (mono_thread_current () == main_thread)
+                       mono_environment_exitcode_set (1);
+               if (domain != mono_get_root_domain () || !delegate) {
                        mono_print_unhandled_exception (exc);
                } else {
                        MonoObject *e = NULL;
@@ -1229,8 +1473,19 @@ mono_runtime_exec_main (MonoMethod *method, MonoArray *args, MonoObject **exc)
 
        domain = mono_object_domain (args);
        if (!domain->entry_assembly) {
-               domain->entry_assembly = method->klass->image->assembly;
-               ves_icall_System_AppDomainSetup_InitAppDomainSetup (domain->setup);
+               gchar *str;
+               gchar *config_suffix;
+               MonoAssembly *assembly;
+
+               assembly = method->klass->image->assembly;
+               domain->entry_assembly = assembly;
+               domain->setup->application_base = mono_string_new (domain, assembly->basedir);
+
+               config_suffix = g_strconcat (assembly->aname.name, ".exe.config", NULL);
+               str = g_build_filename (assembly->basedir, config_suffix, NULL);
+               g_free (config_suffix);
+               domain->setup->configuration_file = mono_string_new (domain, str);
+               g_free (str);
        }
 
        /* FIXME: check signature of method */
@@ -1267,6 +1522,39 @@ mono_install_runtime_invoke (MonoInvokeFunc func)
        default_mono_runtime_invoke = func ? func: dummy_mono_runtime_invoke;
 }
 
+/**
+ * mono_runtime_invoke:
+ *
+ * Invokes the method represented by `method' on the object `obj'.
+ *
+ * obj is the 'this' pointer, it should be NULL for static
+ * methods, a 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: MonoObject* pointers for object instances and
+ * pointers to the value type otherwise. The _invoke_array
+ * variant takes a C# object[] as the params argument (MonoArray
+ * *params): in this case the value types are boxed inside the
+ * respective reference representation.
+ * 
+ * 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).
+ * 
+ * You can pass NULL as the exc argument if you don't want to
+ * catch exceptions, otherwise, *exc will be set to the exception
+ * thrown, if any.  if an exception is thrown, you can't use the
+ * MonoObject* result from the function.
+ * 
+ * If the method returns a value type, it is boxed in an object
+ * reference.
+ */
 MonoObject*
 mono_runtime_invoke_array (MonoMethod *method, void *obj, MonoArray *params,
                           MonoObject **exc)
@@ -1298,6 +1586,13 @@ mono_runtime_invoke_array (MonoMethod *method, void *obj, MonoArray *params,
                        case MONO_TYPE_R4:
                        case MONO_TYPE_R8:
                        case MONO_TYPE_VALUETYPE:
+                               if (sig->params [i]->byref) {
+                                       /* MS seems to create the objects if a null is passed in */
+                                       if (! ((gpointer *)params->vector)[i])
+                                               ((gpointer*)params->vector)[i] = mono_object_new (mono_domain_get (), mono_class_from_mono_type (sig->params [i]));
+                               }
+                               else
+                                       g_assert (((gpointer*)params->vector) [i]);
                                pa [i] = (char *)(((gpointer *)params->vector)[i]) + sizeof (MonoObject);
                                break;
                        case MONO_TYPE_STRING:
@@ -1317,16 +1612,23 @@ mono_runtime_invoke_array (MonoMethod *method, void *obj, MonoArray *params,
        }
 
        if (!strcmp (method->name, ".ctor") && method->klass != mono_defaults.string_class) {
+               void *o = obj;
                if (!obj) {
                        obj = mono_object_new (mono_domain_get (), method->klass);
                        if (mono_object_class(obj) == mono_defaults.transparent_proxy_class) {
-                               method = mono_marshal_get_remoting_invoke (method->klass->vtable [method->slot]);
+                               method = mono_marshal_get_remoting_invoke (method->slot == -1 ? method : method->klass->vtable [method->slot]);
                        }
+                       if (method->klass->valuetype)
+                               o = mono_object_unbox (obj);
+                       else
+                               o = obj;
                }
-               mono_runtime_invoke (method, obj, pa, exc);
+               mono_runtime_invoke (method, o, pa, exc);
                return obj;
-       } else
+       } else {
+               /* obj must be already unboxed if needed */
                return mono_runtime_invoke (method, obj, pa, exc);
+       }
 }
 
 static void
@@ -1379,31 +1681,14 @@ mono_object_allocate_spec (size_t size, void *gcdescr)
 }
 #endif
 
-/**
- * mono_object_free:
- *
- * Frees the memory used by the object.  Debugging purposes
- * only, as we will have our GC system.
- */
-void
-mono_object_free (MonoObject *o)
-{
-#if HAVE_BOEHM_GC
-       g_error ("mono_object_free called with boehm gc.");
-#else
-       MonoClass *c = o->vtable->klass;
-       
-       memset (o, 0, c->instance_size);
-       free (o);
-#endif
-}
-
 /**
  * mono_object_new:
  * @klass: the class of the object that we want to create
  *
- * Returns: A newly created object whose definition is
- * looked up using @klass
+ * Returns a newly created object whose definition is
+ * looked up using @klass.   This will not invoke any constructors, 
+ * so the consumer of this routine has to invoke any constructors on
+ * its own to initialize the object.
  */
 MonoObject *
 mono_object_new (MonoDomain *domain, MonoClass *klass)
@@ -1468,7 +1753,7 @@ mono_object_new_fast (MonoVTable *vtable)
        if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
                o = mono_object_allocate_spec (vtable->klass->instance_size, vtable);
        } else {
-//             printf("OBJECT: %s.%s.\n", vtable->klass->name_space, vtable->klass->name);
+/*             printf("OBJECT: %s.%s.\n", vtable->klass->name_space, vtable->klass->name); */
                o = mono_object_allocate (vtable->klass->instance_size);
                o->vtable = vtable;
        }
@@ -1488,7 +1773,7 @@ mono_object_new_alloc_specific (MonoVTable *vtable)
        if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
                o = mono_object_allocate_spec (vtable->klass->instance_size, vtable);
        } else {
-//             printf("OBJECT: %s.%s.\n", vtable->klass->name_space, vtable->klass->name);
+/*             printf("OBJECT: %s.%s.\n", vtable->klass->name_space, vtable->klass->name); */
                o = mono_object_allocate (vtable->klass->instance_size);
                o->vtable = vtable;
        }
@@ -1725,7 +2010,7 @@ mono_array_new_specific (MonoVTable *vtable, guint32 n)
        if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
                o = mono_object_allocate_spec (byte_len, vtable);
        } else {
-//             printf("ARRAY: %s.%s.\n", vtable->klass->name_space, vtable->klass->name);
+/*             printf("ARRAY: %s.%s.\n", vtable->klass->name_space, vtable->klass->name); */
                o = mono_object_allocate (byte_len);
                o->vtable = vtable;
        }
@@ -1774,18 +2059,23 @@ mono_string_new_size (MonoDomain *domain, gint32 len)
 {
        MonoString *s;
        MonoVTable *vtable;
+       size_t size = (sizeof (MonoString) + ((len + 1) * 2));
+
+       /* overflow ? can't fit it, can't allocate it! */
+       if (len > size)
+               out_of_memory (-1);
 
        vtable = mono_class_vtable (domain, mono_defaults.string_class);
 
 #if CREATION_SPEEDUP
        if (vtable->gc_descr != GC_NO_DESCRIPTOR)
-               s = mono_object_allocate_spec (sizeof (MonoString) + ((len + 1) * 2), vtable);
+               s = mono_object_allocate_spec (size, vtable);
        else {
-               s = (MonoString*)mono_object_allocate (sizeof (MonoString) + ((len + 1) * 2));
+               s = (MonoString*)mono_object_allocate (size);
                s->object.vtable = vtable;
        }
 #else
-       s = (MonoString*)mono_object_allocate (sizeof (MonoString) + ((len + 1) * 2));
+       s = (MonoString*)mono_object_allocate (size);
        s->object.vtable = vtable;
 #endif
 
@@ -1919,10 +2209,23 @@ mono_value_box (MonoDomain *domain, MonoClass *class, gpointer value)
        return res;
 }
 
+MonoDomain*
+mono_object_get_domain (MonoObject *obj)
+{
+       return mono_object_domain (obj);
+}
+
+MonoClass*
+mono_object_get_class (MonoObject *obj)
+{
+       return mono_object_class (obj);
+}
+
 gpointer
 mono_object_unbox (MonoObject *obj)
 {
        /* add assert for valuetypes? */
+       g_assert (obj->vtable->klass->valuetype);
        return ((char*)obj) + sizeof (MonoObject);
 }
 
@@ -1936,84 +2239,76 @@ mono_object_unbox (MonoObject *obj)
 MonoObject *
 mono_object_isinst (MonoObject *obj, MonoClass *klass)
 {
-       MonoVTable *vt;
-       MonoClass *oklass;
+       if (!klass->inited)
+               mono_class_init (klass);
 
-       if (klass->marshalbyref) 
+       if (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE
                return mono_object_isinst_mbyref (obj, klass);
 
        if (!obj)
                return NULL;
 
-       vt = obj->vtable;
-       oklass = vt->klass;
-
-       if (!klass->inited)
-               mono_class_init (klass);
-
-       if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
-               if ((klass->interface_id <= vt->max_interface_id) &&
-                   (vt->interface_offsets [klass->interface_id] != 0))
-                       return obj;
-               else
-                       return NULL;
-       }
-
-       return mono_class_is_assignable_from (klass, oklass) ? obj : NULL;
+       return mono_class_is_assignable_from (klass, obj->vtable->klass) ? obj : NULL;
 }
 
 MonoObject *
 mono_object_isinst_mbyref (MonoObject *obj, MonoClass *klass)
 {
        MonoVTable *vt;
-       MonoClass *oklass;
 
        if (!obj)
                return NULL;
 
        vt = obj->vtable;
-       oklass = vt->klass;
-
-       if (oklass != klass && oklass == mono_defaults.transparent_proxy_class) {
-               oklass = ((MonoTransparentProxy *)obj)->klass;
+       
+       if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
+               if ((klass->interface_id <= vt->max_interface_id) &&
+                   (vt->interface_offsets [klass->interface_id] != 0))
+                       return obj;
+       }
+       else {
+               MonoClass *oklass = vt->klass;
+               if ((oklass == mono_defaults.transparent_proxy_class))
+                       oklass = ((MonoTransparentProxy *)obj)->remote_class->proxy_class;
+       
                if ((oklass->idepth >= klass->idepth) && (oklass->supertypes [klass->idepth - 1] == klass))
                        return obj;
+       }
 
-               /* Check for IRemotingTypeInfo */
-               if (((MonoTransparentProxy *)obj)->custom_type_info) {
-               
-                       MonoObject *res;
-                       MonoObject *rp = (MonoObject *)((MonoTransparentProxy *)obj)->rp;
-                       MonoClass *rpklass = rp->vtable->klass;
-                       MonoMethod *im = NULL;
-                       gpointer pa [2];
-                       int i;
-               
-                       for (i = 0; i < rpklass->method.count; ++i) {
-                               if (!strcmp ("CanCastTo", rpklass->methods [i]->name) &&
-                                       rpklass->methods [i]->signature->param_count == 2) {
-                                       im = rpklass->methods [i];
-                                       break;
-                               }
+       if (vt->klass == mono_defaults.transparent_proxy_class && ((MonoTransparentProxy *)obj)->custom_type_info) 
+       {
+               MonoDomain *domain = mono_domain_get ();
+               MonoObject *res;
+               MonoObject *rp = (MonoObject *)((MonoTransparentProxy *)obj)->rp;
+               MonoClass *rpklass = mono_defaults.iremotingtypeinfo_class;
+               MonoMethod *im = NULL;
+               gpointer pa [2];
+               int i;
+       
+               for (i = 0; i < rpklass->method.count; ++i) {
+                       if (!strcmp ("CanCastTo", rpklass->methods [i]->name)) {
+                               im = rpklass->methods [i];
+                               break;
                        }
-               
-                       g_assert (im);
-               
-                       pa [0] = mono_type_get_object (mono_domain_get (), &klass->byval_arg);
-                       pa [1] = obj;
-
-                       res = mono_runtime_invoke (im, rp, pa, NULL);
-                       if (*(MonoBoolean *) mono_object_unbox(res))
-                               return obj;
                }
+       
+               im = mono_object_get_virtual_method (rp, im);
+               g_assert (im);
+       
+               pa [0] = mono_type_get_object (domain, &klass->byval_arg);
+               pa [1] = obj;
 
-               return NULL;
+               res = mono_runtime_invoke (im, rp, pa, NULL);
+       
+               if (*(MonoBoolean *) mono_object_unbox(res)) {
+                       /* Update the vtable of the remote type, so it can safely cast to this new type */
+                       mono_upgrade_remote_class (domain, ((MonoTransparentProxy *)obj)->remote_class, klass);
+                       obj->vtable = ((MonoTransparentProxy *)obj)->remote_class->vtable;
+                       return obj;
+               }
        }
 
-       if ((oklass->idepth >= klass->idepth) && (oklass->supertypes [klass->idepth - 1] == klass))
-               return obj;
-       else 
-               return NULL;
+       return NULL;
 }
 
 /**
@@ -2425,7 +2720,7 @@ mono_message_invoke (MonoObject *target, MonoMethodMessage *msg,
        if (target && target->vtable->klass == mono_defaults.transparent_proxy_class) {
 
                MonoTransparentProxy* tp = (MonoTransparentProxy *)target;
-               if (tp->klass->contextbound && tp->rp->context == (MonoObject *) mono_context_get ()) {
+               if (tp->remote_class->proxy_class->contextbound && tp->rp->context == (MonoObject *) mono_context_get ()) {
                        target = tp->rp->unwrapped_server;
                } else {
                        return mono_remoting_invoke ((MonoObject *)tp->rp, msg, exc, out_args);
@@ -2453,7 +2748,7 @@ mono_message_invoke (MonoObject *target, MonoMethodMessage *msg,
                }
        }
 
-       return mono_runtime_invoke_array (method, target, msg->args, exc);
+       return mono_runtime_invoke_array (method, method->klass->valuetype? mono_object_unbox (target): target, msg->args, exc);
 }
 
 void
@@ -2639,6 +2934,7 @@ mono_method_return_message_restore (MonoMethod *method, gpointer *params, MonoAr
                        case MONO_TYPE_CLASS: 
                        case MONO_TYPE_ARRAY:
                        case MONO_TYPE_SZARRAY:
+                       case MONO_TYPE_OBJECT:
                                **((MonoObject ***)params [i]) = (MonoObject *)arg;
                                break;
                        default:
@@ -2681,7 +2977,7 @@ mono_load_remote_field (MonoObject *this, MonoClass *klass, MonoClassField *fiel
        if (!res)
                res = &tmp;
 
-       if (tp->klass->contextbound && tp->rp->context == (MonoObject *) mono_context_get ()) {
+       if (tp->remote_class->proxy_class->contextbound && tp->rp->context == (MonoObject *) mono_context_get ()) {
                mono_field_get_value (tp->rp->unwrapped_server, field, res);
                return res;
        }
@@ -2736,7 +3032,7 @@ mono_load_remote_field_new (MonoObject *this, MonoClass *klass, MonoClassField *
 
        field_class = mono_class_from_mono_type (field->type);
 
-       if (tp->klass->contextbound && tp->rp->context == (MonoObject *) mono_context_get ()) {
+       if (tp->remote_class->proxy_class->contextbound && tp->rp->context == (MonoObject *) mono_context_get ()) {
                gpointer val;
                if (field_class->valuetype) {
                        res = mono_object_new (domain, field_class);
@@ -2805,11 +3101,9 @@ mono_store_remote_field (MonoObject *this, MonoClass *klass, MonoClassField *fie
 
        field_class = mono_class_from_mono_type (field->type);
 
-       if (tp->klass->contextbound && tp->rp->context == (MonoObject *) mono_context_get ()) {
-               if (field_class->valuetype)
-                       mono_field_set_value (tp->rp->unwrapped_server, field, val);
-               else
-                       mono_field_set_value (tp->rp->unwrapped_server, field, *((MonoObject **)val));
+       if (tp->remote_class->proxy_class->contextbound && tp->rp->context == (MonoObject *) mono_context_get ()) {
+               if (field_class->valuetype) mono_field_set_value (tp->rp->unwrapped_server, field, val);
+               else mono_field_set_value (tp->rp->unwrapped_server, field, *((MonoObject **)val));
                return;
        }
 
@@ -2860,11 +3154,9 @@ mono_store_remote_field_new (MonoObject *this, MonoClass *klass, MonoClassField
 
        field_class = mono_class_from_mono_type (field->type);
 
-       if (tp->klass->contextbound && tp->rp->context == (MonoObject *) mono_context_get ()) {
-               if (field_class->valuetype)
-                       mono_field_set_value (tp->rp->unwrapped_server, field, ((gchar *) arg) + sizeof (MonoObject));
-               else
-                       mono_field_set_value (tp->rp->unwrapped_server, field, arg);
+       if (tp->remote_class->proxy_class->contextbound && tp->rp->context == (MonoObject *) mono_context_get ()) {
+               if (field_class->valuetype) mono_field_set_value (tp->rp->unwrapped_server, field, ((gchar *) arg) + sizeof (MonoObject));
+               else mono_field_set_value (tp->rp->unwrapped_server, field, arg);
                return;
        }