diff --git a/src/mono/mono/metadata/class-accessors.c b/src/mono/mono/metadata/class-accessors.c index 4cc08da084bafc..f74b2dc4dbe704 100644 --- a/src/mono/mono/metadata/class-accessors.c +++ b/src/mono/mono/metadata/class-accessors.c @@ -578,6 +578,23 @@ mono_class_set_failure (MonoClass *klass, MonoErrorBoxed *boxed_error) return TRUE; } +/** + * mono_class_set_deferred_failure: + * \param klass class in which the failure was detected + + * This method marks the class with a deferred failure, indicating that a failure was detected but it will be processed during AOT runtime.. + * Note that only the first failure is kept. + * + * LOCKING: Acquires the loader lock. + */ +void +mono_class_set_deferred_failure (MonoClass *klass) +{ + mono_loader_lock (); + klass->has_deferred_failure = 1; + mono_loader_unlock (); +} + /** * mono_class_set_nonblittable: * \param klass class which will be marked as not blittable. diff --git a/src/mono/mono/metadata/class-getters.h b/src/mono/mono/metadata/class-getters.h index 9eafbb4330bc84..c0e15059991ad6 100644 --- a/src/mono/mono/metadata/class-getters.h +++ b/src/mono/mono/metadata/class-getters.h @@ -49,6 +49,7 @@ MONO_CLASS_GETTER(m_class_is_simd_type, gboolean, , MonoClass, simd_type) MONO_CLASS_GETTER(m_class_is_has_finalize_inited, gboolean, , MonoClass, has_finalize_inited) MONO_CLASS_GETTER(m_class_is_fields_inited, gboolean, , MonoClass, fields_inited) MONO_CLASS_GETTER(m_class_has_failure, gboolean, , MonoClass, has_failure) +MONO_CLASS_GETTER(m_class_has_deferred_failure, gboolean, , MonoClass, has_deferred_failure) MONO_CLASS_GETTER(m_class_has_weak_fields, gboolean, , MonoClass, has_weak_fields) MONO_CLASS_GETTER(m_class_has_dim_conflicts, gboolean, , MonoClass, has_dim_conflicts) MONO_CLASS_GETTER(m_class_get_parent, MonoClass *, , MonoClass, parent) diff --git a/src/mono/mono/metadata/class-init.c b/src/mono/mono/metadata/class-init.c index dc6e35862ea294..8c9286d4bf9b29 100644 --- a/src/mono/mono/metadata/class-init.c +++ b/src/mono/mono/metadata/class-init.c @@ -305,7 +305,7 @@ mono_class_setup_fields (MonoClass *klass) } if (m_class_is_inlinearray (klass) && m_class_inlinearray_value (klass) <= 0) - mono_class_set_type_load_failure (klass, "Inline array length property must be positive."); + mono_class_set_deferred_type_load_failure_callback (klass, "Inline array length property must be positive."); /* Get the real size */ explicit_size = mono_metadata_packing_from_typedef (klass->image, klass->type_token, &packing_size, &real_size); @@ -376,8 +376,8 @@ mono_class_setup_fields (MonoClass *klass) break; } if (m_class_is_inlinearray (klass)) { - mono_class_set_type_load_failure (klass, "Inline array struct must not have explicit layout."); - break; + if (mono_class_set_deferred_type_load_failure_callback (klass, "Inline array struct must not have explicit layout.")) + break; } } if (mono_type_has_exceptions (field->type)) { @@ -2274,12 +2274,15 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ if (m_class_is_inlinearray (klass)) { // Limit the max size of array instance to 1MiB const guint32 struct_max_size = 1024 * 1024; + guint32 initial_size = size; // If size overflows, it returns 0 size *= m_class_inlinearray_value (klass); inlined_fields++; if(size == 0 || size > struct_max_size) { - mono_class_set_type_load_failure (klass, "Inline array struct size out of bounds, abnormally large."); - break; + if (mono_class_set_deferred_type_load_failure_callback (klass, "Inline array struct size out of bounds, abnormally large.")) + break; + else + size = initial_size; } } @@ -2310,7 +2313,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ } } if (m_class_is_inlinearray (klass) && inlined_fields != 1) - mono_class_set_type_load_failure (klass, "Inline array struct must have a single field."); + mono_class_set_deferred_type_load_failure_callback (klass, "Inline array struct must have a single field."); break; case TYPE_ATTRIBUTE_EXPLICIT_LAYOUT: { real_size = 0; @@ -2991,6 +2994,14 @@ mono_class_init_internal (MonoClass *klass) has_cached_info = mono_class_get_cached_class_info (klass, &cached_info); + /* + * If the class has a deferred failure, ignore the cached info and + * let the runtime go on the slow path of trying to setup the class + * layout at runtime. + */ + if (has_cached_info && cached_info.has_deferred_failure) + has_cached_info = FALSE; + /* Compute instance size etc. */ init_sizes_with_info (klass, has_cached_info ? &cached_info : NULL); if (mono_class_has_failure (klass)) diff --git a/src/mono/mono/metadata/class-internals.h b/src/mono/mono/metadata/class-internals.h index 7915be4ed11839..68f83b16ab2fca 100644 --- a/src/mono/mono/metadata/class-internals.h +++ b/src/mono/mono/metadata/class-internals.h @@ -578,6 +578,7 @@ typedef struct MonoCachedClassInfo { guint no_special_static_fields : 1; guint is_generic_container : 1; guint has_weak_fields : 1; + guint has_deferred_failure : 1; guint32 cctor_token; MonoImage *finalize_image; guint32 finalize_token; @@ -1062,9 +1063,6 @@ mono_register_jit_icall_info (MonoJitICallInfo *info, T func, const char *name, #define mono_register_jit_icall(func, sig, no_wrapper) (mono_register_jit_icall_info (&mono_get_jit_icall_info ()->func, func, #func, (sig), (no_wrapper), NULL)) -gboolean -mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...) MONO_ATTR_FORMAT_PRINTF(2,3); - MonoException* mono_class_get_exception_for_failure (MonoClass *klass); @@ -1268,6 +1266,9 @@ mono_error_set_for_class_failure (MonoError *orerror, const MonoClass *klass); gboolean mono_class_has_failure (const MonoClass *klass); +gboolean +mono_class_has_deferred_failure (const MonoClass *klass); + /* Kind specific accessors */ MONO_COMPONENT_API MonoGenericClass* mono_class_get_generic_class (MonoClass *klass); @@ -1429,6 +1430,9 @@ mono_class_find_enum_basetype (MonoClass *klass, MonoError *error); gboolean mono_class_set_failure (MonoClass *klass, MonoErrorBoxed *boxed_error); +void +mono_class_set_deferred_failure (MonoClass *klass); + gboolean mono_class_set_type_load_failure_causedby_class (MonoClass *klass, const MonoClass *caused_by, const gchar* msg); diff --git a/src/mono/mono/metadata/class-private-definition.h b/src/mono/mono/metadata/class-private-definition.h index 08139a219c6934..ee104bb40a6668 100644 --- a/src/mono/mono/metadata/class-private-definition.h +++ b/src/mono/mono/metadata/class-private-definition.h @@ -83,6 +83,7 @@ struct _MonoClass { guint has_weak_fields : 1; /* class has weak reference fields */ guint has_dim_conflicts : 1; /* Class has conflicting default interface methods */ guint any_field_has_auto_layout : 1; /* a field in this type's layout uses auto-layout */ + guint has_deferred_failure : 1; MonoClass *parent; MonoClass *nested_in; diff --git a/src/mono/mono/metadata/class.c b/src/mono/mono/metadata/class.c index 5dc37fc02f8462..3b371bffdc7002 100644 --- a/src/mono/mono/metadata/class.c +++ b/src/mono/mono/metadata/class.c @@ -5943,36 +5943,11 @@ mono_class_has_failure (const MonoClass *klass) return m_class_has_failure ((MonoClass*)klass) != 0; } - -/** - * mono_class_set_type_load_failure: - * \param klass class in which the failure was detected - * \param fmt \c printf -style error message string. - * - * Collect detected failure informaion in the class for later processing. - * The error is stored as a MonoErrorBoxed as with mono_error_set_type_load_class() - * Note that only the first failure is kept. - * - * LOCKING: Acquires the loader lock. - * - * \returns FALSE if a failure was already set on the class, or TRUE otherwise. - */ gboolean -mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...) +mono_class_has_deferred_failure (const MonoClass *klass) { - ERROR_DECL (prepare_error); - va_list args; - - if (mono_class_has_failure (klass)) - return FALSE; - - va_start (args, fmt); - mono_error_vset_type_load_class (prepare_error, klass, fmt, args); - va_end (args); - - MonoErrorBoxed *box = mono_error_box (prepare_error, m_class_get_image (klass)); - mono_error_cleanup (prepare_error); - return mono_class_set_failure (klass, box); + g_assert (klass != NULL); + return m_class_has_deferred_failure ((MonoClass*)klass) != 0; } /** diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index a5d36d9c939013..5a581216c2bf2e 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -7816,7 +7816,7 @@ emit_klass_info (MonoAotCompile *acfg, guint32 token) } else { gboolean has_nested = mono_class_get_nested_classes_property (klass) != NULL; encode_value (m_class_get_vtable_size (klass), p, &p); - encode_value ((m_class_has_weak_fields (klass) << 9) | (mono_class_is_gtd (klass) ? (1 << 8) : 0) | (no_special_static << 7) | (m_class_has_static_refs (klass) << 6) | (m_class_has_references (klass) << 5) | ((m_class_is_blittable (klass) << 4) | (has_nested ? 1 : 0) << 3) | (m_class_has_cctor (klass) << 2) | (m_class_has_finalize (klass) << 1) | m_class_is_ghcimpl (klass), p, &p); + encode_value ((m_class_has_deferred_failure (klass) << 10) | (m_class_has_weak_fields (klass) << 9) | (mono_class_is_gtd (klass) ? (1 << 8) : 0) | (no_special_static << 7) | (m_class_has_static_refs (klass) << 6) | (m_class_has_references (klass) << 5) | ((m_class_is_blittable (klass) << 4) | (has_nested ? 1 : 0) << 3) | (m_class_has_cctor (klass) << 2) | (m_class_has_finalize (klass) << 1) | m_class_is_ghcimpl (klass), p, &p); if (m_class_has_cctor (klass)) encode_method_ref (acfg, mono_class_get_cctor (klass), p, &p); if (m_class_has_finalize (klass)) diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c index 1b273de6c0146e..495fc0310435a1 100644 --- a/src/mono/mono/mini/aot-runtime.c +++ b/src/mono/mono/mini/aot-runtime.c @@ -2498,6 +2498,7 @@ decode_cached_class_info (MonoAotModule *module, MonoCachedClassInfo *info, guin info->no_special_static_fields = (flags >> 7) & 0x1; info->is_generic_container = (flags >> 8) & 0x1; info->has_weak_fields = (flags >> 9) & 0x1; + info->has_deferred_failure = (flags >> 10) & 0x1; if (info->has_cctor) { res = decode_method_ref (module, &ref, buf, &buf, error); diff --git a/src/mono/mono/mini/driver.c b/src/mono/mono/mini/driver.c index a5c95bd8ae56be..2d21c98dd4fc8d 100644 --- a/src/mono/mono/mini/driver.c +++ b/src/mono/mono/mini/driver.c @@ -1400,7 +1400,7 @@ main_thread_handler (gpointer user_data) MonoAssembly **assemblies; assemblies = g_new0 (MonoAssembly*, main_args->argc); - + set_failure_type (DEFERRED_FAILURE); /* Treat the other arguments as assemblies to compile too */ for (i = 0; i < main_args->argc; ++i) { assembly = mono_domain_assembly_open_internal (mono_alc_get_default (), main_args->argv [i]); @@ -1428,6 +1428,7 @@ main_thread_handler (gpointer user_data) return; } + set_failure_type (IMMEDIATE_FAILURE); assembly = mono_domain_assembly_open_internal (mono_alc_get_default (), main_args->file); if (!assembly){ fprintf (stderr, "Can not open image %s\n", main_args->file); diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 094da4f03a247e..199aa8a59c01eb 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -1547,7 +1547,6 @@ mono_resolve_patch_target_ext (MonoMemoryManager *mem_manager, MonoMethod *metho break; case MONO_PATCH_INFO_VTABLE: target = mono_class_vtable_checked (patch_info->data.klass, error); - mono_error_assert_ok (error); break; case MONO_PATCH_INFO_DELEGATE_INFO: { MonoDelegateClassMethodPair *del_tramp = patch_info->data.del_tramp; diff --git a/src/mono/mono/utils/mono-error-internals.h b/src/mono/mono/utils/mono-error-internals.h index 6ed41ef1945c5e..272d87bbdf32b2 100644 --- a/src/mono/mono/utils/mono-error-internals.h +++ b/src/mono/mono/utils/mono-error-internals.h @@ -319,6 +319,36 @@ mono_error_set_specific (MonoError *error, int error_code, const char *missing_m void mono_error_set_first_argument (MonoError *oerror, const char *first_argument); +typedef enum { + DEFERRED_FAILURE, // Used during AOT compilation to defer failure for execution + IMMEDIATE_FAILURE // Used during runtime to indicate that the failure should be reported +} FailureType; + +void +set_failure_type (FailureType failure_type); + +/** + * TypeLoadFailureCallback: + * @param klass: Class in which the failure was detected. + * @param fmt: printf-style error message string. + * + * The callback is responsible for processing the failure information provided by the @klass parameter and the error message format string @fmt. + * If a deferred failure occurs, the callback should return FALSE to let the AOT compiler proceed with the class layout setup. + * Otherwise, if the callback returns TRUE, it indicates that the failure should be reported. + * + * @returns: TRUE if the failure is handled and the runtime should not proceed with class setup, FALSE if the failure should be deferred for runtime class setup. + * + */ +typedef gboolean (*TypeLoadFailureCallback)(MonoClass *klass, const char * fmt, ...) MONO_ATTR_FORMAT_PRINTF(2,3); + +extern TypeLoadFailureCallback mono_class_set_deferred_type_load_failure_callback; + +gboolean +mono_class_set_deferred_type_load_failure (MonoClass *klass, const char * fmt, ...); + +gboolean +mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...); + #if HOST_WIN32 #if HOST_X86 || HOST_AMD64 diff --git a/src/mono/mono/utils/mono-error.c b/src/mono/mono/utils/mono-error.c index 821fc33c5cd2a6..3cb2cf7b8ef7b9 100644 --- a/src/mono/mono/utils/mono-error.c +++ b/src/mono/mono/utils/mono-error.c @@ -30,6 +30,8 @@ va_end (args); \ } while (0) +TypeLoadFailureCallback mono_class_set_deferred_type_load_failure_callback = mono_class_set_type_load_failure; + static void mono_error_set_generic_errorv (MonoError *oerror, const char *name_space, const char *name, const char *msg_format, va_list args); @@ -908,3 +910,73 @@ mono_error_set_first_argument (MonoError *oerror, const char *first_argument) to->first_argument = g_strdup (first_argument); to->flags |= MONO_ERROR_FREE_STRINGS; } + +/** + * mono_class_set_deferred_type_load_failure: + * \param klass class in which the failure was detected + * \param fmt \c printf -style error message string. + * + * Sets a deferred failure in the class and prints a warning message. + * The deferred failure allows the runtime to attempt setting up the class layout at runtime. + * + * LOCKING: Acquires the loader lock. + * + * \returns FALSE + */ +gboolean +mono_class_set_deferred_type_load_failure (MonoClass *klass, const char * fmt, ...) +{ + if (!mono_class_has_deferred_failure (klass)) { + va_list args; + + va_start (args, fmt); + g_warning ("Warning: %s", fmt, args); + va_end (args); + + mono_class_set_deferred_failure (klass); + } + + return FALSE; +} + +/** + * mono_class_set_type_load_failure: + * \param klass class in which the failure was detected + * \param fmt \c printf -style error message string. + * + * Collect detected failure informaion in the class for later processing. + * The error is stored as a MonoErrorBoxed as with mono_error_set_type_load_class() + * Note that only the first failure is kept. + * + * LOCKING: Acquires the loader lock. + * + * \returns TRUE + */ +gboolean +mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...) +{ + if (!mono_class_has_failure (klass)) { + ERROR_DECL (prepare_error); + va_list args; + + va_start (args, fmt); + mono_error_vset_type_load_class (prepare_error, klass, fmt, args); + va_end (args); + + MonoErrorBoxed *box = mono_error_box (prepare_error, m_class_get_image (klass)); + mono_error_cleanup (prepare_error); + mono_class_set_failure (klass, box); + } + + return TRUE; +} + +void set_failure_type(FailureType failure_type) { + if (failure_type == DEFERRED_FAILURE) { + mono_class_set_deferred_type_load_failure_callback = mono_class_set_deferred_type_load_failure; + } else if (failure_type == IMMEDIATE_FAILURE) { + mono_class_set_deferred_type_load_failure_callback = mono_class_set_type_load_failure; + } else { + g_assert_not_reached (); + } +} \ No newline at end of file diff --git a/src/tests/issues.targets b/src/tests/issues.targets index d09d7b84f16daf..9a51fd6eb46569 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -2777,6 +2777,9 @@ Needs coreclr build + + https://github.com/dotnet/runtime/issues/86327 +