[runtime] Default Interface Method support (#4625)
authorZoltan Varga <vargaz@gmail.com>
Mon, 3 Apr 2017 14:42:07 +0000 (10:42 -0400)
committerGitHub <noreply@github.com>
Mon, 3 Apr 2017 14:42:07 +0000 (10:42 -0400)
* [runtime] Add support for the simplest case of default interfaces, where the default method is used.

* [runtime] Implement overriding of default interface methods in interfaces.

* [jit] Fix gshared support for default interface methods, they require an mrgctx since the this argument doesn't point to the implementing class, not the interface.

mono/metadata/class.c
mono/mini/method-to-ir.c
mono/mini/mini-generic-sharing.c

index 6e086cf1442a451765cc3fdb8ad2586709f0e208..cd15ef8ea77dda8f112f841eab872d4b241e10f5 100644 (file)
@@ -4034,6 +4034,32 @@ mono_class_need_stelemref_method (MonoClass *klass)
        return klass->rank == 1 && MONO_TYPE_IS_REFERENCE (&klass->element_class->byval_arg);
 }
 
+static int
+apply_override (MonoClass *klass, MonoMethod **vtable, MonoMethod *decl, MonoMethod *override)
+{
+       int dslot;
+       dslot = mono_method_get_vtable_slot (decl);
+       if (dslot == -1) {
+               mono_class_set_type_load_failure (klass, "");
+               return FALSE;
+       }
+
+       dslot += mono_class_interface_offset (klass, decl->klass);
+       vtable [dslot] = override;
+       if (!MONO_CLASS_IS_INTERFACE (override->klass)) {
+               /*
+                * If override from an interface, then it is an override of a default interface method,
+                * don't override its slot.
+                */
+               vtable [dslot]->slot = dslot;
+       }
+
+       if (mono_security_core_clr_enabled ())
+               mono_security_core_clr_check_override (klass, vtable [dslot], decl);
+
+       return TRUE;
+}
+
 /*
  * LOCKING: this is supposed to be called with the loader lock held.
  */
@@ -4191,29 +4217,48 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o
        }
 
        TRACE_INTERFACE_VTABLE (print_vtable_full (klass, vtable, cur_slot, first_non_interface_slot, "AFTER INHERITING PARENT VTABLE", TRUE));
+
+       /* Process overrides from interface default methods */
+       // FIXME: Ordering between interfaces
+       for (int ifindex = 0; ifindex < klass->interface_offsets_count; ifindex++) {
+               ic = klass->interfaces_packed [ifindex];
+
+               mono_class_setup_methods (ic);
+               if (mono_class_has_failure (ic))
+                       goto fail;
+
+               MonoMethod **iface_overrides;
+               int iface_onum;
+               gboolean ok = mono_class_get_overrides_full (ic->image, ic->type_token, &iface_overrides, &iface_onum, mono_class_get_context (ic));
+               if (ok) {
+                       for (int i = 0; i < iface_onum; i++) {
+                               MonoMethod *decl = iface_overrides [i*2];
+                               MonoMethod *override = iface_overrides [i*2 + 1];
+                               if (!apply_override (klass, vtable, decl, override))
+                                       goto fail;
+
+                               if (!override_map)
+                                       override_map = g_hash_table_new (mono_aligned_addr_hash, NULL);
+                               g_hash_table_insert (override_map, decl, override);
+                       }
+                       g_free (iface_overrides);
+               }
+       }
+
        /* override interface methods */
        for (i = 0; i < onum; i++) {
                MonoMethod *decl = overrides [i*2];
+               MonoMethod *override = overrides [i*2 + 1];
                if (MONO_CLASS_IS_INTERFACE (decl->klass)) {
-                       int dslot;
-                       dslot = mono_method_get_vtable_slot (decl);
-                       if (dslot == -1) {
-                               mono_class_set_type_load_failure (klass, "");
+                       if (!apply_override (klass, vtable, decl, override))
                                goto fail;
-                       }
 
-                       dslot += mono_class_interface_offset (klass, decl->klass);
-                       vtable [dslot] = overrides [i*2 + 1];
-                       vtable [dslot]->slot = dslot;
                        if (!override_map)
                                override_map = g_hash_table_new (mono_aligned_addr_hash, NULL);
-
-                       g_hash_table_insert (override_map, overrides [i * 2], overrides [i * 2 + 1]);
-
-                       if (mono_security_core_clr_enabled ())
-                               mono_security_core_clr_check_override (klass, vtable [dslot], decl);
+                       g_hash_table_insert (override_map, decl, override);
                }
        }
+
        TRACE_INTERFACE_VTABLE (print_overrides (override_map, "AFTER OVERRIDING INTERFACE METHODS"));
        TRACE_INTERFACE_VTABLE (print_vtable_full (klass, vtable, cur_slot, first_non_interface_slot, "AFTER OVERRIDING INTERFACE METHODS", FALSE));
 
@@ -4319,6 +4364,13 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o
                                                TRACE_INTERFACE_VTABLE ((cm != NULL) && printf ("\n"));
                                        }
                                }
+
+                               if (vtable [im_slot] == NULL) {
+                                       if (!(im->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
+                                               TRACE_INTERFACE_VTABLE (printf ("    Using default iface method %s.\n", mono_method_full_name (im, 1)));
+                                               vtable [im_slot] = im;
+                                       }
+                               }
                        } else {
                                g_assert (vtable [im_slot] == override_im);
                        }
index 6929340ba6ced071ff3b44fe0c7c807749692cdd..0f35640120924a613771086ece5de5eb7865b8f5 100644 (file)
@@ -3294,6 +3294,16 @@ emit_get_rgctx (MonoCompile *cfg, MonoMethod *method, int context_used)
                mrgctx_loc = mono_get_vtable_var (cfg);
                EMIT_NEW_TEMPLOAD (cfg, mrgctx_var, mrgctx_loc->inst_c0);
 
+               return mrgctx_var;
+       } else if (MONO_CLASS_IS_INTERFACE (cfg->method->klass)) {
+               MonoInst *mrgctx_loc, *mrgctx_var;
+
+               /* Default interface methods need an mrgctx since the vtabke at runtime points at an implementing class */
+               mrgctx_loc = mono_get_vtable_var (cfg);
+               EMIT_NEW_TEMPLOAD (cfg, mrgctx_var, mrgctx_loc->inst_c0);
+
+               g_assert (mono_method_needs_static_rgctx_invoke (cfg->method, TRUE));
+
                return mrgctx_var;
        } else if (method->flags & METHOD_ATTRIBUTE_STATIC || method->klass->valuetype) {
                MonoInst *vtable_loc, *vtable_var;
index bc7d12aa678ed4dd54f5f111ef17998c28aa1668..587d368a4186ce564c123e0969c783d24a0fe667 100644 (file)
@@ -3016,7 +3016,8 @@ mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_v
                return TRUE;
 
        return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
-                       method->klass->valuetype) &&
+                       method->klass->valuetype ||
+                       MONO_CLASS_IS_INTERFACE (method->klass)) &&
                (mono_class_is_ginst (method->klass) || mono_class_is_gtd (method->klass));
 }