diff --git a/src/mono/mono/metadata/class-inlines.h b/src/mono/mono/metadata/class-inlines.h index 0de73b36ee8d3..7c8a61ae5b8e7 100644 --- a/src/mono/mono/metadata/class-inlines.h +++ b/src/mono/mono/metadata/class-inlines.h @@ -205,6 +205,18 @@ m_class_is_private (MonoClass *klass) return (mono_class_get_flags (klass) & TYPE_ATTRIBUTE_VISIBILITY_MASK) == TYPE_ATTRIBUTE_NOT_PUBLIC; } +static inline gboolean +m_method_is_static (MonoMethod *method) +{ + return (method->flags & METHOD_ATTRIBUTE_STATIC) != 0; +} + +static inline gboolean +m_method_is_virtual (MonoMethod *method) +{ + return (method->flags & METHOD_ATTRIBUTE_VIRTUAL) != 0; +} + static inline gboolean m_method_is_icall (MonoMethod *method) { diff --git a/src/mono/mono/metadata/class-setup-vtable.c b/src/mono/mono/metadata/class-setup-vtable.c index a05d49dba1cc7..70a63c6e32201 100644 --- a/src/mono/mono/metadata/class-setup-vtable.c +++ b/src/mono/mono/metadata/class-setup-vtable.c @@ -710,19 +710,18 @@ verify_class_overrides (MonoClass *klass, MonoMethod **overrides, int onum) return FALSE; } - if (!(body->flags & METHOD_ATTRIBUTE_VIRTUAL) || (body->flags & METHOD_ATTRIBUTE_STATIC)) { - if (body->flags & METHOD_ATTRIBUTE_STATIC) - mono_class_set_type_load_failure (klass, "Method must not be static to override a base type"); - else - mono_class_set_type_load_failure (klass, "Method must be virtual to override a base type"); + if (m_method_is_static (decl) != m_method_is_static (body)) { + mono_class_set_type_load_failure (klass, "Static method can't override a non-static method and vice versa."); return FALSE; } - if (!(decl->flags & METHOD_ATTRIBUTE_VIRTUAL) || (decl->flags & METHOD_ATTRIBUTE_STATIC)) { - if (body->flags & METHOD_ATTRIBUTE_STATIC) - mono_class_set_type_load_failure (klass, "Cannot override a static method in a base type"); - else - mono_class_set_type_load_failure (klass, "Cannot override a non virtual method in a base type"); + if (!m_method_is_virtual (body) && !m_method_is_static (body)) { + mono_class_set_type_load_failure (klass, "Method must be virtual to override a base type"); + return FALSE; + } + + if (!m_method_is_virtual (decl)) { + mono_class_set_type_load_failure (klass, "Cannot override a non virtual method in a base type"); return FALSE; } @@ -1773,7 +1772,7 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o int im_slot = ic_offset + im->slot; MonoMethod *override_im = (override_map != NULL) ? (MonoMethod *)g_hash_table_lookup (override_map, im) : NULL; - if (im->flags & METHOD_ATTRIBUTE_STATIC) + if (!m_method_is_virtual (im)) continue; TRACE_INTERFACE_VTABLE (printf ("\tchecking iface method %s\n", mono_method_full_name (im,1))); @@ -1856,7 +1855,7 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o MonoMethod *im = ic->methods [im_index]; int im_slot = ic_offset + im->slot; - if (im->flags & METHOD_ATTRIBUTE_STATIC) + if (!m_method_is_virtual (im)) continue; if (mono_method_get_is_reabstracted (im)) continue; @@ -2067,11 +2066,11 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o g_assert (cur_slot <= max_vtsize); - /* Ensure that all vtable slots are filled with concrete instance methods */ + /* Ensure that all vtable slots are filled with concrete methods */ // Now it is okay to implement a class that is not abstract and implements a interface that has an abstract method because it's reabstracted if (!mono_class_is_abstract (klass)) { for (i = 0; i < cur_slot; ++i) { - if (vtable [i] == NULL || (vtable [i]->flags & (METHOD_ATTRIBUTE_ABSTRACT | METHOD_ATTRIBUTE_STATIC))) { + if (vtable [i] == NULL || (vtable [i]->flags & METHOD_ATTRIBUTE_ABSTRACT)) { if (vtable [i] != NULL && mono_method_get_is_reabstracted (vtable [i])) continue; char *type_name = mono_type_get_full_name (klass); diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 6e1f8b8f31f40..f09b05f67d33f 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -3172,7 +3172,9 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target mono_class_setup_vtable (target_method->klass); // Follow the rules for constrained calls from ECMA spec - if (!m_class_is_valuetype (constrained_class)) { + if (m_method_is_static (target_method)) { + is_virtual = FALSE; + } else if (!m_class_is_valuetype (constrained_class)) { StackInfo *sp = td->sp - 1 - csignature->param_count; /* managed pointer on the stack, we need to deref that puppy */ interp_add_ins (td, MINT_LDIND_I); @@ -3197,7 +3199,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target if (target_method) mono_class_init_internal (target_method->klass); - if (!is_virtual && target_method && (target_method->flags & METHOD_ATTRIBUTE_ABSTRACT)) { + if (!is_virtual && target_method && (target_method->flags & METHOD_ATTRIBUTE_ABSTRACT) && !m_method_is_static (target_method)) { if (!mono_class_is_interface (method->klass)) interp_generate_bie_throw (td); else @@ -7119,6 +7121,12 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, if (method->wrapper_type == MONO_WRAPPER_NONE && m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) m = mono_marshal_get_synchronized_wrapper (m); + if (constrained_class) { + m = mono_get_method_constrained_with_method (image, m, constrained_class, generic_context, error); + goto_if_nok (error, exit); + constrained_class = NULL; + } + if (G_UNLIKELY (*td->ip == CEE_LDFTN && m->wrapper_type == MONO_WRAPPER_NONE && mono_method_has_unmanaged_callers_only_attribute (m))) { diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index 8185db6536467..238a4db6875b2 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -3826,6 +3826,48 @@ mono_emit_load_got_addr (MonoCompile *cfg) MONO_ADD_INS (cfg->bb_exit, dummy_use); } +static MonoMethod* +get_constrained_method (MonoCompile *cfg, MonoImage *image, guint32 token, + MonoMethod *cil_method, MonoClass *constrained_class, + MonoGenericContext *generic_context) +{ + MonoMethod *cmethod = cil_method; + gboolean constrained_is_generic_param = + m_class_get_byval_arg (constrained_class)->type == MONO_TYPE_VAR || + m_class_get_byval_arg (constrained_class)->type == MONO_TYPE_MVAR; + + if (cfg->current_method->wrapper_type != MONO_WRAPPER_NONE) { + if (cfg->verbose_level > 2) + printf ("DM Constrained call to %s\n", mono_type_get_full_name (constrained_class)); + if (!(constrained_is_generic_param && + cfg->gshared)) { + cmethod = mono_get_method_constrained_with_method (image, cil_method, constrained_class, generic_context, cfg->error); + CHECK_CFG_ERROR; + } + } else { + if (cfg->verbose_level > 2) + printf ("Constrained call to %s\n", mono_type_get_full_name (constrained_class)); + + if (constrained_is_generic_param && cfg->gshared) { + /* + * This is needed since get_method_constrained can't find + * the method in klass representing a type var. + * The type var is guaranteed to be a reference type in this + * case. + */ + if (!mini_is_gsharedvt_klass (constrained_class)) + g_assert (!m_class_is_valuetype (cmethod->klass)); + } else { + cmethod = mono_get_method_constrained_checked (image, token, constrained_class, generic_context, &cil_method, cfg->error); + CHECK_CFG_ERROR; + } + } + return cmethod; + + mono_error_exit: + return NULL; +} + static gboolean method_does_not_return (MonoMethod *method) { @@ -5641,7 +5683,10 @@ handle_constrained_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignat } } - if (constrained_partial_call) { + if (m_method_is_static (cmethod)) { + /* Call to an abstract static method */ + return NULL; + } if (constrained_partial_call) { gboolean need_box = TRUE; /* @@ -7128,36 +7173,12 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MonoMethod *cil_method; cil_method = cmethod; if (constrained_class) { - gboolean constrained_is_generic_param = - m_class_get_byval_arg (constrained_class)->type == MONO_TYPE_VAR || - m_class_get_byval_arg (constrained_class)->type == MONO_TYPE_MVAR; - - if (method->wrapper_type != MONO_WRAPPER_NONE) { - if (cfg->verbose_level > 2) - printf ("DM Constrained call to %s\n", mono_type_get_full_name (constrained_class)); - if (!(constrained_is_generic_param && - cfg->gshared)) { - cmethod = mono_get_method_constrained_with_method (image, cil_method, constrained_class, generic_context, cfg->error); - CHECK_CFG_ERROR; - } - } else { - if (cfg->verbose_level > 2) - printf ("Constrained call to %s\n", mono_type_get_full_name (constrained_class)); + if (m_method_is_static (cil_method) && mini_class_check_context_used (cfg, constrained_class)) + // FIXME: + GENERIC_SHARING_FAILURE (CEE_CALL); - if (constrained_is_generic_param && cfg->gshared) { - /* - * This is needed since get_method_constrained can't find - * the method in klass representing a type var. - * The type var is guaranteed to be a reference type in this - * case. - */ - if (!mini_is_gsharedvt_klass (constrained_class)) - g_assert (!m_class_is_valuetype (cmethod->klass)); - } else { - cmethod = mono_get_method_constrained_checked (image, token, constrained_class, generic_context, &cil_method, cfg->error); - CHECK_CFG_ERROR; - } - } + cmethod = get_constrained_method (cfg, image, token, cil_method, constrained_class, generic_context); + CHECK_CFG_ERROR; if (m_class_is_enumtype (constrained_class) && !strcmp (cmethod->name, "GetHashCode")) { /* Use the corresponding method from the base type to avoid boxing */ @@ -7405,7 +7426,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b context_used = mini_method_check_context_used (cfg, cmethod); - if (context_used && mono_class_is_interface (cmethod->klass)) { + if (context_used && mono_class_is_interface (cmethod->klass) && !m_method_is_static (cmethod)) { /* Generic method interface calls are resolved via a helper function and don't @@ -10960,6 +10981,15 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b cmethod = mini_get_method (cfg, method, n, NULL, generic_context); CHECK_CFG_ERROR; + if (constrained_class) { + if (m_method_is_static (cmethod) && mini_class_check_context_used (cfg, constrained_class)) + // FIXME: + GENERIC_SHARING_FAILURE (CEE_LDFTN); + cmethod = get_constrained_method (cfg, image, n, cmethod, constrained_class, generic_context); + constrained_class = NULL; + CHECK_CFG_ERROR; + } + mono_class_init_internal (cmethod->klass); mono_save_token_info (cfg, image, n, cmethod); diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 0d6119827aef1..f1d2ba28e4605 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1156,7 +1156,13 @@ Doesn't pass after LLVM AOT compilation. - + + Static virtual methods are not yet implemented in the Mono runtime. + + + Static virtual methods are not yet implemented in the Mono runtime. + + Static virtual methods are not yet implemented in the Mono runtime.