diff --git a/src/mono/mono/metadata/CMakeLists.txt b/src/mono/mono/metadata/CMakeLists.txt index dac92188d4f68d..39ec34205c6808 100644 --- a/src/mono/mono/metadata/CMakeLists.txt +++ b/src/mono/mono/metadata/CMakeLists.txt @@ -171,6 +171,8 @@ set(metadata_common_sources abi-details.h abi.c memory-manager.c + unsafe-accessor.h + unsafe-accessor.c icall-table.h ${icall_table_sources}) diff --git a/src/mono/mono/metadata/class-internals.h b/src/mono/mono/metadata/class-internals.h index 68f83b16ab2fca..3863d3213fa408 100644 --- a/src/mono/mono/metadata/class-internals.h +++ b/src/mono/mono/metadata/class-internals.h @@ -1421,6 +1421,9 @@ mono_method_has_no_body (MonoMethod *method); MONO_COMPONENT_API MonoMethodHeader* mono_method_get_header_internal (MonoMethod *method, MonoError *error); +gboolean +mono_method_metadata_has_header (MonoMethod *method); + MONO_COMPONENT_API void mono_method_get_param_names_internal (MonoMethod *method, const char **names); diff --git a/src/mono/mono/metadata/custom-attrs.c b/src/mono/mono/metadata/custom-attrs.c index 31a0be630fa05e..8fc9f47daf0af9 100644 --- a/src/mono/mono/metadata/custom-attrs.c +++ b/src/mono/mono/metadata/custom-attrs.c @@ -50,6 +50,7 @@ static gboolean type_is_reference (MonoType *type); static GENERATE_GET_CLASS_WITH_CACHE (custom_attribute_typed_argument, "System.Reflection", "CustomAttributeTypedArgument"); static GENERATE_GET_CLASS_WITH_CACHE (custom_attribute_named_argument, "System.Reflection", "CustomAttributeNamedArgument"); static GENERATE_TRY_GET_CLASS_WITH_CACHE (customattribute_data, "System.Reflection", "RuntimeCustomAttributeData"); +static GENERATE_TRY_GET_CLASS_WITH_CACHE (unsafe_accessor_attribute, "System.Runtime.CompilerServices", "UnsafeAccessorAttribute"); static MonoCustomAttrInfo* mono_custom_attrs_from_builders_handle (MonoImage *alloc_img, MonoImage *image, MonoArrayHandle cattrs, gboolean respect_cattr_visibility); @@ -2056,6 +2057,62 @@ mono_custom_attrs_from_method_checked (MonoMethod *method, MonoError *error) return mono_custom_attrs_from_index_checked (m_class_get_image (method->klass), idx, FALSE, error); } +gboolean +mono_method_get_unsafe_accessor_attr_data (MonoMethod *method, int *accessor_kind, char **member_name, MonoError *error) +{ + MonoCustomAttrInfo *cinfo = mono_custom_attrs_from_method_checked (method, error); + + if (!cinfo) + return FALSE; + + MonoClass *unsafeAccessor = mono_class_try_get_unsafe_accessor_attribute_class (); + MonoCustomAttrEntry *attr = NULL; + + for (int idx = 0; idx < cinfo->num_attrs; ++idx) { + MonoClass *ctor_class = cinfo->attrs [idx].ctor->klass; + if (ctor_class == unsafeAccessor) { + attr = &cinfo->attrs [idx]; + break; + } + } + + if (!attr){ + if (!cinfo->cached) + mono_custom_attrs_free(cinfo); + return FALSE; + } + + MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (m_class_get_image (attr->ctor->klass), attr->ctor, attr->data, attr->data_size, error); + + if (!is_ok (error)) { + mono_error_cleanup (error); + mono_reflection_free_custom_attr_data_args_noalloc (decoded_args); + if (!cinfo->cached) + mono_custom_attrs_free(cinfo); + return FALSE; + } + + g_assert (decoded_args->typed_args_num == 1); + *accessor_kind = *(int*)decoded_args->typed_args [0]->value.primitive; + + for (int i = 0; i < decoded_args->named_args_num; ++i) { + if (decoded_args->named_args_info [i].prop && !strcmp (decoded_args->named_args_info [i].prop->name, "Name")) { + const char *ptr = (const char*)decoded_args->named_args [i]->value.primitive; + uint32_t len = mono_metadata_decode_value (ptr, &ptr); + char *name = m_method_alloc0 (method, len + 1); + memcpy (name, ptr, len); + name[len] = 0; + *member_name = (char*)name; + } + } + + mono_reflection_free_custom_attr_data_args_noalloc (decoded_args); + if (!cinfo->cached) + mono_custom_attrs_free(cinfo); + + return TRUE; +} + /** * mono_custom_attrs_from_class: */ diff --git a/src/mono/mono/metadata/loader-internals.h b/src/mono/mono/metadata/loader-internals.h index 18fdb4dcbaabea..994d7d96c26420 100644 --- a/src/mono/mono/metadata/loader-internals.h +++ b/src/mono/mono/metadata/loader-internals.h @@ -88,6 +88,7 @@ typedef struct { GHashTable *cominterop_invoke_cache; GHashTable *cominterop_wrapper_cache; /* LOCKING: marshal lock */ GHashTable *thunk_invoke_cache; + GHashTable *unsafe_accessor_cache; } MonoWrapperCaches; /* Lock-free allocator */ diff --git a/src/mono/mono/metadata/loader.c b/src/mono/mono/metadata/loader.c index 81209134e64441..31b5d751bed7fd 100644 --- a/src/mono/mono/metadata/loader.c +++ b/src/mono/mono/metadata/loader.c @@ -2076,8 +2076,8 @@ mono_method_get_header_internal (MonoMethod *method, MonoError *error) g_assert (mono_metadata_token_table (method->token) == MONO_TABLE_METHOD); idx = mono_metadata_token_index (method->token); - if (G_UNLIKELY (img->has_updates)) - loc = mono_metadata_update_get_updated_method_rva (img, idx); + if (G_UNLIKELY (img->has_updates)) + loc = mono_metadata_update_get_updated_method_rva (img, idx); if (!loc) { rva = mono_metadata_decode_row_col (&img->tables [MONO_TABLE_METHOD], idx - 1, MONO_METHOD_RVA); @@ -2100,6 +2100,44 @@ mono_method_get_header_internal (MonoMethod *method, MonoError *error) return mono_metadata_parse_mh_full (img, container, (const char *)loc, error); } +gboolean +mono_method_metadata_has_header (MonoMethod *method) +{ + int idx; + guint32 rva; + MonoImage* img; + gpointer loc = NULL; + + img = m_class_get_image (method->klass); + + if (mono_method_has_no_body (method)) { + return FALSE; + } + + if (method->is_inflated) { + MonoMethodInflated *imethod = (MonoMethodInflated *) method; + return mono_method_metadata_has_header (imethod->declaring); + } + + if (method->wrapper_type != MONO_WRAPPER_NONE || method->sre_method) { + MonoMethodWrapper *mw = (MonoMethodWrapper *)method; + return mw->header != NULL; + } + + g_assert (mono_metadata_token_table (method->token) == MONO_TABLE_METHOD); + idx = mono_metadata_token_index (method->token); + + if (G_UNLIKELY (img->has_updates)) + loc = mono_metadata_update_get_updated_method_rva (img, idx); + + if (!loc) { + rva = mono_metadata_decode_row_col (&img->tables [MONO_TABLE_METHOD], idx - 1, MONO_METHOD_RVA); + loc = mono_image_rva_map (img, rva); + } + + return loc != NULL; +} + MonoMethodHeader* mono_method_get_header_checked (MonoMethod *method, MonoError *error) // Public function that must initialize MonoError for compatibility. diff --git a/src/mono/mono/metadata/marshal-lightweight.c b/src/mono/mono/metadata/marshal-lightweight.c index 8871b2f4eee4ea..0b4e787f5f2bd9 100644 --- a/src/mono/mono/metadata/marshal-lightweight.c +++ b/src/mono/mono/metadata/marshal-lightweight.c @@ -39,6 +39,7 @@ #include "mono/metadata/handle.h" #include "mono/metadata/custom-attrs-internals.h" #include "mono/metadata/icall-internals.h" +#include "mono/metadata/unsafe-accessor.h" #include "mono/utils/mono-tls.h" #include "mono/utils/mono-memory-model.h" #include "mono/utils/atomic.h" @@ -2319,6 +2320,79 @@ emit_array_accessor_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, Mo mono_mb_emit_byte (mb, CEE_RET); } +static void +emit_unsafe_accessor_field_wrapper (MonoMethodBuilder *mb, MonoMethod *accessor_method, MonoMethodSignature *sig, MonoGenericContext *ctx, MonoUnsafeAccessorKind kind, const char *member_name) +{ + // Field access requires a single argument for target type and a return type. + g_assert (kind == MONO_UNSAFE_ACCESSOR_FIELD || kind == MONO_UNSAFE_ACCESSOR_STATIC_FIELD); + g_assert (member_name != NULL); + + MonoType *target_type = sig->params[0]; // params[0] is the field's parent + MonoType *ret_type = sig->ret; + if (sig->param_count != 1 || target_type == NULL || sig->ret->type == MONO_TYPE_VOID) { + mono_mb_emit_exception_full (mb, "System", "BadImageFormatException", "Invalid usage of UnsafeAccessorAttribute."); + return; + } + + MonoClass *target_class = mono_class_from_mono_type_internal (target_type); + gboolean target_byref = m_type_is_byref (target_type); + gboolean target_valuetype = m_class_is_valuetype (target_class); + gboolean ret_byref = m_type_is_byref (ret_type); + if (!ret_byref || (kind == MONO_UNSAFE_ACCESSOR_FIELD && target_valuetype && !target_byref)) { + mono_mb_emit_exception_full (mb, "System", "BadImageFormatException", "Invalid usage of UnsafeAccessorAttribute."); + return; + } + + MonoClassField *target_field = mono_class_get_field_from_name_full (target_class, member_name, NULL); + if (target_field == NULL || !mono_metadata_type_equal_full (target_field->type, m_class_get_byval_arg (mono_class_from_mono_type_internal (ret_type)), TRUE)) { + mono_mb_emit_exception_full (mb, "System", "MissingFieldException", + g_strdup_printf("No '%s' in '%s'. Or the type of '%s' doesn't match", member_name, m_class_get_name (target_class), member_name)); + return; + } + gboolean is_field_static = !!(target_field->type->attrs & FIELD_ATTRIBUTE_STATIC); + if ((kind == MONO_UNSAFE_ACCESSOR_FIELD && is_field_static) || (kind == MONO_UNSAFE_ACCESSOR_STATIC_FIELD && !is_field_static)) { + mono_mb_emit_exception_full (mb, "System", "MissingFieldException", g_strdup_printf("UnsafeAccessorKind does not match expected static modifier on field '%s' in '%s'", member_name, m_class_get_name (target_class))); + return; + } + + if (kind == MONO_UNSAFE_ACCESSOR_FIELD) + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_op (mb, kind == MONO_UNSAFE_ACCESSOR_FIELD ? CEE_LDFLDA : CEE_LDSFLDA, target_field); + mono_mb_emit_byte (mb, CEE_RET); +} + +static void +emit_unsafe_accessor_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *accessor_method, MonoMethodSignature *sig, MonoGenericContext *ctx, MonoUnsafeAccessorKind kind, const char *member_name) +{ + if (accessor_method->is_generic) { + mono_mb_emit_exception_full (mb, "System", "NotImplementedException", "UnsafeAccessor_Generics"); + return; + } + + if (!m_method_is_static (accessor_method)) { + mono_mb_emit_exception_full (mb, "System", "BadImageFormatException", "UnsafeAccessor_NonStatic"); + return; + } + + switch (kind) { + case MONO_UNSAFE_ACCESSOR_FIELD: + case MONO_UNSAFE_ACCESSOR_STATIC_FIELD: + emit_unsafe_accessor_field_wrapper (mb, accessor_method, sig, ctx, kind, member_name); + return; + case MONO_UNSAFE_ACCESSOR_CTOR: + // TODO + mono_mb_emit_exception_full (mb, "System", "NotImplementedException", "UnsafeAccessor"); + return; + case MONO_UNSAFE_ACCESSOR_METHOD: + case MONO_UNSAFE_ACCESSOR_STATIC_METHOD: + // TODO + mono_mb_emit_exception_full (mb, "System", "NotImplementedException", "UnsafeAccessor"); + return; + default: + g_assert_not_reached(); // some unknown wrapper kind + } +} + static void emit_generic_array_helper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *csig) { @@ -3154,6 +3228,7 @@ mono_marshal_lightweight_init (void) cb.emit_synchronized_wrapper = emit_synchronized_wrapper_ilgen; cb.emit_unbox_wrapper = emit_unbox_wrapper_ilgen; cb.emit_array_accessor_wrapper = emit_array_accessor_wrapper_ilgen; + cb.emit_unsafe_accessor_wrapper = emit_unsafe_accessor_wrapper_ilgen; cb.emit_generic_array_helper = emit_generic_array_helper_ilgen; cb.emit_thunk_invoke_wrapper = emit_thunk_invoke_wrapper_ilgen; cb.emit_create_string_hack = emit_create_string_hack_ilgen; diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 0307dc16a515aa..0e48c3d024664a 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -5094,6 +5094,96 @@ mono_marshal_get_array_accessor_wrapper (MonoMethod *method) return res; } +/* + * mono_marshal_get_unsafe_accessor_wrapper: + * + * Return a wrapper for an extern [UnsafeAccessor] method that accesses a member of some class. + * + * member_name can be NULL in which case the name of the member is the same as the name of the accessor method + * + * If the kind is Field or StaticField the accessor_method must have a signature like: + * ref FieldType AccessorMethod (TargetClassOrStruct @this); + * or + * ref FieldType AccessorMethod (ref TargetStruct @this); + * + * If the kind is Method or StaticMethod, the accessor_method must have a signature like: + * ReturnType AccessorMethod (TargetClassOrStruct @this[, FirstArg arg1[, SecondArg arg2[, ...]]]) + * + * where the member method is + * + * class TargetClassOrStruct { + * ReturnType MemberName ([FirstArg arg1[, SecondArg arg2[, ...]]]); + * } + * + * + * or + * ReturnType AccessorMethod (ref TargetStruct @this[, FirstArg arg1[, SecondArg arg2[, ...]]]) + * + * where the member method is + * + * struct TargetStruct { + * ReturnType MemberName ([FirstArg arg1[, SecondArg arg2[, ...]]]); + * } + * + * + * If the kind is Constructor, the accessor_method must have a signature like: + * TargetClass AccessorMethod ([FirstArg arg1[, SecondArg arg2[, ...]]]); + */ +MonoMethod * +mono_marshal_get_unsafe_accessor_wrapper (MonoMethod *accessor_method, MonoUnsafeAccessorKind kind, const char *member_name) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + GHashTable *cache; + MonoGenericContext *ctx = NULL; + MonoMethod *orig_method = NULL; + WrapperInfo *info; + + if (member_name == NULL) + member_name = accessor_method->name; + + /* + * Check cache + */ + if (ctx) { + cache = NULL; + g_assert_not_reached (); + } else { + cache = get_cache (&mono_method_get_wrapper_cache (accessor_method)->unsafe_accessor_cache, mono_aligned_addr_hash, NULL); + if ((res = mono_marshal_find_in_cache (cache, accessor_method))) + return res; + } + + sig = mono_metadata_signature_dup_full (get_method_image (accessor_method), mono_method_signature_internal (accessor_method)); + sig->pinvoke = 0; + + mb = mono_mb_new (accessor_method->klass, accessor_method->name, MONO_WRAPPER_OTHER); + + get_marshal_cb ()->mb_skip_visibility (mb); + + get_marshal_cb ()->emit_unsafe_accessor_wrapper (mb, accessor_method, sig, ctx, kind, member_name); + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_UNSAFE_ACCESSOR); + info->d.unsafe_accessor.method = accessor_method; + info->d.unsafe_accessor.kind = kind; + info->d.unsafe_accessor.member_name = member_name; + + if (ctx) { + MonoMethod *def; + def = mono_mb_create_and_cache_full (cache, accessor_method, mb, sig, sig->param_count + 16, info, NULL); + res = cache_generic_wrapper (cache, orig_method, def, ctx, orig_method); + } else { + res = mono_mb_create_and_cache_full (cache, accessor_method, mb, sig, sig->param_count + 16, info, NULL); + } + mono_mb_free (mb); + + // TODO: remove before merging + mono_method_print_code (res); + + return res; +} + #ifdef HOST_WIN32 static void* @@ -6398,4 +6488,5 @@ mono_wrapper_caches_free (MonoWrapperCaches *cache) free_hash (cache->cominterop_invoke_cache); free_hash (cache->cominterop_wrapper_cache); free_hash (cache->thunk_invoke_cache); + free_hash (cache->unsafe_accessor_cache); } diff --git a/src/mono/mono/metadata/marshal.h b/src/mono/mono/metadata/marshal.h index 9f261cf01a7fc9..f78962313783ce 100644 --- a/src/mono/mono/metadata/marshal.h +++ b/src/mono/mono/metadata/marshal.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -135,7 +136,8 @@ typedef enum { WRAPPER_SUBTYPE_INTERP_IN, WRAPPER_SUBTYPE_INTERP_LMF, WRAPPER_SUBTYPE_AOT_INIT, - WRAPPER_SUBTYPE_LLVM_FUNC + WRAPPER_SUBTYPE_LLVM_FUNC, + WRAPPER_SUBTYPE_UNSAFE_ACCESSOR, } WrapperSubtype; typedef struct { @@ -239,6 +241,12 @@ typedef struct { MonoMethodSignature *sig; } NativeFuncWrapperInfo; +typedef struct { + MonoMethod *method; + MonoUnsafeAccessorKind kind; + const char *member_name; /* the member we're accessing */ +} UnsafeAccessorWrapperInfo; + /* * This structure contains additional information to uniquely identify a given wrapper * method. It can be retrieved by mono_marshal_get_wrapper_info () for certain types @@ -285,6 +293,8 @@ typedef struct { LLVMFuncWrapperInfo llvm_func; /* NATIVE_FUNC_INDIRECT */ NativeFuncWrapperInfo native_func; + /* UNSAFE_ACCESSOR */ + UnsafeAccessorWrapperInfo unsafe_accessor; } d; } WrapperInfo; @@ -332,6 +342,7 @@ typedef struct { 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); + void (*emit_unsafe_accessor_wrapper) (MonoMethodBuilder *mb, MonoMethod *accessor_method, MonoMethodSignature *sig, MonoGenericContext *ctx, MonoUnsafeAccessorKind kind, const char *member_name); void (*emit_generic_array_helper) (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *csig); void (*emit_thunk_invoke_wrapper) (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *csig); void (*emit_create_string_hack) (MonoMethodBuilder *mb, MonoMethodSignature *csig, MonoMethod *res); @@ -588,6 +599,9 @@ mono_marshal_get_gsharedvt_in_wrapper (void); MonoMethod* mono_marshal_get_gsharedvt_out_wrapper (void); +MonoMethod* +mono_marshal_get_unsafe_accessor_wrapper (MonoMethod *accessor_method, MonoUnsafeAccessorKind kind, const char *member_name); + void mono_marshal_free_dynamic_wrappers (MonoMethod *method); diff --git a/src/mono/mono/metadata/reflection-internals.h b/src/mono/mono/metadata/reflection-internals.h index cb8d87a85038c4..01e3d136e0aab7 100644 --- a/src/mono/mono/metadata/reflection-internals.h +++ b/src/mono/mono/metadata/reflection-internals.h @@ -65,6 +65,8 @@ MonoCustomAttrInfo* mono_custom_attrs_from_index_checked (MonoImage *image, uint32_t idx, gboolean ignore_missing, MonoError *error); MONO_COMPONENT_API MonoCustomAttrInfo* mono_custom_attrs_from_method_checked (MonoMethod *method, MonoError *error); +gboolean +mono_method_get_unsafe_accessor_attr_data (MonoMethod *method, int *accessor_kind, char **member_name, MonoError *error); MONO_COMPONENT_API MonoCustomAttrInfo* mono_custom_attrs_from_class_checked (MonoClass *klass, MonoError *error); MONO_COMPONENT_API MonoCustomAttrInfo* diff --git a/src/mono/mono/metadata/unsafe-accessor.c b/src/mono/mono/metadata/unsafe-accessor.c new file mode 100644 index 00000000000000..070b05d93d5705 --- /dev/null +++ b/src/mono/mono/metadata/unsafe-accessor.c @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#include "config.h" +#include +#include "mono/metadata/unsafe-accessor.h" +#include "mono/utils/mono-compiler.h" + +MONO_EMPTY_SOURCE_FILE (unsafe_accessor) diff --git a/src/mono/mono/metadata/unsafe-accessor.h b/src/mono/mono/metadata/unsafe-accessor.h new file mode 100644 index 00000000000000..dd50cc6ca97ab4 --- /dev/null +++ b/src/mono/mono/metadata/unsafe-accessor.h @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#ifndef __MONO_METADATA_UNSAFE_ACCESSOR_H__ +#define __MONO_METADATA_UNSAFE_ACCESSOR_H__ + +/* keep in sync with System.Runtime.CompilerServices.UnsafeAccessorKind + * https://github.com/dotnet/runtime/blob/a2c19cd005a1130ba7f921e0264287cfbfa8513c/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/UnsafeAccessorAttribute.cs#L9-L35 + */ +typedef enum { + MONO_UNSAFE_ACCESSOR_CTOR, + MONO_UNSAFE_ACCESSOR_METHOD, + MONO_UNSAFE_ACCESSOR_STATIC_METHOD, + MONO_UNSAFE_ACCESSOR_FIELD, + MONO_UNSAFE_ACCESSOR_STATIC_FIELD, +} MonoUnsafeAccessorKind; + +#endif /* __MONO_METADATA_UNSAFE_ACCESSOR_H__ */ diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index c7e2141171c759..ca41c34b4c3ac5 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -3841,6 +3841,14 @@ encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8 encode_method_ref (acfg, info->d.synchronized_inner.method, p, &p); else if (info->subtype == WRAPPER_SUBTYPE_ARRAY_ACCESSOR) encode_method_ref (acfg, info->d.array_accessor.method, p, &p); + else if (info->subtype == WRAPPER_SUBTYPE_UNSAFE_ACCESSOR) { + encode_method_ref (acfg, info->d.unsafe_accessor.method, p, &p); + encode_value (info->d.unsafe_accessor.kind, p, &p); + /* WISH: is there some kind of string heap token we could use here? */ + uint32_t len = (uint32_t) strlen (info->d.unsafe_accessor.member_name); + encode_value (len, p, &p); + encode_string (info->d.unsafe_accessor.member_name, p, &p); + } else if (info->subtype == WRAPPER_SUBTYPE_INTERP_IN) encode_signature (acfg, info->d.interp_in.sig, p, &p); else if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG) @@ -5236,6 +5244,25 @@ add_full_aot_wrappers (MonoAotCompile *acfg) add_method (acfg, mono_marshal_get_ptr_to_struct (klass)); } } + + /* unsafe accessor wrappers */ + rows = table_info_get_rows (&acfg->image->tables [MONO_TABLE_METHOD]); + for (int i = 0; i < rows; ++i) { + ERROR_DECL (error); + token = MONO_TOKEN_METHOD_DEF | (i + 1); + method = mono_get_method_checked (acfg->image, token, NULL, NULL, error); + report_loader_error (acfg, error, TRUE, "Failed to load method token 0x%x due to %s\n", i, mono_error_get_message (error)); + + if (G_LIKELY (mono_method_metadata_has_header (method))) + continue; + + char *member_name = NULL; + int accessor_kind = -1; + if (mono_method_get_unsafe_accessor_attr_data (method, &accessor_kind, &member_name, error)) { + add_extra_method (acfg, mono_marshal_get_unsafe_accessor_wrapper (method, (MonoUnsafeAccessorKind)accessor_kind, member_name)); + } + } + } static void @@ -10154,6 +10181,9 @@ append_mangled_wrapper_subtype (GString *s, WrapperSubtype subtype) case WRAPPER_SUBTYPE_ARRAY_ACCESSOR: label = "array_acc"; break; + case WRAPPER_SUBTYPE_UNSAFE_ACCESSOR: + label = "unsafe_acc"; + break; case WRAPPER_SUBTYPE_GENERIC_ARRAY_HELPER: label = "generic_arry_help"; break; @@ -10320,6 +10350,8 @@ append_mangled_wrapper (GString *s, MonoMethod *method) success = success && append_mangled_method (s, info->d.synchronized_inner.method); else if (info->subtype == WRAPPER_SUBTYPE_ARRAY_ACCESSOR) success = success && append_mangled_method (s, info->d.array_accessor.method); + else if (info->subtype == WRAPPER_SUBTYPE_UNSAFE_ACCESSOR) + success = success && append_mangled_method (s, info->d.unsafe_accessor.method); else if (info->subtype == WRAPPER_SUBTYPE_INTERP_IN) append_mangled_signature (s, info->d.interp_in.sig); else if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG) { diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c index e6c0904e65b79a..86c70dba388621 100644 --- a/src/mono/mono/mini/aot-runtime.c +++ b/src/mono/mono/mini/aot-runtime.c @@ -1066,6 +1066,15 @@ decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod if (!m) return FALSE; ref->method = mono_marshal_get_array_accessor_wrapper (m); + } else if (subtype == WRAPPER_SUBTYPE_UNSAFE_ACCESSOR) { + MonoMethod *m = decode_resolve_method_ref (module, p, &p, error); + if (!m) + return FALSE; + MonoUnsafeAccessorKind kind = (MonoUnsafeAccessorKind) decode_value (p, &p); + uint32_t name_len = decode_value (p, &p); + const char *member_name = (const char*)p; + p += name_len + 1; + ref->method = mono_marshal_get_unsafe_accessor_wrapper (m, kind, member_name); } else if (subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN) { ref->method = mono_marshal_get_gsharedvt_in_wrapper (); } else if (subtype == WRAPPER_SUBTYPE_GSHAREDVT_OUT) { diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 32c9b2e3468a84..bc52e37f2dd213 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -11419,6 +11419,13 @@ mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, Mon return_if_nok (error); } + int accessor_kind = -1; + char *member_name = NULL; + if (!header && mono_method_get_unsafe_accessor_attr_data (method, &accessor_kind, &member_name, error)) { + method = mono_marshal_get_unsafe_accessor_wrapper (method, (MonoUnsafeAccessorKind)accessor_kind, member_name); + g_assert (method); + } + if (!header) { header = mono_method_get_header_checked (method, error); return_if_nok (error); diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 933b6f8b48db62..1d01a77fb3c14a 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -2649,6 +2649,22 @@ compile_special (MonoMethod *method, MonoError *error) } } + gboolean has_header = mono_method_metadata_has_header (method); + if (G_UNLIKELY (!has_header)) { + char *member_name = NULL; + int accessor_kind = -1; + if (mono_method_get_unsafe_accessor_attr_data (method, &accessor_kind, &member_name, error)) { + MonoMethod *wrapper = mono_marshal_get_unsafe_accessor_wrapper (method, (MonoUnsafeAccessorKind)accessor_kind, member_name); + gpointer compiled_wrapper = mono_jit_compile_method_jit_only (wrapper, error); + return_val_if_nok (error, NULL); + code = mono_get_addr_from_ftnptr (compiled_wrapper); + jinfo = mini_jit_info_table_find (code); + if (jinfo) + MONO_PROFILER_RAISE (jit_done, (method, jinfo)); + return code; + } + } + return NULL; } diff --git a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs index 982b2e39fc4b5b..b155c0a445e070 100644 --- a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs +++ b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs @@ -155,7 +155,6 @@ public static void Verify_CallCtorAsMethodValue() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/86040", TestRuntimes.Mono)] public static void Verify_AccessStaticFieldClass() { Console.WriteLine($"Running {nameof(Verify_AccessStaticFieldClass)}"); @@ -180,7 +179,6 @@ public static void Verify_AccessFieldClass() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/86040", TestRuntimes.Mono)] public static void Verify_AccessStaticFieldValue() { Console.WriteLine($"Running {nameof(Verify_AccessStaticFieldValue)}"); @@ -192,7 +190,6 @@ public static void Verify_AccessStaticFieldValue() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/86040", TestRuntimes.Mono)] public static void Verify_AccessFieldValue() { Console.WriteLine($"Running {nameof(Verify_AccessFieldValue)}"); @@ -389,6 +386,16 @@ public static void Verify_InvalidTargetUnsafeAccessor() AssertExtensions.ThrowsMissingMemberException( isNativeAot ? null : DoesNotExist, () => FieldNotFound(null)); + AssertExtensions.ThrowsMissingMemberException( + isNativeAot ? null : UserDataClass.StaticFieldName, + () => + { + UserDataValue value = default; + FieldNotFoundStaticMismatch1(ref value); + }); + AssertExtensions.ThrowsMissingMemberException( + isNativeAot ? null : UserDataValue.FieldName, + () => FieldNotFoundStaticMismatch2(default)); AssertExtensions.ThrowsMissingMemberException( isNativeAot ? null : DoesNotExist, () => StaticFieldNotFound(null)); @@ -409,6 +416,12 @@ public static void Verify_InvalidTargetUnsafeAccessor() [UnsafeAccessor(UnsafeAccessorKind.Field, Name=DoesNotExist)] extern static ref string FieldNotFound(UserDataClass d); + [UnsafeAccessor(UnsafeAccessorKind.Field, Name=UserDataValue.StaticFieldName)] + extern static ref string FieldNotFoundStaticMismatch1(ref UserDataValue d); + + [UnsafeAccessor(UnsafeAccessorKind.StaticField, Name=UserDataValue.FieldName)] + extern static ref string FieldNotFoundStaticMismatch2(UserDataValue d); + [UnsafeAccessor(UnsafeAccessorKind.StaticField, Name=DoesNotExist)] extern static ref string StaticFieldNotFound(UserDataClass d);