#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"
MonoArray *
mono_array_new_va (MonoMethod *cm, ...)
{
+ MonoError error;
+ MonoArray *arr;
MonoDomain *domain = mono_domain_get ();
va_list ap;
uintptr_t *lengths;
}
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;
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;
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;
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;
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
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;
}
/*
}
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;
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) {
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)
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;
target, addr);
}
- *vtable_slot = addr;
-
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)
{
}
/*
- * mono_resolve_vcall:
+ * resolve_vcall:
*
- * Return the executable code for calling this_obj->vtable [slot].
+ * 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 (MonoObject *this_obj, int slot, MonoMethod *imt_method, gpointer *out_arg, gboolean gsharedvt)
+resolve_vcall (MonoVTable *vt, int slot, MonoMethod *imt_method, gpointer *out_arg, gboolean gsharedvt)
{
- MonoVTable *vt;
MonoMethod *m, *generic_virtual = NULL;
- gpointer *vtable_slot;
gpointer addr, compiled_method;
gboolean need_unbox_tramp = FALSE;
- // FIXME: Optimize this
-
- if (!this_obj)
- /* The caller will handle it */
- return NULL;
-
- vt = this_obj->vtable;
-
- vtable_slot = &(vt->vtable [slot]);
-
/* Same as in common_call_trampoline () */
/* Avoid loading metadata or creating a generic vtable if possible */
addr = mono_aot_get_method_from_vt_slot (mono_domain_get (), vt, slot);
- if (addr && !vt->klass->valuetype) {
- if (mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot))
- *vtable_slot = addr;
-
+ if (addr && !vt->klass->valuetype)
return mono_create_ftnptr (mono_domain_get (), addr);
- }
m = mono_class_get_vtable_entry (vt->klass, slot);
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, gsharedvt, need_unbox_tramp, out_arg);
- // FIXME: Unify this with mono_resolve_iface_call
+ if (!gsharedvt && generic_virtual) {
+ // FIXME: This wastes memory since add_generic_virtual_invocation ignores it in a lot of cases
+ MonoFtnDesc *ftndesc = mini_create_llvmonly_ftndesc (mono_domain_get (), addr, out_arg);
- *vtable_slot = addr;
-
- 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;
+ mono_method_add_generic_virtual_invocation (mono_domain_get (),
+ vt, vt->vtable + slot,
+ generic_virtual, ftndesc);
}
return addr;
}
gpointer
-mono_resolve_vcall (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_arg, TRUE);
+}
+
+/*
+ * mono_resolve_generic_virtual_call:
+ *
+ * Resolve a generic virtual call.
+ * This function is called on a slowpath, so it doesn't need to be fast.
+ */
+MonoFtnDesc*
+mono_resolve_generic_virtual_call (MonoVTable *vt, int slot, MonoMethod *generic_virtual)
+{
+ MonoMethod *m;
+ gpointer addr, compiled_method;
+ gboolean need_unbox_tramp = FALSE;
+ MonoError error;
+ MonoGenericContext context = { NULL, NULL };
+ MonoMethod *declaring;
+ gpointer arg = NULL;
+
+ m = mono_class_get_vtable_entry (vt->klass, slot);
+
+ g_assert (is_generic_method_definition (m));
+
+ if (m->is_inflated)
+ declaring = mono_method_get_declaring_generic_method (m);
+ else
+ declaring = m;
+
+ if (m->klass->generic_class)
+ context.class_inst = m->klass->generic_class->context.class_inst;
+ else
+ g_assert (!m->klass->generic_container);
+
+ g_assert (generic_virtual->is_inflated);
+ context.method_inst = ((MonoMethodInflated*)generic_virtual)->context.method_inst;
+
+ m = mono_class_inflate_generic_method_checked (declaring, &context, &error);
+ g_assert (mono_error_ok (&error));
+
+ if (vt->klass->valuetype)
+ need_unbox_tramp = TRUE;
+
+ // FIXME: This can throw exceptions
+ addr = compiled_method = mono_compile_method (m);
+ g_assert (addr);
+
+ 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.
+ */
+ MonoFtnDesc *ftndesc = mini_create_llvmonly_ftndesc (mono_domain_get (), addr, arg);
+
+ mono_method_add_generic_virtual_invocation (mono_domain_get (),
+ vt, vt->vtable + slot,
+ generic_virtual, ftndesc);
+ return ftndesc;
+}
+
+/*
+ * mono_resolve_generic_virtual_call:
+ *
+ * Resolve a generic virtual/variant iface call on interfaces.
+ * This function is called on a slowpath, so it doesn't need to be fast.
+ */
+MonoFtnDesc*
+mono_resolve_generic_virtual_iface_call (MonoVTable *vt, int imt_slot, MonoMethod *generic_virtual)
{
- return resolve_vcall (this_obj, slot, imt_method, out_rgctx_arg, FALSE);
+ MonoMethod *m, *variant_iface;
+ gpointer addr, aot_addr, compiled_method;
+ gboolean need_unbox_tramp = FALSE;
+ gboolean need_rgctx_tramp;
+ 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);
+
+ if (vt->klass->valuetype)
+ need_unbox_tramp = TRUE;
+
+ // FIXME: This can throw exceptions
+ addr = compiled_method = mono_compile_method (m);
+ g_assert (addr);
+
+ 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.
+ */
+ MonoFtnDesc *ftndesc = mini_create_llvmonly_ftndesc (mono_domain_get (), addr, arg);
+
+ mono_method_add_generic_virtual_invocation (mono_domain_get (),
+ vt, imt + imt_slot,
+ variant_iface ? variant_iface : generic_virtual, ftndesc);
+ return ftndesc;
}
+/*
+ * mono_init_vtable_slot:
+ *
+ * Initialize slot SLOT of VTABLE.
+ * Return the contents of the vtable slot.
+ */
gpointer
-mono_resolve_vcall_gsharedvt (MonoObject *this_obj, int slot, MonoMethod *imt_method, gpointer *out_rgctx_arg)
+mono_init_vtable_slot (MonoVTable *vtable, int slot)
{
- return resolve_vcall (this_obj, slot, imt_method, out_rgctx_arg, TRUE);
+ gpointer arg = NULL;
+ gpointer addr;
+ gpointer *ftnptr;
+
+ addr = resolve_vcall (vtable, slot, NULL, &arg, FALSE);
+ ftnptr = mono_domain_alloc0 (vtable->domain, 2 * sizeof (gpointer));
+ ftnptr [0] = addr;
+ ftnptr [1] = arg;
+ mono_memory_barrier ();
+
+ vtable->vtable [slot] = ftnptr;
+
+ return ftnptr;
}
/*
- * 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*
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)
{
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);
+}