#endif
typedef struct {
+ GHashTable *delegate_invoke_cache;
+ GHashTable *delegate_invoke_virtual_cache;
/*
* indexed by MonoMethodSignature
* Protected by the marshal lock
*/
- GHashTable *delegate_invoke_cache;
GHashTable *delegate_begin_invoke_cache;
GHashTable *delegate_end_invoke_cache;
GHashTable *runtime_invoke_signature_cache;
mono_mb_emit_restore_result (mb, sig->ret);
}
+#define MONO_TYPE_IS_PRIMITIVE(t) ((!m_type_is_byref ((t)) && ((((t)->type >= MONO_TYPE_BOOLEAN && (t)->type <= MONO_TYPE_R8) || ((t)->type >= MONO_TYPE_I && (t)->type <= MONO_TYPE_U)))))
+
static void
-emit_delegate_invoke_internal_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodSignature *invoke_sig, MonoMethodSignature *target_method_sig, gboolean static_method_with_first_arg_bound, gboolean callvirt, gboolean closed_over_null, MonoMethod *method, MonoMethod *target_method, MonoClass *target_class, MonoGenericContext *ctx, MonoGenericContainer *container)
+emit_delegate_invoke_internal_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodSignature *invoke_sig, MonoMethodSignature *target_method_sig, gboolean static_method_with_first_arg_bound, gboolean callvirt, gboolean closed_over_null, MonoMethod *method, MonoMethod *target_method, MonoGenericContext *ctx, MonoGenericContainer *container)
{
int local_i, local_len, local_delegates, local_d, local_target, local_res = 0;
int pos0, pos1, pos2;
if (callvirt) {
if (!closed_over_null) {
- for (i = 1; i <= sig->param_count; ++i)
+ for (i = 1; i <= sig->param_count; ++i) {
mono_mb_emit_ldarg (mb, i);
- mono_mb_emit_ldarg (mb, 1);
+ if (i == 1 && (MONO_TYPE_ISSTRUCT (sig->params [0]) || MONO_TYPE_IS_PRIMITIVE (sig->params [0])))
+ mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type_internal (sig->params [0]));
+ }
+ if (MONO_TYPE_ISSTRUCT (sig->params [0]) || MONO_TYPE_IS_PRIMITIVE (sig->params [0]))
+ mono_mb_emit_ldarg_addr (mb, 1);
+ else
+ mono_mb_emit_ldarg (mb, 1);
mono_mb_emit_ldarg (mb, 0);
mono_mb_emit_icall (mb, mono_get_addr_compiled_method);
mono_mb_emit_op (mb, CEE_CALLI, target_method_sig);
GHashTable *cache;
gpointer cache_key = NULL;
char *name;
- MonoClass *target_class = NULL;
gboolean closed_over_null = FALSE;
MonoGenericContext *ctx = NULL;
MonoGenericContainer *container = NULL;
* call is made to that method with the first delegate argument as this. This is
* a non-documented .NET feature.
*/
- if (callvirt) {
+ if (callvirt)
subtype = WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL;
- if (target_method->is_inflated) {
- ERROR_DECL (error);
- MonoType *target_type;
-
- g_assert (method->signature->hasthis);
- target_type = mono_class_inflate_generic_type_checked (method->signature->params [0],
- mono_method_get_context (method), error);
- mono_error_assert_ok (error); /* FIXME don't swallow the error */
- target_class = mono_class_from_mono_type_internal (target_type);
- } else {
- target_class = target_method->klass;
- }
-
- closed_over_null = sig->param_count == mono_method_signature_internal (target_method)->param_count;
-
- /*
- * We don't want to use target_method's signature because it can be freed early
- */
- target_method_sig = mono_method_signature_internal (target_method);
- }
if (static_method_with_first_arg_bound) {
subtype = WRAPPER_SUBTYPE_DELEGATE_INVOKE_BOUND;
/*
* For generic delegates, create a generic wrapper, and return an instance to help AOT.
*/
- if (method->is_inflated && subtype == WRAPPER_SUBTYPE_NONE) {
+ if (method->is_inflated && (subtype == WRAPPER_SUBTYPE_NONE || subtype == WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL)) {
ctx = &((MonoMethodInflated*)method)->context;
method = ((MonoMethodInflated*)method)->declaring;
invoke_sig = sig = mono_signature_no_pinvoke (method);
}
+ if (subtype == WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL) {
+ /*
+ * We don't want to use target_method's signature because it can be freed early
+ */
+ target_method_sig = mono_metadata_signature_dup_delegate_invoke_to_target (invoke_sig);
+
+ closed_over_null = sig->param_count == target_method_sig->param_count;
+ }
+
/*
* Check cache
*/
if (ctx) {
- cache = get_cache (&((MonoMethodInflated*)orig_method)->owner->wrapper_caches.delegate_invoke_cache, mono_aligned_addr_hash, NULL);
+ if (callvirt)
+ cache = get_cache (&((MonoMethodInflated*)orig_method)->owner->wrapper_caches.delegate_invoke_virtual_cache, mono_aligned_addr_hash, NULL);
+ else
+ cache = get_cache (&((MonoMethodInflated*)orig_method)->owner->wrapper_caches.delegate_invoke_cache, mono_aligned_addr_hash, NULL);
res = check_generic_delegate_wrapper_cache (cache, orig_method, method, ctx);
if (res)
return res;
/* FIXME: Other subtypes */
mb->mem_manager = m_method_get_mem_manager (method);
- get_marshal_cb ()->emit_delegate_invoke_internal (mb, sig, invoke_sig, target_method_sig, static_method_with_first_arg_bound, callvirt, closed_over_null, method, target_method, target_class, ctx, container);
+ get_marshal_cb ()->emit_delegate_invoke_internal (mb, sig, invoke_sig, target_method_sig, static_method_with_first_arg_bound, callvirt, closed_over_null, method, target_method, ctx, container);
get_marshal_cb ()->mb_skip_visibility (mb);
mono_wrapper_caches_free (MonoWrapperCaches *cache)
{
free_hash (cache->delegate_invoke_cache);
+ free_hash (cache->delegate_invoke_virtual_cache);
free_hash (cache->delegate_begin_invoke_cache);
free_hash (cache->delegate_end_invoke_cache);
free_hash (cache->delegate_bound_static_invoke_cache);
void (*emit_runtime_invoke_dynamic) (MonoMethodBuilder *mb);
void (*emit_delegate_begin_invoke) (MonoMethodBuilder *mb, MonoMethodSignature *sig);
void (*emit_delegate_end_invoke) (MonoMethodBuilder *mb, MonoMethodSignature *sig);
- void (*emit_delegate_invoke_internal) (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodSignature *invoke_sig, MonoMethodSignature *target_method_sig, gboolean static_method_with_first_arg_bound, gboolean callvirt, gboolean closed_over_null, MonoMethod *method, MonoMethod *target_method, MonoClass *target_class, MonoGenericContext *ctx, MonoGenericContainer *container);
+ void (*emit_delegate_invoke_internal) (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodSignature *invoke_sig, MonoMethodSignature *target_method_sig, gboolean static_method_with_first_arg_bound, gboolean callvirt, gboolean closed_over_null, MonoMethod *method, MonoMethod *target_method, MonoGenericContext *ctx, MonoGenericContainer *container);
void (*emit_synchronized_wrapper) (MonoMethodBuilder *mb, MonoMethod *method, MonoGenericContext *ctx, MonoGenericContainer *container, MonoMethod *enter_method, MonoMethod *exit_method, MonoMethod *gettypefromhandle_method);
void (*emit_unbox_wrapper) (MonoMethodBuilder *mb, MonoMethod *method);
void (*emit_array_accessor_wrapper) (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *sig, MonoGenericContext *ctx);
MonoMethodSignature *mono_metadata_signature_dup_mempool (MonoMemPool *mp, MonoMethodSignature *sig);
MonoMethodSignature *mono_metadata_signature_dup_mem_manager (MonoMemoryManager *mem_manager, MonoMethodSignature *sig);
MonoMethodSignature *mono_metadata_signature_dup_add_this (MonoImage *image, MonoMethodSignature *sig, MonoClass *klass);
+MonoMethodSignature *mono_metadata_signature_dup_delegate_invoke_to_target (MonoMethodSignature *sig);
MonoGenericInst *
mono_get_shared_generic_inst (MonoGenericContainer *container);
return mono_metadata_signature_dup_full (NULL, sig);
}
+/**
+ * mono_metadata_signature_dup_delegate_invoke_to_target:
+ * \param sig method signature
+ *
+ * Duplicate an existing \c MonoMethodSignature but removes first param from it so it can
+ * be used as signature for a delegate target method.
+ * This is a Mono runtime internal function.
+ *
+ * \returns the new \c MonoMethodSignature structure.
+ */
+MonoMethodSignature*
+mono_metadata_signature_dup_delegate_invoke_to_target (MonoMethodSignature *sig)
+{
+ MonoMethodSignature *res = mono_metadata_signature_dup_full (NULL, sig);
+
+ for (int i = 0 ; i < sig->param_count - 1; i ++) {
+ res->params [i] = sig->params [i + 1];
+ }
+ res->param_count --;
+
+ return res;
+}
+
/*
* mono_metadata_signature_size:
*
encode_klass_ref (acfg, method->klass, p, &p);
} else {
MonoMethodSignature *sig = mono_method_signature_internal (method);
- WrapperInfo *wrapper_info = mono_marshal_get_wrapper_info (method);
encode_value (0, p, &p);
if (method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE)
- encode_value (wrapper_info ? wrapper_info->subtype : 0, p, &p);
- encode_signature (acfg, sig, p, &p);
+ encode_value (info ? info->subtype : 0, p, &p);
+
+ if (info && info->subtype == WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL)
+ encode_klass_ref (acfg, info->d.delegate_invoke.method->klass, p, &p);
+ else
+ encode_signature (acfg, sig, p, &p);
}
break;
}
return can_marshal;
}
+/* Create a ref shared instantiation */
+static void
+create_ref_shared_inst (MonoAotCompile *acfg, MonoMethod *method, MonoGenericContext *ctx)
+{
+ MonoGenericContext shared_context;
+ MonoType **args;
+ MonoGenericInst *inst;
+ MonoGenericContainer *container;
+
+ memset (ctx, 0, sizeof (MonoGenericContext));
+
+ if (mono_class_is_gtd (method->klass)) {
+ shared_context = mono_class_get_generic_container (method->klass)->context;
+ inst = shared_context.class_inst;
+
+ args = g_new0 (MonoType*, inst->type_argc);
+ for (guint i = 0; i < inst->type_argc; ++i)
+ args [i] = mono_get_object_type ();
+ ctx->class_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
+ g_free (args);
+ }
+ if (method->is_generic) {
+ container = mono_method_get_generic_container (method);
+ g_assert (!container->is_anonymous && container->is_method);
+ shared_context = container->context;
+ inst = shared_context.method_inst;
+
+ args = g_new0 (MonoType*, inst->type_argc);
+ for (int i = 0; i < container->type_argc; ++i)
+ args [i] = mono_get_object_type ();
+ ctx->method_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
+ g_free (args);
+ }
+}
+
static void
create_gsharedvt_inst (MonoAotCompile *acfg, MonoMethod *method, MonoGenericContext *ctx)
{
if (!m_class_is_delegate (klass) || klass == mono_defaults.delegate_class || klass == mono_defaults.multicastdelegate_class)
continue;
+ method = mono_get_delegate_invoke_internal (klass);
+ if (mono_class_is_gtd (klass)) {
+ MonoGenericContext ctx;
+ MonoMethod *inst, *gshared;
+
+ create_ref_shared_inst (acfg, method, &ctx);
+
+ inst = mono_class_inflate_generic_method_checked (method, &ctx, error);
+ g_assert (is_ok (error)); /* FIXME don't swallow the error */
+
+ sig = mono_method_signature_internal (method);
+ if (sig->param_count && !m_class_is_byreflike (mono_class_from_mono_type_internal (sig->params [0])) && !m_type_is_byref (sig->params [0])) {
+ m = mono_marshal_get_delegate_invoke_internal (inst, TRUE, FALSE, NULL);
+
+ gshared = mini_get_shared_method_full (m, SHARE_MODE_NONE, error);
+ mono_error_assert_ok (error);
+
+ add_extra_method (acfg, gshared);
+ }
+ }
+
if (!mono_class_is_gtd (klass)) {
- method = mono_get_delegate_invoke_internal (klass);
m = mono_marshal_get_delegate_invoke (method, NULL);
add_method (acfg, m);
+ sig = mono_method_signature_internal (method);
+ if (sig->param_count && !m_class_is_byreflike (mono_class_from_mono_type_internal (sig->params [0])) && !m_type_is_byref (sig->params [0])) {
+ m = mono_marshal_get_delegate_invoke_internal (method, TRUE, FALSE, NULL);
+ add_method (acfg, m);
+ }
+
method = try_get_method_nofail (klass, "BeginInvoke", -1, 0);
if (method)
add_method (acfg, mono_marshal_get_delegate_begin_invoke (method));
ref->method = wrapper;
}
} else {
- /*
- * These wrappers are associated with a signature, not with a method.
- * Since we can't decode them into methods, they need a target method.
- */
- if (!target)
- return FALSE;
+ MonoClass *klass;
+ MonoMethod *invoke;
+
if (wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE) {
subtype = (WrapperSubtype)decode_value (p, &p);
+ if (subtype == WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL) {
+ klass = decode_klass_ref (module, p, &p, error);
+ invoke = mono_get_delegate_invoke_internal (klass);
+ ref->method = mono_marshal_get_delegate_invoke_internal(invoke, TRUE, FALSE, NULL);
+ break;
+ }
+
+ /*
+ * These wrappers are associated with a signature, not with a method.
+ * Since we can't decode them into methods, they need a target method.
+ */
+ if (!target)
+ return FALSE;
info = mono_marshal_get_wrapper_info (target);
if (info) {
if (info->subtype != subtype)
return FALSE;
}
}
- if (sig_matches_target (module, target, p, &p))
+ if (target && sig_matches_target (module, target, p, &p))
ref->method = target;
else
return FALSE;