Merge pull request #2532 from lambdageek/monoerror-mono_object_new
[mono.git] / mono / mini / jit-icalls.c
index 1979867d513527695cd57a6275c7ff70be634160..9a9f194b9ae86c2773d6a0b787d130315144d7c3 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "jit-icalls.h"
 #include <mono/utils/mono-error-internals.h>
+#include <mono/metadata/threads-types.h>
 
 #ifdef ENABLE_LLVM
 #include "mini-llvm-cpp.h"
@@ -658,6 +659,8 @@ mono_fload_r4_arg (double val)
 MonoArray *
 mono_array_new_va (MonoMethod *cm, ...)
 {
+       MonoError error;
+       MonoArray *arr;
        MonoDomain *domain = mono_domain_get ();
        va_list ap;
        uintptr_t *lengths;
@@ -691,13 +694,22 @@ mono_array_new_va (MonoMethod *cm, ...)
        }
        va_end(ap);
 
-       return mono_array_new_full (domain, cm->klass, lengths, lower_bounds);
+       arr = mono_array_new_full_checked (domain, cm->klass, lengths, lower_bounds, &error);
+
+       if (!mono_error_ok (&error)) {
+               mono_error_set_pending_exception (&error);
+               return NULL;
+       }
+
+       return arr;
 }
 
 /* Specialized version of mono_array_new_va () which avoids varargs */
 MonoArray *
 mono_array_new_1 (MonoMethod *cm, guint32 length)
 {
+       MonoError error;
+       MonoArray *arr;
        MonoDomain *domain = mono_domain_get ();
        uintptr_t lengths [1];
        intptr_t *lower_bounds;
@@ -718,12 +730,21 @@ mono_array_new_1 (MonoMethod *cm, guint32 length)
                lower_bounds = NULL;
        }
 
-       return mono_array_new_full (domain, cm->klass, lengths, lower_bounds);
+       arr = mono_array_new_full_checked (domain, cm->klass, lengths, lower_bounds, &error);
+
+       if (!mono_error_ok (&error)) {
+               mono_error_set_pending_exception (&error);
+               return NULL;
+       }
+
+       return arr;
 }
 
 MonoArray *
 mono_array_new_2 (MonoMethod *cm, guint32 length1, guint32 length2)
 {
+       MonoError error;
+       MonoArray *arr;
        MonoDomain *domain = mono_domain_get ();
        uintptr_t lengths [2];
        intptr_t *lower_bounds;
@@ -745,12 +766,21 @@ mono_array_new_2 (MonoMethod *cm, guint32 length1, guint32 length2)
                lower_bounds = NULL;
        }
 
-       return mono_array_new_full (domain, cm->klass, lengths, lower_bounds);
+       arr = mono_array_new_full_checked (domain, cm->klass, lengths, lower_bounds, &error);
+
+       if (!mono_error_ok (&error)) {
+               mono_error_set_pending_exception (&error);
+               return NULL;
+       }
+
+       return arr;
 }
 
 MonoArray *
 mono_array_new_3 (MonoMethod *cm, guint32 length1, guint32 length2, guint32 length3)
 {
+       MonoError error;
+       MonoArray *arr;
        MonoDomain *domain = mono_domain_get ();
        uintptr_t lengths [3];
        intptr_t *lower_bounds;
@@ -773,12 +803,21 @@ mono_array_new_3 (MonoMethod *cm, guint32 length1, guint32 length2, guint32 leng
                lower_bounds = NULL;
        }
 
-       return mono_array_new_full (domain, cm->klass, lengths, lower_bounds);
+       arr = mono_array_new_full_checked (domain, cm->klass, lengths, lower_bounds, &error);
+
+       if (!mono_error_ok (&error)) {
+               mono_error_set_pending_exception (&error);
+               return NULL;
+       }
+
+       return arr;
 }
 
 MonoArray *
 mono_array_new_4 (MonoMethod *cm, guint32 length1, guint32 length2, guint32 length3, guint32 length4)
 {
+       MonoError error;
+       MonoArray *arr;
        MonoDomain *domain = mono_domain_get ();
        uintptr_t lengths [4];
        intptr_t *lower_bounds;
@@ -802,7 +841,14 @@ mono_array_new_4 (MonoMethod *cm, guint32 length1, guint32 length2, guint32 leng
                lower_bounds = NULL;
        }
 
-       return mono_array_new_full (domain, cm->klass, lengths, lower_bounds);
+       arr = mono_array_new_full_checked (domain, cm->klass, lengths, lower_bounds, &error);
+
+       if (!mono_error_ok (&error)) {
+               mono_error_set_pending_exception (&error);
+               return NULL;
+       }
+
+       return arr;
 }
 
 gpointer
@@ -1081,7 +1127,10 @@ mono_helper_newobj_mscorlib (guint32 idx)
                return NULL;
        }
 
-       return mono_object_new (mono_domain_get (), klass);
+       MonoObject *obj = mono_object_new_checked (mono_domain_get (), klass, &error);
+       if (!mono_error_ok (&error))
+               mono_error_set_pending_exception (&error);
+       return obj;
 }
 
 /*
@@ -1220,11 +1269,13 @@ mono_get_native_calli_wrapper (MonoImage *image, MonoMethodSignature *sig, gpoin
 }
 
 static MonoMethod*
-constrained_gsharedvt_call_setup (gpointer mp, MonoMethod *cmethod, MonoClass *klass, gpointer *this_arg)
+constrained_gsharedvt_call_setup (gpointer mp, MonoMethod *cmethod, MonoClass *klass, gpointer *this_arg, MonoError *error)
 {
        MonoMethod *m;
        int vt_slot, iface_offset;
 
+       mono_error_init (error);
+
        if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
                MonoObject *this_obj;
 
@@ -1280,11 +1331,17 @@ constrained_gsharedvt_call_setup (gpointer mp, MonoMethod *cmethod, MonoClass *k
 MonoObject*
 mono_gsharedvt_constrained_call (gpointer mp, MonoMethod *cmethod, MonoClass *klass, gboolean deref_arg, gpointer *args)
 {
+       MonoError error;
        MonoMethod *m;
        gpointer this_arg;
        gpointer new_args [16];
 
-       m = constrained_gsharedvt_call_setup (mp, cmethod, klass, &this_arg);
+       m = constrained_gsharedvt_call_setup (mp, cmethod, klass, &this_arg, &error);
+       if (!mono_error_ok (&error)) {
+               mono_error_set_pending_exception (&error);
+               return NULL;
+       }
+
        if (!m)
                return NULL;
        if (args && deref_arg) {
@@ -1318,19 +1375,38 @@ mono_generic_class_init (MonoVTable *vtable)
 gpointer
 mono_fill_class_rgctx (MonoVTable *vtable, int index)
 {
-       return mono_class_fill_runtime_generic_context (vtable, index);
+       MonoError error;
+       gpointer res;
+
+       res = mono_class_fill_runtime_generic_context (vtable, index, &error);
+       if (!mono_error_ok (&error)) {
+               mono_error_set_pending_exception (&error);
+               return NULL;
+       }
+       return res;
 }
 
 gpointer
 mono_fill_method_rgctx (MonoMethodRuntimeGenericContext *mrgctx, int index)
 {
-       return mono_method_fill_runtime_generic_context (mrgctx, index);
+       MonoError error;
+       gpointer res;
+
+       res = mono_method_fill_runtime_generic_context (mrgctx, index, &error);
+       if (!mono_error_ok (&error)) {
+               mono_error_set_pending_exception (&error);
+               return NULL;
+       }
+       return res;
 }
 
 /*
  * resolve_iface_call:
  *
  *   Return the executable code for the iface method IMT_METHOD called on THIS.
+ * This function is called on a slowpath, so it doesn't need to be fast.
+ * This returns an ftnptr by returning the address part, and the arg in the OUT_ARG
+ * out parameter.
  */
 static gpointer
 resolve_iface_call (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg, gboolean caller_gsharedvt)
@@ -1341,8 +1417,6 @@ resolve_iface_call (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method,
        gpointer addr, compiled_method, aot_addr;
        gboolean need_rgctx_tramp = FALSE, need_unbox_tramp = FALSE;
 
-       // FIXME: Optimize this
-
        if (!this_obj)
                /* The caller will handle it */
                return NULL;
@@ -1380,12 +1454,6 @@ resolve_iface_call (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method,
        return addr;
 }
 
-gpointer
-mono_resolve_iface_call (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg)
-{
-       return resolve_iface_call (this_obj, imt_slot, imt_method, out_arg, FALSE);
-}
-
 gpointer
 mono_resolve_iface_call_gsharedvt (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg)
 {
@@ -1413,6 +1481,9 @@ is_generic_method_definition (MonoMethod *m)
  * resolve_vcall:
  *
  *   Return the executable code for calling vt->vtable [slot].
+ * This function is called on a slowpath, so it doesn't need to be fast.
+ * This returns an ftnptr by returning the address part, and the arg in the OUT_ARG
+ * out parameter.
  */
 static gpointer
 resolve_vcall (MonoVTable *vt, int slot, MonoMethod *imt_method, gpointer *out_arg, gboolean gsharedvt)
@@ -1421,8 +1492,6 @@ resolve_vcall (MonoVTable *vt, int slot, MonoMethod *imt_method, gpointer *out_a
        gpointer addr, compiled_method;
        gboolean need_unbox_tramp = FALSE;
 
-       // FIXME: Optimize this
-
        /* Same as in common_call_trampoline () */
 
        /* Avoid loading metadata or creating a generic vtable if possible */
@@ -1468,29 +1537,11 @@ resolve_vcall (MonoVTable *vt, int slot, MonoMethod *imt_method, gpointer *out_a
        addr = compiled_method = mono_compile_method (m);
        g_assert (addr);
 
-       addr = mini_add_method_wrappers_llvmonly (m, addr, FALSE, need_unbox_tramp, out_arg);
-
-       // FIXME: Unify this with mono_resolve_iface_call
-
-       if (gsharedvt) {
-               /*
-                * The callee uses the gsharedvt calling convention, have to add an out wrapper.
-                */
-               g_assert (out_arg);
-               g_assert (*out_arg);
-
-               gpointer out_wrapper = mini_get_gsharedvt_wrapper (FALSE, NULL, mono_method_signature (imt_method), NULL, -1, FALSE);
-               gpointer *out_wrapper_arg = mini_create_llvmonly_ftndesc (addr, *out_arg);
-
-               addr = out_wrapper;
-               *out_arg = out_wrapper_arg;
-       }
+       addr = mini_add_method_wrappers_llvmonly (m, addr, gsharedvt, need_unbox_tramp, out_arg);
 
        if (!gsharedvt && generic_virtual) {
                // FIXME: This wastes memory since add_generic_virtual_invocation ignores it in a lot of cases
-               gpointer *ftndesc = mono_domain_alloc0 (mono_domain_get (), 2 * sizeof (gpointer));
-               ftndesc [0] = addr;
-               ftndesc [1] = *out_arg;
+               MonoFtnDesc *ftndesc = mini_create_llvmonly_ftndesc (mono_domain_get (), addr, out_arg);
 
                mono_method_add_generic_virtual_invocation (mono_domain_get (),
                                                                                                        vt, vt->vtable + slot,
@@ -1501,28 +1552,20 @@ resolve_vcall (MonoVTable *vt, int slot, MonoMethod *imt_method, gpointer *out_a
 }
 
 gpointer
-mono_resolve_vcall (MonoObject *this_obj, int slot, MonoMethod *imt_method, gpointer *out_rgctx_arg)
-{
-       g_assert (this_obj);
-
-       return resolve_vcall (this_obj->vtable, slot, imt_method, out_rgctx_arg, FALSE);
-}
-
-gpointer
-mono_resolve_vcall_gsharedvt (MonoObject *this_obj, int slot, MonoMethod *imt_method, gpointer *out_rgctx_arg)
+mono_resolve_vcall_gsharedvt (MonoObject *this_obj, int slot, MonoMethod *imt_method, gpointer *out_arg)
 {
        g_assert (this_obj);
 
-       return resolve_vcall (this_obj->vtable, slot, imt_method, out_rgctx_arg, TRUE);
+       return resolve_vcall (this_obj->vtable, slot, imt_method, out_arg, TRUE);
 }
 
 /*
  * mono_resolve_generic_virtual_call:
  *
- *   Resolve a generic virtual call. This returns an ftndesc.
+ *   Resolve a generic virtual call.
  * This function is called on a slowpath, so it doesn't need to be fast.
  */
-gpointer
+MonoFtnDesc*
 mono_resolve_generic_virtual_call (MonoVTable *vt, int slot, MonoMethod *generic_virtual)
 {
        MonoMethod *m;
@@ -1531,7 +1574,7 @@ mono_resolve_generic_virtual_call (MonoVTable *vt, int slot, MonoMethod *generic
        MonoError error;
        MonoGenericContext context = { NULL, NULL };
        MonoMethod *declaring;
-       gpointer out_arg = NULL;
+       gpointer arg = NULL;
 
        m = mono_class_get_vtable_entry (vt->klass, slot);
 
@@ -1560,16 +1603,14 @@ mono_resolve_generic_virtual_call (MonoVTable *vt, int slot, MonoMethod *generic
        addr = compiled_method = mono_compile_method (m);
        g_assert (addr);
 
-       addr = mini_add_method_wrappers_llvmonly (m, addr, FALSE, need_unbox_tramp, &out_arg);
+       addr = mini_add_method_wrappers_llvmonly (m, addr, FALSE, need_unbox_tramp, &arg);
 
        /*
         * This wastes memory but the memory usage is bounded since
         * mono_method_add_generic_virtual_invocation () eventually builds an imt thunk for
         * this vtable slot so we are not called any more for this instantiation.
         */
-       gpointer *ftndesc = mono_domain_alloc0 (mono_domain_get (), 2 * sizeof (gpointer));
-       ftndesc [0] = addr;
-       ftndesc [1] = out_arg;
+       MonoFtnDesc *ftndesc = mini_create_llvmonly_ftndesc (mono_domain_get (), addr, arg);
 
        mono_method_add_generic_virtual_invocation (mono_domain_get (),
                                                                                                vt, vt->vtable + slot,
@@ -1580,23 +1621,22 @@ mono_resolve_generic_virtual_call (MonoVTable *vt, int slot, MonoMethod *generic
 /*
  * mono_resolve_generic_virtual_call:
  *
- *   Resolve a generic virtual call on interfaces. This returns an ftndesc.
+ *   Resolve a generic virtual/variant iface call on interfaces.
  * This function is called on a slowpath, so it doesn't need to be fast.
  */
-gpointer
+MonoFtnDesc*
 mono_resolve_generic_virtual_iface_call (MonoVTable *vt, int imt_slot, MonoMethod *generic_virtual)
 {
        MonoMethod *m, *variant_iface;
        gpointer addr, aot_addr, compiled_method;
        gboolean need_unbox_tramp = FALSE;
        gboolean need_rgctx_tramp;
-       gpointer out_arg = NULL;
+       gpointer arg = NULL;
        gpointer *imt;
 
        imt = (gpointer*)vt - MONO_IMT_SIZE;
 
        mini_resolve_imt_method (vt, imt + imt_slot, generic_virtual, &m, &aot_addr, &need_rgctx_tramp, &variant_iface);
-       g_assert (!variant_iface);
 
        if (vt->klass->valuetype)
                need_unbox_tramp = TRUE;
@@ -1605,20 +1645,18 @@ mono_resolve_generic_virtual_iface_call (MonoVTable *vt, int imt_slot, MonoMetho
        addr = compiled_method = mono_compile_method (m);
        g_assert (addr);
 
-       addr = mini_add_method_wrappers_llvmonly (m, addr, FALSE, need_unbox_tramp, &out_arg);
+       addr = mini_add_method_wrappers_llvmonly (m, addr, FALSE, need_unbox_tramp, &arg);
 
        /*
         * This wastes memory but the memory usage is bounded since
         * mono_method_add_generic_virtual_invocation () eventually builds an imt thunk for
         * this vtable slot so we are not called any more for this instantiation.
         */
-       gpointer *ftndesc = mono_domain_alloc0 (mono_domain_get (), 2 * sizeof (gpointer));
-       ftndesc [0] = addr;
-       ftndesc [1] = out_arg;
+       MonoFtnDesc *ftndesc = mini_create_llvmonly_ftndesc (mono_domain_get (), addr, arg);
 
        mono_method_add_generic_virtual_invocation (mono_domain_get (),
                                                                                                vt, imt + imt_slot,
-                                                                                               generic_virtual, ftndesc);
+                                                                                               variant_iface ? variant_iface : generic_virtual, ftndesc);
        return ftndesc;
 }
 
@@ -1647,41 +1685,43 @@ mono_init_vtable_slot (MonoVTable *vtable, int slot)
 }
 
 /*
- * mono_init_delegate:
+ * mono_llvmonly_init_delegate:
  *
  *   Initialize a MonoDelegate object.
  * Similar to mono_delegate_ctor ().
  */
 void
-mono_init_delegate (MonoDelegate *del, MonoObject *target, MonoMethod *method)
+mono_llvmonly_init_delegate (MonoDelegate *del)
 {
-       MONO_OBJECT_SETREF (del, target, target);
-       del->method = method;
-       del->method_ptr = mono_compile_method (method);
+       MonoFtnDesc *ftndesc = *(MonoFtnDesc**)del->method_code;
 
-       del->method_ptr = mini_add_method_wrappers_llvmonly (method, del->method_ptr, FALSE, FALSE, &del->rgctx);
-       if (!del->rgctx) {
-               if (mono_method_needs_static_rgctx_invoke (method, FALSE))
-                       del->rgctx = mini_method_get_rgctx (method);
+       /*
+        * We store a MonoFtnDesc in del->method_code.
+        * It would be better to store an ftndesc in del->method_ptr too,
+        * but we don't have a a structure which could own its memory.
+        */
+       if (G_UNLIKELY (!ftndesc)) {
+               gpointer addr = mono_compile_method (del->method);
+               gpointer arg = mini_get_delegate_arg (del->method, addr);
+
+               ftndesc = mini_create_llvmonly_ftndesc (mono_domain_get (), addr, arg);
+               mono_memory_barrier ();
+               *del->method_code = (gpointer)ftndesc;
        }
+       del->method_ptr = ftndesc->addr;
+       del->extra_arg = ftndesc->arg;
 }
 
 void
-mono_init_delegate_virtual (MonoDelegate *del, MonoObject *target, MonoMethod *method)
+mono_llvmonly_init_delegate_virtual (MonoDelegate *del, MonoObject *target, MonoMethod *method)
 {
        g_assert (target);
 
        method = mono_object_get_virtual_method (target, method);
 
-       MONO_OBJECT_SETREF (del, target, target);
        del->method = method;
        del->method_ptr = mono_compile_method (method);
-
-       del->method_ptr = mini_add_method_wrappers_llvmonly (method, del->method_ptr, FALSE, FALSE, &del->rgctx);
-       if (!del->rgctx) {
-               if (mono_method_needs_static_rgctx_invoke (method, FALSE))
-                       del->rgctx = mini_method_get_rgctx (method);
-       }
+       del->extra_arg = mini_get_delegate_arg (del->method, del->method_ptr);
 }
 
 MonoObject*
@@ -1690,6 +1730,12 @@ mono_get_assembly_object (MonoImage *image)
        return (MonoObject*)mono_assembly_get_object (mono_domain_get (), image->assembly);
 }
 
+MonoObject*
+mono_get_method_object (MonoMethod *method)
+{
+       return (MonoObject*)mono_method_get_object (mono_domain_get (), method, method->klass);
+}
+
 double
 mono_ckfinite (double d)
 {
@@ -1697,3 +1743,105 @@ mono_ckfinite (double d)
                mono_set_pending_exception (mono_get_exception_arithmetic ());
        return d;
 }
+
+void
+mono_llvmonly_set_calling_assembly (MonoImage *image)
+{
+       MonoJitTlsData *jit_tls = NULL;
+
+       jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
+       g_assert (jit_tls);
+       jit_tls->calling_image = image;
+}
+
+
+static gboolean
+get_executing (MonoMethod *m, gint32 no, gint32 ilo, gboolean managed, gpointer data)
+{
+       MonoMethod **dest = (MonoMethod **)data;
+
+       /* skip unmanaged frames */
+       if (!managed)
+               return FALSE;
+
+       if (!(*dest)) {
+               if (!strcmp (m->klass->name_space, "System.Reflection"))
+                       return FALSE;
+               *dest = m;
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static gboolean
+get_caller_no_reflection (MonoMethod *m, gint32 no, gint32 ilo, gboolean managed, gpointer data)
+{
+       MonoMethod **dest = (MonoMethod **)data;
+
+       /* skip unmanaged frames */
+       if (!managed)
+               return FALSE;
+
+       if (m->wrapper_type != MONO_WRAPPER_NONE)
+               return FALSE;
+
+       if (m->klass->image == mono_defaults.corlib && !strcmp (m->klass->name_space, "System.Reflection"))
+               return FALSE;
+
+       if (m == *dest) {
+               *dest = NULL;
+               return FALSE;
+       }
+       if (!(*dest)) {
+               *dest = m;
+               return TRUE;
+       }
+       return FALSE;
+}
+
+MonoObject*
+mono_llvmonly_get_calling_assembly (void)
+{
+       MonoJitTlsData *jit_tls = NULL;
+       MonoMethod *m;
+       MonoMethod *dest;
+       MonoAssembly *assembly;
+
+       dest = NULL;
+       mono_stack_walk_no_il (get_executing, &dest);
+       m = dest;
+       mono_stack_walk_no_il (get_caller_no_reflection, &dest);
+
+       if (!dest) {
+               /* Fall back to TLS */
+               jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
+               g_assert (jit_tls);
+               if (!jit_tls->calling_image) {
+                       mono_set_pending_exception (mono_get_exception_not_supported ("Stack walks are not supported on this platform."));
+                       return NULL;
+               }
+               assembly = jit_tls->calling_image->assembly;
+       } else {
+               assembly = dest->klass->image->assembly;
+       }
+       return (MonoObject*)mono_assembly_get_object (mono_domain_get (), jit_tls->calling_image->assembly);
+}
+
+/*
+ * mono_interruption_checkpoint_from_trampoline:
+ *
+ *   Check whenever the thread has a pending exception, and throw it
+ * if needed.
+ * Architectures should move away from calling this function and
+ * instead call mono_thread_force_interruption_checkpoint_noraise (),
+ * rewrind to the parent frame, and throw the exception normally.
+ */
+void
+mono_interruption_checkpoint_from_trampoline (void)
+{
+       MonoException *ex;
+
+       ex = mono_thread_force_interruption_checkpoint_noraise ();
+       if (ex)
+               mono_raise_exception (ex);
+}