Make begin-invoke/end-invoke wrappers for generic delegates like Func/Action generic...
authorZoltan Varga <vargaz@gmail.com>
Thu, 7 Mar 2013 14:41:05 +0000 (15:41 +0100)
committerZoltan Varga <vargaz@gmail.com>
Thu, 7 Mar 2013 15:26:30 +0000 (16:26 +0100)
mono/metadata/image.c
mono/metadata/marshal.c
mono/metadata/metadata-internals.h
mono/mini/gshared.cs
mono/mini/method-to-ir.c

index 1533f1004623b9eed4ed885628151199eb630155..69e53b496c20cec8950e87718d13449268aba194 100644 (file)
@@ -1622,6 +1622,8 @@ mono_image_close_except_pools (MonoImage *image)
        free_hash (image->delegate_abstract_invoke_cache);
        free_hash (image->delegate_bound_static_invoke_cache);
        free_hash (image->delegate_invoke_generic_cache);
+       free_hash (image->delegate_begin_invoke_generic_cache);
+       free_hash (image->delegate_end_invoke_generic_cache);
        free_hash (image->remoting_invoke_cache);
        free_hash (image->runtime_invoke_cache);
        free_hash (image->runtime_invoke_direct_cache);
index 800aa2f441b8e5b2d091dc300bbf49c895782ba6..0e9e7f94b22529e7bbf35a155a48b42fcc7ddb32 100644 (file)
@@ -2660,6 +2660,59 @@ get_wrapper_target_class (MonoImage *image)
        return klass;
 }
 
+static MonoMethod*
+check_generic_delegate_wrapper_cache (GHashTable *cache, MonoMethod *orig_method, MonoMethod *def_method, MonoGenericContext *ctx)
+{
+       MonoMethod *res;
+       MonoMethod *inst, *def;
+
+       /*
+        * Look for the instance
+        */
+       res = mono_marshal_find_in_cache (cache, orig_method->klass);
+       if (res)
+               return res;
+
+       /*
+        * Look for the definition
+        */
+       def = mono_marshal_find_in_cache (cache, def_method->klass);
+       if (def) {
+               inst = mono_class_inflate_generic_method (def, ctx);
+               /* Cache it */
+               mono_memory_barrier ();
+               mono_marshal_lock ();
+               res = g_hash_table_lookup (cache, orig_method->klass);
+               if (!res) {
+                       g_hash_table_insert (cache, orig_method->klass, inst);
+                       res = inst;
+               }
+               mono_marshal_unlock ();
+               return res;
+       }
+       return NULL;
+}
+
+static MonoMethod*
+cache_generic_delegate_wrapper (GHashTable *cache, MonoMethod *orig_method, MonoMethod *def, MonoGenericContext *ctx)
+{
+       MonoMethod *inst, *res;
+
+       /*
+        * We use the same cache for the generic definition and the instances.
+        */
+       inst = mono_class_inflate_generic_method (def, ctx);
+       mono_memory_barrier ();
+       mono_marshal_lock ();
+       res = g_hash_table_lookup (cache, orig_method->klass);
+       if (!res) {
+               g_hash_table_insert (cache, orig_method->klass, inst);
+               res = inst;
+       }
+       mono_marshal_unlock ();
+       return res;
+}
+
 MonoMethod *
 mono_marshal_get_delegate_begin_invoke (MonoMethod *method)
 {
@@ -2669,22 +2722,46 @@ mono_marshal_get_delegate_begin_invoke (MonoMethod *method)
        GHashTable *cache;
        int params_var;
        char *name;
+       MonoGenericContext *ctx = NULL;
+       MonoMethod *orig_method = NULL;
 
        g_assert (method && method->klass->parent == mono_defaults.multicastdelegate_class &&
                  !strcmp (method->name, "BeginInvoke"));
 
+       /*
+        * For generic delegates, create a generic wrapper, and returns an instance to help AOT.
+        */
+       if (method->is_inflated) {
+               orig_method = method;
+               ctx = &((MonoMethodInflated*)method)->context;
+               method = ((MonoMethodInflated*)method)->declaring;
+       }
+
        sig = mono_signature_no_pinvoke (method);
 
-       cache = get_cache (&method->klass->image->delegate_begin_invoke_cache,
-                                          (GHashFunc)mono_signature_hash, 
-                                          (GCompareFunc)mono_metadata_signature_equal);
-       if ((res = mono_marshal_find_in_cache (cache, sig)))
-               return res;
+       /*
+        * Check cache
+        */
+       if (ctx) {
+               cache = get_cache (&method->klass->image->delegate_begin_invoke_generic_cache, mono_aligned_addr_hash, NULL);
+               res = check_generic_delegate_wrapper_cache (cache, orig_method, method, ctx);
+               if (res)
+                       return res;
+       } else {
+               cache = get_cache (&method->klass->image->delegate_begin_invoke_cache,
+                                                  (GHashFunc)mono_signature_hash, 
+                                                  (GCompareFunc)mono_metadata_signature_equal);
+               if ((res = mono_marshal_find_in_cache (cache, sig)))
+                       return res;
+       }
 
        g_assert (sig->hasthis);
 
        name = mono_signature_to_name (sig, "begin_invoke");
-       mb = mono_mb_new (get_wrapper_target_class (method->klass->image), name, MONO_WRAPPER_DELEGATE_BEGIN_INVOKE);
+       if (ctx)
+               mb = mono_mb_new (method->klass, name, MONO_WRAPPER_DELEGATE_BEGIN_INVOKE);
+       else
+               mb = mono_mb_new (get_wrapper_target_class (method->klass->image), name, MONO_WRAPPER_DELEGATE_BEGIN_INVOKE);
        g_free (name);
 
 #ifndef DISABLE_JIT
@@ -2696,7 +2773,14 @@ mono_marshal_get_delegate_begin_invoke (MonoMethod *method)
        mono_mb_emit_byte (mb, CEE_RET);
 #endif
 
-       res = mono_mb_create_and_cache (cache, sig, mb, sig, sig->param_count + 16);
+       if (ctx) {
+               MonoMethod *def;
+               def = mono_mb_create_and_cache (cache, method->klass, mb, sig, sig->param_count + 16);
+               res = cache_generic_delegate_wrapper (cache, orig_method, def, ctx);
+       } else {
+               res = mono_mb_create_and_cache (cache, sig, mb, sig, sig->param_count + 16);
+       }
+
        mono_mb_free (mb);
        return res;
 }
@@ -2823,6 +2907,12 @@ mono_mb_emit_restore_result (MonoMethodBuilder *mb, MonoType *return_type)
                mono_mb_emit_op (mb, CEE_LDOBJ, klass);
                break;
        }
+       case MONO_TYPE_VAR:
+       case MONO_TYPE_MVAR: {
+               MonoClass *klass = mono_class_from_mono_type (return_type);
+               mono_mb_emit_op (mb, CEE_UNBOX_ANY, klass);
+               break;
+       }
        default:
                g_warning ("type 0x%x not handled", return_type->type);
                g_assert_not_reached ();
@@ -2842,22 +2932,46 @@ mono_marshal_get_delegate_end_invoke (MonoMethod *method)
        GHashTable *cache;
        int params_var;
        char *name;
+       MonoGenericContext *ctx = NULL;
+       MonoMethod *orig_method = NULL;
 
        g_assert (method && method->klass->parent == mono_defaults.multicastdelegate_class &&
                  !strcmp (method->name, "EndInvoke"));
 
+       /*
+        * For generic delegates, create a generic wrapper, and returns an instance to help AOT.
+        */
+       if (method->is_inflated) {
+               orig_method = method;
+               ctx = &((MonoMethodInflated*)method)->context;
+               method = ((MonoMethodInflated*)method)->declaring;
+       }
+
        sig = mono_signature_no_pinvoke (method);
 
-       cache = get_cache (&method->klass->image->delegate_end_invoke_cache,
-                                          (GHashFunc)mono_signature_hash, 
-                                          (GCompareFunc)mono_metadata_signature_equal);
-       if ((res = mono_marshal_find_in_cache (cache, sig)))
-               return res;
+       /*
+        * Check cache
+        */
+       if (ctx) {
+               cache = get_cache (&method->klass->image->delegate_end_invoke_generic_cache, mono_aligned_addr_hash, NULL);
+               res = check_generic_delegate_wrapper_cache (cache, orig_method, method, ctx);
+               if (res)
+                       return res;
+       } else {
+               cache = get_cache (&method->klass->image->delegate_end_invoke_cache,
+                                                  (GHashFunc)mono_signature_hash, 
+                                                  (GCompareFunc)mono_metadata_signature_equal);
+               if ((res = mono_marshal_find_in_cache (cache, sig)))
+                       return res;
+       }
 
        g_assert (sig->hasthis);
 
        name = mono_signature_to_name (sig, "end_invoke");
-       mb = mono_mb_new (get_wrapper_target_class (method->klass->image), name, MONO_WRAPPER_DELEGATE_END_INVOKE);
+       if (ctx)
+               mb = mono_mb_new (method->klass, name, MONO_WRAPPER_DELEGATE_END_INVOKE);
+       else
+               mb = mono_mb_new (get_wrapper_target_class (method->klass->image), name, MONO_WRAPPER_DELEGATE_END_INVOKE);
        g_free (name);
 
 #ifndef DISABLE_JIT
@@ -2874,8 +2988,14 @@ mono_marshal_get_delegate_end_invoke (MonoMethod *method)
                mono_mb_emit_restore_result (mb, sig->ret);
 #endif
 
-       res = mono_mb_create_and_cache (cache, sig,
-                                                                                mb, sig, sig->param_count + 16);
+       if (ctx) {
+               MonoMethod *def;
+               def = mono_mb_create_and_cache (cache, method->klass, mb, sig, sig->param_count + 16);
+               res = cache_generic_delegate_wrapper (cache, orig_method, def, ctx);
+       } else {
+               res = mono_mb_create_and_cache (cache, sig,
+                                                                               mb, sig, sig->param_count + 16);
+       }
        mono_mb_free (mb);
 
        return res;
@@ -4022,33 +4142,10 @@ mono_marshal_get_delegate_invoke (MonoMethod *method, MonoDelegate *del)
         * Check cache
         */
        if (ctx) {
-               MonoMethod *def, *inst;
-
-               /*
-                * Look for the instance
-                */
                cache = get_cache (&method->klass->image->delegate_invoke_generic_cache, mono_aligned_addr_hash, NULL);
-               res = mono_marshal_find_in_cache (cache, orig_method->klass);
+               res = check_generic_delegate_wrapper_cache (cache, orig_method, method, ctx);
                if (res)
                        return res;
-
-               /*
-                * Look for the definition
-                */
-               def = mono_marshal_find_in_cache (cache, method->klass);
-               if (def) {
-                       inst = mono_class_inflate_generic_method (def, ctx);
-                       /* Cache it */
-                       mono_memory_barrier ();
-                       mono_marshal_lock ();
-                       res = g_hash_table_lookup (cache, orig_method->klass);
-                       if (!res) {
-                               g_hash_table_insert (cache, orig_method->klass, inst);
-                               res = inst;
-                       }
-                       mono_marshal_unlock ();
-                       return res;
-               }
        } else if (callvirt || static_method_with_first_arg_bound) {
                GHashTable **cache_ptr;
                if (static_method_with_first_arg_bound)
@@ -4197,22 +4294,10 @@ mono_marshal_get_delegate_invoke (MonoMethod *method, MonoDelegate *del)
 #endif /* DISABLE_JIT */
 
        if (ctx) {
-               MonoMethod *def, *inst;
+               MonoMethod *def;
 
-               /*
-                * We use the same cache for the generic definition and the instances.
-                */
                def = mono_mb_create_and_cache (cache, method->klass, mb, sig, sig->param_count + 16);
-
-               inst = mono_class_inflate_generic_method (def, ctx);
-               mono_memory_barrier ();
-               mono_marshal_lock ();
-               res = g_hash_table_lookup (cache, orig_method->klass);
-               if (!res) {
-                       g_hash_table_insert (cache, orig_method->klass, inst);
-                       res = inst;
-               }
-               mono_marshal_unlock ();
+               res = cache_generic_delegate_wrapper (cache, orig_method, def, ctx);
        } else if (static_method_with_first_arg_bound || callvirt) {
                // From mono_mb_create_and_cache
                newm = mono_mb_create_method (mb, sig, sig->param_count + 16);
index e869bc7a781211868dba4dde82e5fe50fd9b594d..d0029658c9d31ebe5571d5b483127d88a3ae7555 100644 (file)
@@ -293,6 +293,8 @@ struct _MonoImage {
        GHashTable *proxy_isinst_cache;
        GHashTable *rgctx_template_hash; /* LOCKING: templates lock */
        GHashTable *delegate_invoke_generic_cache;
+       GHashTable *delegate_begin_invoke_generic_cache;
+       GHashTable *delegate_end_invoke_generic_cache;
 
        /* Contains rarely used fields of runtime structures belonging to this image */
        MonoPropertyHash *property_hash;
index 79a1906c51ed6a5a619726b2e372b819a0f282de..864af61984a3c801e9639731f45ed30f209ffd0f 100644 (file)
@@ -1058,4 +1058,30 @@ public class Tests
                }
                return 0;
        }
+
+       interface IFace6 {
+               T[] Del<T> (T t);
+       }
+
+       class Class6 : IFace6 {
+               public T[] Del<T> (T t) {
+                       var res = new T [5];
+                       Func<T, T, T, T, T> func = delegate(T t1, T t2, T t3, T t4) { res [0] = t1; res [1] = t2; res [2] = t3; res [3] = t4; return t1; };
+                       var v = func.BeginInvoke(t, t, t, t, null, null);
+                       res [4] = func.EndInvoke (v);
+                       return res;
+               }
+       }
+
+       public static int test_0_begin_end_invoke () {
+               IFace6 o = new Class6 ();
+               var arr1 = o.Del (1);
+               if (arr1 [0] != 1 || arr1 [1] != 1 || arr1 [2] != 1 || arr1 [3] != 1 || arr1 [4] != 1)
+                       return 1;
+               var arr2 = o.Del (2.0);
+               if (arr2 [0] != 2.0 || arr2 [1] != 2.0 || arr2 [2] != 2.0 || arr2 [3] != 2.0 || arr2 [4] != 2.0)
+                       return 2;
+               return 0;
+       }
+
 }
index 9cffb1420684c4dfbb40277206984d5f750250b3..442017d4b56b35ad1dc6aa5b19718b43d1a4bd02 100644 (file)
@@ -5651,10 +5651,13 @@ mini_get_class (MonoMethod *method, guint32 token, MonoGenericContext *context)
 {
        MonoClass *klass;
 
-       if (method->wrapper_type != MONO_WRAPPER_NONE)
+       if (method->wrapper_type != MONO_WRAPPER_NONE) {
                klass = mono_method_get_wrapper_data (method, token);
-       else
+               if (context)
+                       klass = mono_class_inflate_generic_class (klass, context);
+       } else {
                klass = mono_class_get_full (method->klass->image, token, context);
+       }
        if (klass)
                mono_class_init (klass);
        return klass;