Skip to content

Commit 45db21d

Browse files
fanyang-monolambdageekAaronRobinsonMSFT
authored
[Mono] UnsafeAccessorAttribute non-generic support for field (#88626)
* Detect an UnsafeAccessorAttribute for method with interpreter * Change field to property * Get Kind from typed_args * define MonoUnsafeAccessorKind enum * Add the frontend for JIT * Add mono_marshal_get_unsafe_accessor_wrapper and WRAPPER_SUBTYPE_UNSAFE_ACCESSOR And the associated AOT compiler/runtime and marshaling caching boilerplate. * [interp] get the unsafe accessor wrapper * fix: skip visibility in unsafe accessor wrappers that is the whole point of them * fix: decode the length and copy the name from UnsafeAccessorAttribute The name has a length as a prefix and doesn not have a null terminator * [mini] compile wrapper * [aot] Emit unsafe accessor wrappers to the AOT image * Add the method to emit wrapper for field * Fix typo * Remove assertion for interpreter * Fix format and replace assertion with proper exception * Free the memory and throw proper NotImplementedException * Enable StaticField and Field tests Co-authored-by: Aleksey Kliger <[email protected]> Co-authored-by: Aaron Robinson <[email protected]>
1 parent 940c26d commit 45db21d

16 files changed

+395
-6
lines changed

src/mono/mono/metadata/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ set(metadata_common_sources
171171
abi-details.h
172172
abi.c
173173
memory-manager.c
174+
unsafe-accessor.h
175+
unsafe-accessor.c
174176
icall-table.h
175177
${icall_table_sources})
176178

src/mono/mono/metadata/class-internals.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1421,6 +1421,9 @@ mono_method_has_no_body (MonoMethod *method);
14211421
MONO_COMPONENT_API MonoMethodHeader*
14221422
mono_method_get_header_internal (MonoMethod *method, MonoError *error);
14231423

1424+
gboolean
1425+
mono_method_metadata_has_header (MonoMethod *method);
1426+
14241427
MONO_COMPONENT_API void
14251428
mono_method_get_param_names_internal (MonoMethod *method, const char **names);
14261429

src/mono/mono/metadata/custom-attrs.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ static gboolean type_is_reference (MonoType *type);
5050
static GENERATE_GET_CLASS_WITH_CACHE (custom_attribute_typed_argument, "System.Reflection", "CustomAttributeTypedArgument");
5151
static GENERATE_GET_CLASS_WITH_CACHE (custom_attribute_named_argument, "System.Reflection", "CustomAttributeNamedArgument");
5252
static GENERATE_TRY_GET_CLASS_WITH_CACHE (customattribute_data, "System.Reflection", "RuntimeCustomAttributeData");
53+
static GENERATE_TRY_GET_CLASS_WITH_CACHE (unsafe_accessor_attribute, "System.Runtime.CompilerServices", "UnsafeAccessorAttribute");
5354

5455
static MonoCustomAttrInfo*
5556
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)
20562057
return mono_custom_attrs_from_index_checked (m_class_get_image (method->klass), idx, FALSE, error);
20572058
}
20582059

2060+
gboolean
2061+
mono_method_get_unsafe_accessor_attr_data (MonoMethod *method, int *accessor_kind, char **member_name, MonoError *error)
2062+
{
2063+
MonoCustomAttrInfo *cinfo = mono_custom_attrs_from_method_checked (method, error);
2064+
2065+
if (!cinfo)
2066+
return FALSE;
2067+
2068+
MonoClass *unsafeAccessor = mono_class_try_get_unsafe_accessor_attribute_class ();
2069+
MonoCustomAttrEntry *attr = NULL;
2070+
2071+
for (int idx = 0; idx < cinfo->num_attrs; ++idx) {
2072+
MonoClass *ctor_class = cinfo->attrs [idx].ctor->klass;
2073+
if (ctor_class == unsafeAccessor) {
2074+
attr = &cinfo->attrs [idx];
2075+
break;
2076+
}
2077+
}
2078+
2079+
if (!attr){
2080+
if (!cinfo->cached)
2081+
mono_custom_attrs_free(cinfo);
2082+
return FALSE;
2083+
}
2084+
2085+
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);
2086+
2087+
if (!is_ok (error)) {
2088+
mono_error_cleanup (error);
2089+
mono_reflection_free_custom_attr_data_args_noalloc (decoded_args);
2090+
if (!cinfo->cached)
2091+
mono_custom_attrs_free(cinfo);
2092+
return FALSE;
2093+
}
2094+
2095+
g_assert (decoded_args->typed_args_num == 1);
2096+
*accessor_kind = *(int*)decoded_args->typed_args [0]->value.primitive;
2097+
2098+
for (int i = 0; i < decoded_args->named_args_num; ++i) {
2099+
if (decoded_args->named_args_info [i].prop && !strcmp (decoded_args->named_args_info [i].prop->name, "Name")) {
2100+
const char *ptr = (const char*)decoded_args->named_args [i]->value.primitive;
2101+
uint32_t len = mono_metadata_decode_value (ptr, &ptr);
2102+
char *name = m_method_alloc0 (method, len + 1);
2103+
memcpy (name, ptr, len);
2104+
name[len] = 0;
2105+
*member_name = (char*)name;
2106+
}
2107+
}
2108+
2109+
mono_reflection_free_custom_attr_data_args_noalloc (decoded_args);
2110+
if (!cinfo->cached)
2111+
mono_custom_attrs_free(cinfo);
2112+
2113+
return TRUE;
2114+
}
2115+
20592116
/**
20602117
* mono_custom_attrs_from_class:
20612118
*/

src/mono/mono/metadata/loader-internals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ typedef struct {
8888
GHashTable *cominterop_invoke_cache;
8989
GHashTable *cominterop_wrapper_cache; /* LOCKING: marshal lock */
9090
GHashTable *thunk_invoke_cache;
91+
GHashTable *unsafe_accessor_cache;
9192
} MonoWrapperCaches;
9293

9394
/* Lock-free allocator */

src/mono/mono/metadata/loader.c

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2076,8 +2076,8 @@ mono_method_get_header_internal (MonoMethod *method, MonoError *error)
20762076
g_assert (mono_metadata_token_table (method->token) == MONO_TABLE_METHOD);
20772077
idx = mono_metadata_token_index (method->token);
20782078

2079-
if (G_UNLIKELY (img->has_updates))
2080-
loc = mono_metadata_update_get_updated_method_rva (img, idx);
2079+
if (G_UNLIKELY (img->has_updates))
2080+
loc = mono_metadata_update_get_updated_method_rva (img, idx);
20812081

20822082
if (!loc) {
20832083
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)
21002100
return mono_metadata_parse_mh_full (img, container, (const char *)loc, error);
21012101
}
21022102

2103+
gboolean
2104+
mono_method_metadata_has_header (MonoMethod *method)
2105+
{
2106+
int idx;
2107+
guint32 rva;
2108+
MonoImage* img;
2109+
gpointer loc = NULL;
2110+
2111+
img = m_class_get_image (method->klass);
2112+
2113+
if (mono_method_has_no_body (method)) {
2114+
return FALSE;
2115+
}
2116+
2117+
if (method->is_inflated) {
2118+
MonoMethodInflated *imethod = (MonoMethodInflated *) method;
2119+
return mono_method_metadata_has_header (imethod->declaring);
2120+
}
2121+
2122+
if (method->wrapper_type != MONO_WRAPPER_NONE || method->sre_method) {
2123+
MonoMethodWrapper *mw = (MonoMethodWrapper *)method;
2124+
return mw->header != NULL;
2125+
}
2126+
2127+
g_assert (mono_metadata_token_table (method->token) == MONO_TABLE_METHOD);
2128+
idx = mono_metadata_token_index (method->token);
2129+
2130+
if (G_UNLIKELY (img->has_updates))
2131+
loc = mono_metadata_update_get_updated_method_rva (img, idx);
2132+
2133+
if (!loc) {
2134+
rva = mono_metadata_decode_row_col (&img->tables [MONO_TABLE_METHOD], idx - 1, MONO_METHOD_RVA);
2135+
loc = mono_image_rva_map (img, rva);
2136+
}
2137+
2138+
return loc != NULL;
2139+
}
2140+
21032141
MonoMethodHeader*
21042142
mono_method_get_header_checked (MonoMethod *method, MonoError *error)
21052143
// Public function that must initialize MonoError for compatibility.

src/mono/mono/metadata/marshal-lightweight.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "mono/metadata/handle.h"
4040
#include "mono/metadata/custom-attrs-internals.h"
4141
#include "mono/metadata/icall-internals.h"
42+
#include "mono/metadata/unsafe-accessor.h"
4243
#include "mono/utils/mono-tls.h"
4344
#include "mono/utils/mono-memory-model.h"
4445
#include "mono/utils/atomic.h"
@@ -2319,6 +2320,79 @@ emit_array_accessor_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, Mo
23192320
mono_mb_emit_byte (mb, CEE_RET);
23202321
}
23212322

2323+
static void
2324+
emit_unsafe_accessor_field_wrapper (MonoMethodBuilder *mb, MonoMethod *accessor_method, MonoMethodSignature *sig, MonoGenericContext *ctx, MonoUnsafeAccessorKind kind, const char *member_name)
2325+
{
2326+
// Field access requires a single argument for target type and a return type.
2327+
g_assert (kind == MONO_UNSAFE_ACCESSOR_FIELD || kind == MONO_UNSAFE_ACCESSOR_STATIC_FIELD);
2328+
g_assert (member_name != NULL);
2329+
2330+
MonoType *target_type = sig->params[0]; // params[0] is the field's parent
2331+
MonoType *ret_type = sig->ret;
2332+
if (sig->param_count != 1 || target_type == NULL || sig->ret->type == MONO_TYPE_VOID) {
2333+
mono_mb_emit_exception_full (mb, "System", "BadImageFormatException", "Invalid usage of UnsafeAccessorAttribute.");
2334+
return;
2335+
}
2336+
2337+
MonoClass *target_class = mono_class_from_mono_type_internal (target_type);
2338+
gboolean target_byref = m_type_is_byref (target_type);
2339+
gboolean target_valuetype = m_class_is_valuetype (target_class);
2340+
gboolean ret_byref = m_type_is_byref (ret_type);
2341+
if (!ret_byref || (kind == MONO_UNSAFE_ACCESSOR_FIELD && target_valuetype && !target_byref)) {
2342+
mono_mb_emit_exception_full (mb, "System", "BadImageFormatException", "Invalid usage of UnsafeAccessorAttribute.");
2343+
return;
2344+
}
2345+
2346+
MonoClassField *target_field = mono_class_get_field_from_name_full (target_class, member_name, NULL);
2347+
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)) {
2348+
mono_mb_emit_exception_full (mb, "System", "MissingFieldException",
2349+
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));
2350+
return;
2351+
}
2352+
gboolean is_field_static = !!(target_field->type->attrs & FIELD_ATTRIBUTE_STATIC);
2353+
if ((kind == MONO_UNSAFE_ACCESSOR_FIELD && is_field_static) || (kind == MONO_UNSAFE_ACCESSOR_STATIC_FIELD && !is_field_static)) {
2354+
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)));
2355+
return;
2356+
}
2357+
2358+
if (kind == MONO_UNSAFE_ACCESSOR_FIELD)
2359+
mono_mb_emit_ldarg (mb, 0);
2360+
mono_mb_emit_op (mb, kind == MONO_UNSAFE_ACCESSOR_FIELD ? CEE_LDFLDA : CEE_LDSFLDA, target_field);
2361+
mono_mb_emit_byte (mb, CEE_RET);
2362+
}
2363+
2364+
static void
2365+
emit_unsafe_accessor_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *accessor_method, MonoMethodSignature *sig, MonoGenericContext *ctx, MonoUnsafeAccessorKind kind, const char *member_name)
2366+
{
2367+
if (accessor_method->is_generic) {
2368+
mono_mb_emit_exception_full (mb, "System", "NotImplementedException", "UnsafeAccessor_Generics");
2369+
return;
2370+
}
2371+
2372+
if (!m_method_is_static (accessor_method)) {
2373+
mono_mb_emit_exception_full (mb, "System", "BadImageFormatException", "UnsafeAccessor_NonStatic");
2374+
return;
2375+
}
2376+
2377+
switch (kind) {
2378+
case MONO_UNSAFE_ACCESSOR_FIELD:
2379+
case MONO_UNSAFE_ACCESSOR_STATIC_FIELD:
2380+
emit_unsafe_accessor_field_wrapper (mb, accessor_method, sig, ctx, kind, member_name);
2381+
return;
2382+
case MONO_UNSAFE_ACCESSOR_CTOR:
2383+
// TODO
2384+
mono_mb_emit_exception_full (mb, "System", "NotImplementedException", "UnsafeAccessor");
2385+
return;
2386+
case MONO_UNSAFE_ACCESSOR_METHOD:
2387+
case MONO_UNSAFE_ACCESSOR_STATIC_METHOD:
2388+
// TODO
2389+
mono_mb_emit_exception_full (mb, "System", "NotImplementedException", "UnsafeAccessor");
2390+
return;
2391+
default:
2392+
g_assert_not_reached(); // some unknown wrapper kind
2393+
}
2394+
}
2395+
23222396
static void
23232397
emit_generic_array_helper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *csig)
23242398
{
@@ -3158,6 +3232,7 @@ mono_marshal_lightweight_init (void)
31583232
cb.emit_synchronized_wrapper = emit_synchronized_wrapper_ilgen;
31593233
cb.emit_unbox_wrapper = emit_unbox_wrapper_ilgen;
31603234
cb.emit_array_accessor_wrapper = emit_array_accessor_wrapper_ilgen;
3235+
cb.emit_unsafe_accessor_wrapper = emit_unsafe_accessor_wrapper_ilgen;
31613236
cb.emit_generic_array_helper = emit_generic_array_helper_ilgen;
31623237
cb.emit_thunk_invoke_wrapper = emit_thunk_invoke_wrapper_ilgen;
31633238
cb.emit_create_string_hack = emit_create_string_hack_ilgen;

src/mono/mono/metadata/marshal.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5094,6 +5094,96 @@ mono_marshal_get_array_accessor_wrapper (MonoMethod *method)
50945094
return res;
50955095
}
50965096

5097+
/*
5098+
* mono_marshal_get_unsafe_accessor_wrapper:
5099+
*
5100+
* Return a wrapper for an extern [UnsafeAccessor] method that accesses a member of some class.
5101+
*
5102+
* member_name can be NULL in which case the name of the member is the same as the name of the accessor method
5103+
*
5104+
* If the kind is Field or StaticField the accessor_method must have a signature like:
5105+
* ref FieldType AccessorMethod (TargetClassOrStruct @this);
5106+
* or
5107+
* ref FieldType AccessorMethod (ref TargetStruct @this);
5108+
*
5109+
* If the kind is Method or StaticMethod, the accessor_method must have a signature like:
5110+
* ReturnType AccessorMethod (TargetClassOrStruct @this[, FirstArg arg1[, SecondArg arg2[, ...]]])
5111+
*
5112+
* where the member method is
5113+
*
5114+
* class TargetClassOrStruct {
5115+
* ReturnType MemberName ([FirstArg arg1[, SecondArg arg2[, ...]]]);
5116+
* }
5117+
*
5118+
*
5119+
* or
5120+
* ReturnType AccessorMethod (ref TargetStruct @this[, FirstArg arg1[, SecondArg arg2[, ...]]])
5121+
*
5122+
* where the member method is
5123+
*
5124+
* struct TargetStruct {
5125+
* ReturnType MemberName ([FirstArg arg1[, SecondArg arg2[, ...]]]);
5126+
* }
5127+
*
5128+
*
5129+
* If the kind is Constructor, the accessor_method must have a signature like:
5130+
* TargetClass AccessorMethod ([FirstArg arg1[, SecondArg arg2[, ...]]]);
5131+
*/
5132+
MonoMethod *
5133+
mono_marshal_get_unsafe_accessor_wrapper (MonoMethod *accessor_method, MonoUnsafeAccessorKind kind, const char *member_name)
5134+
{
5135+
MonoMethodSignature *sig;
5136+
MonoMethodBuilder *mb;
5137+
MonoMethod *res;
5138+
GHashTable *cache;
5139+
MonoGenericContext *ctx = NULL;
5140+
MonoMethod *orig_method = NULL;
5141+
WrapperInfo *info;
5142+
5143+
if (member_name == NULL)
5144+
member_name = accessor_method->name;
5145+
5146+
/*
5147+
* Check cache
5148+
*/
5149+
if (ctx) {
5150+
cache = NULL;
5151+
g_assert_not_reached ();
5152+
} else {
5153+
cache = get_cache (&mono_method_get_wrapper_cache (accessor_method)->unsafe_accessor_cache, mono_aligned_addr_hash, NULL);
5154+
if ((res = mono_marshal_find_in_cache (cache, accessor_method)))
5155+
return res;
5156+
}
5157+
5158+
sig = mono_metadata_signature_dup_full (get_method_image (accessor_method), mono_method_signature_internal (accessor_method));
5159+
sig->pinvoke = 0;
5160+
5161+
mb = mono_mb_new (accessor_method->klass, accessor_method->name, MONO_WRAPPER_OTHER);
5162+
5163+
get_marshal_cb ()->mb_skip_visibility (mb);
5164+
5165+
get_marshal_cb ()->emit_unsafe_accessor_wrapper (mb, accessor_method, sig, ctx, kind, member_name);
5166+
5167+
info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_UNSAFE_ACCESSOR);
5168+
info->d.unsafe_accessor.method = accessor_method;
5169+
info->d.unsafe_accessor.kind = kind;
5170+
info->d.unsafe_accessor.member_name = member_name;
5171+
5172+
if (ctx) {
5173+
MonoMethod *def;
5174+
def = mono_mb_create_and_cache_full (cache, accessor_method, mb, sig, sig->param_count + 16, info, NULL);
5175+
res = cache_generic_wrapper (cache, orig_method, def, ctx, orig_method);
5176+
} else {
5177+
res = mono_mb_create_and_cache_full (cache, accessor_method, mb, sig, sig->param_count + 16, info, NULL);
5178+
}
5179+
mono_mb_free (mb);
5180+
5181+
// TODO: remove before merging
5182+
mono_method_print_code (res);
5183+
5184+
return res;
5185+
}
5186+
50975187
#ifdef HOST_WIN32
50985188

50995189
static void*
@@ -6398,4 +6488,5 @@ mono_wrapper_caches_free (MonoWrapperCaches *cache)
63986488
free_hash (cache->cominterop_invoke_cache);
63996489
free_hash (cache->cominterop_wrapper_cache);
64006490
free_hash (cache->thunk_invoke_cache);
6491+
free_hash (cache->unsafe_accessor_cache);
64016492
}

0 commit comments

Comments
 (0)