Skip to content

Commit e14174b

Browse files
authored
[mono] Mono support for InlineArrayAttribute (#83776)
* Add InlineArray attribute in class init * Add constraints for InlineArrayAttribute * Consider the struct field as an array in compute_class_bitmap * Make a note on marshalling structs with InlineArrayAttribute * Enable InlineArray tests on Mono * Move custom attribute value retrieval to a callback function
1 parent 8e5c4a7 commit e14174b

File tree

11 files changed

+129
-48
lines changed

11 files changed

+129
-48
lines changed

src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSManagedTypeInfo.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ public static JSTypeInfo CreateJSTypeInfoForTypeSymbol(ITypeSymbol type)
164164
}
165165
return new JSFunctionTypeInfo(false, signatureTypes);
166166
default:
167+
// JS Interop generator does not support the marshalling of structs
168+
// In case structs were to be allowed for marshalling in the future,
169+
// disallow marshalling of structs with the InlineArrayAttribute
167170
return new JSInvalidTypeInfo();
168171
}
169172
}

src/mono/mono/metadata/assembly.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1785,7 +1785,7 @@ struct HasReferenceAssemblyAttributeIterData {
17851785
};
17861786

17871787
static gboolean
1788-
has_reference_assembly_attribute_iterator (MonoImage *image, guint32 typeref_scope_token, const char *nspace, const char *name, guint32 method_token, gpointer user_data)
1788+
has_reference_assembly_attribute_iterator (MonoImage *image, guint32 typeref_scope_token, const char *nspace, const char *name, guint32 method_token, guint32 *cols, gpointer user_data)
17891789
{
17901790
gboolean stop_scanning = FALSE;
17911791
struct HasReferenceAssemblyAttributeIterData *iter_data = (struct HasReferenceAssemblyAttributeIterData*)user_data;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ MONO_CLASS_GETTER(m_class_is_unicode, gboolean, , MonoClass, unicode)
2929
MONO_CLASS_GETTER(m_class_was_typebuilder, gboolean, , MonoClass, wastypebuilder)
3030
MONO_CLASS_GETTER(m_class_is_array_special_interface, gboolean, , MonoClass, is_array_special_interface)
3131
MONO_CLASS_GETTER(m_class_is_byreflike, gboolean, , MonoClass, is_byreflike)
32+
MONO_CLASS_GETTER(m_class_is_inlinearray, gboolean, , MonoClass, is_inlinearray)
33+
MONO_CLASS_GETTER(m_class_inlinearray_value, gint32, , MonoClass, inlinearray_value)
3234
MONO_CLASS_GETTER(m_class_get_min_align, guint8, , MonoClass, min_align)
3335
MONO_CLASS_GETTER(m_class_get_packing_size, guint, , MonoClass, packing_size)
3436
MONO_CLASS_GETTER(m_class_is_ghcimpl, gboolean, , MonoClass, ghcimpl)

src/mono/mono/metadata/class-init.c

Lines changed: 106 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,27 @@ static gint32 inflated_classes_size;
4949
gint32 mono_inflated_methods_size;
5050
static gint32 class_def_count, class_gtd_count, class_ginst_count, class_gparam_count, class_array_count, class_pointer_count;
5151

52+
typedef struct {
53+
/* inputs */
54+
const char *nspace;
55+
const char *name;
56+
gboolean in_corlib;
57+
gboolean has_value;
58+
MonoHasValueCallback has_value_callback;
59+
/* output */
60+
gboolean has_attr;
61+
gpointer value;
62+
} FoundAttrUD;
63+
5264
/* Low level lock which protects data structures in this module */
5365
static mono_mutex_t classes_mutex;
5466

5567
static gboolean class_kind_may_contain_generic_instances (MonoTypeKind kind);
5668
static void mono_generic_class_setup_parent (MonoClass *klass, MonoClass *gtd);
5769
static int generic_array_methods (MonoClass *klass);
5870
static void setup_generic_array_ifaces (MonoClass *klass, MonoClass *iface, MonoMethod **methods, int pos, GHashTable *cache);
59-
static gboolean class_has_isbyreflike_attribute (MonoClass *klass);
71+
static FoundAttrUD class_has_isbyreflike_attribute (MonoClass *klass);
72+
static FoundAttrUD class_has_inlinearray_attribute (MonoClass *klass);
6073

6174
static
6275
GENERATE_TRY_GET_CLASS_WITH_CACHE(icollection, "System.Collections.Generic", "ICollection`1");
@@ -291,6 +304,9 @@ mono_class_setup_fields (MonoClass *klass)
291304
instance_size = MONO_ABI_SIZEOF (MonoObject);
292305
}
293306

307+
if (m_class_is_inlinearray (klass) && m_class_inlinearray_value (klass) <= 0)
308+
mono_class_set_type_load_failure (klass, "Inline array length property must be positive.");
309+
294310
/* Get the real size */
295311
explicit_size = mono_metadata_packing_from_typedef (klass->image, klass->type_token, &packing_size, &real_size);
296312
if (explicit_size)
@@ -359,6 +375,10 @@ mono_class_setup_fields (MonoClass *klass)
359375
mono_class_set_type_load_failure (klass, "Generic class cannot have explicit layout.");
360376
break;
361377
}
378+
if (m_class_is_inlinearray (klass)) {
379+
mono_class_set_type_load_failure (klass, "Inline array struct must not have explicit layout.");
380+
break;
381+
}
362382
}
363383
if (mono_type_has_exceptions (field->type)) {
364384
char *class_name = mono_type_get_full_name (klass);
@@ -699,9 +719,17 @@ mono_class_create_from_typedef (MonoImage *image, guint32 type_token, MonoError
699719
mono_get_runtime_callbacks ()->init_class (klass);
700720

701721
// compute is_byreflike
702-
if (m_class_is_valuetype (klass))
703-
if (class_has_isbyreflike_attribute (klass))
722+
if (m_class_is_valuetype (klass)) {
723+
FoundAttrUD attr;
724+
attr = class_has_isbyreflike_attribute (klass);
725+
if (attr.has_attr)
704726
klass->is_byreflike = 1;
727+
attr = class_has_inlinearray_attribute (klass);
728+
if (attr.has_attr) {
729+
klass->is_inlinearray = 1;
730+
klass->inlinearray_value = GPOINTER_TO_INT32 (attr.value);
731+
}
732+
}
705733

706734
mono_loader_unlock ();
707735

@@ -746,65 +774,87 @@ mono_generic_class_setup_parent (MonoClass *klass, MonoClass *gtd)
746774
mono_loader_unlock ();
747775
}
748776

749-
struct FoundAttrUD {
750-
/* inputs */
751-
const char *nspace;
752-
const char *name;
753-
gboolean in_corlib;
754-
/* output */
755-
gboolean has_attr;
756-
};
757-
758777
static gboolean
759-
has_wellknown_attribute_func (MonoImage *image, guint32 typeref_scope_token, const char *nspace, const char *name, guint32 method_token, gpointer user_data)
778+
has_wellknown_attribute_func (MonoImage *image, guint32 typeref_scope_token, const char *nspace, const char *name, guint32 method_token, guint32 *cols, gpointer user_data)
760779
{
761-
struct FoundAttrUD *has_attr = (struct FoundAttrUD *)user_data;
762-
if (!strcmp (name, has_attr->name) && !strcmp (nspace, has_attr->nspace)) {
763-
has_attr->has_attr = TRUE;
764-
return TRUE;
780+
FoundAttrUD *attr = (FoundAttrUD *)user_data;
781+
if (!strcmp (name, attr->name) && !strcmp (nspace, attr->nspace)) {
782+
if (attr->has_value_callback != NULL) {
783+
attr->has_value_callback (image, method_token, cols, user_data);
784+
}
785+
attr->has_attr = TRUE;
765786
}
766787
/* TODO: use typeref_scope_token to check that attribute comes from
767788
* corlib if in_corlib is TRUE, without triggering an assembly load.
768789
* If we're inside corlib, expect the scope to be
769790
* MONO_RESOLUTION_SCOPE_MODULE I think, if we're outside it'll be an
770791
* MONO_RESOLUTION_SCOPE_ASSEMBLYREF and we'll need to check the
771792
* name.*/
772-
return FALSE;
793+
return attr->has_attr;
773794
}
774795

775-
static gboolean
776-
class_has_wellknown_attribute (MonoClass *klass, const char *nspace, const char *name, gboolean in_corlib)
796+
static void
797+
has_inline_array_attribute_value_func (MonoImage *image, uint32_t method_token, uint32_t *cols, gpointer user_data)
777798
{
778-
struct FoundAttrUD has_attr;
779-
has_attr.nspace = nspace;
780-
has_attr.name = name;
781-
has_attr.in_corlib = in_corlib;
782-
has_attr.has_attr = FALSE;
799+
FoundAttrUD *attr = (FoundAttrUD *)user_data;
800+
MonoError error;
801+
MonoMethod *ctor = mono_get_method_checked (image, method_token, NULL, NULL, &error);
802+
if (ctor) {
803+
const char *data = mono_metadata_blob_heap (image, cols [MONO_CUSTOM_ATTR_VALUE]);
804+
uint32_t data_size = mono_metadata_decode_value (data, &data);
805+
MonoDecodeCustomAttr *decoded_attr = mono_reflection_create_custom_attr_data_args_noalloc (image, ctor, (guchar*)data, data_size, &error);
806+
mono_error_assert_ok (&error);
807+
g_assert (decoded_attr->named_args_num == 0 && decoded_attr->typed_args_num == 1);
808+
attr->value = *(gpointer*)decoded_attr->typed_args [0]->value.primitive;
809+
g_free (decoded_attr);
810+
} else {
811+
g_warning ("Can't find custom attr constructor image: %s mtoken: 0x%08x due to: %s", image->name, method_token, mono_error_get_message (&error));
812+
}
813+
}
783814

784-
mono_class_metadata_foreach_custom_attr (klass, has_wellknown_attribute_func, &has_attr);
815+
static FoundAttrUD
816+
class_has_wellknown_attribute (MonoClass *klass, const char *nspace, const char *name, gboolean in_corlib, gboolean has_value, MonoHasValueCallback callback)
817+
{
818+
FoundAttrUD attr;
819+
attr.nspace = nspace;
820+
attr.name = name;
821+
attr.in_corlib = in_corlib;
822+
attr.has_attr = FALSE;
823+
attr.has_value = has_value;
824+
attr.has_value_callback = callback;
785825

786-
return has_attr.has_attr;
826+
mono_class_metadata_foreach_custom_attr (klass, has_wellknown_attribute_func, &attr);
827+
828+
return attr;
787829
}
788830

789-
static gboolean
790-
method_has_wellknown_attribute (MonoMethod *method, const char *nspace, const char *name, gboolean in_corlib)
831+
static FoundAttrUD
832+
method_has_wellknown_attribute (MonoMethod *method, const char *nspace, const char *name, gboolean in_corlib, gboolean has_value, MonoHasValueCallback callback)
791833
{
792-
struct FoundAttrUD has_attr;
793-
has_attr.nspace = nspace;
794-
has_attr.name = name;
795-
has_attr.in_corlib = in_corlib;
796-
has_attr.has_attr = FALSE;
834+
FoundAttrUD attr;
835+
attr.nspace = nspace;
836+
attr.name = name;
837+
attr.in_corlib = in_corlib;
838+
attr.has_attr = FALSE;
839+
attr.has_value = has_value;
840+
attr.has_value_callback = callback;
797841

798-
mono_method_metadata_foreach_custom_attr (method, has_wellknown_attribute_func, &has_attr);
842+
mono_method_metadata_foreach_custom_attr (method, has_wellknown_attribute_func, &attr);
799843

800-
return has_attr.has_attr;
844+
return attr;
801845
}
802846

803847

804-
static gboolean
848+
static FoundAttrUD
805849
class_has_isbyreflike_attribute (MonoClass *klass)
806850
{
807-
return class_has_wellknown_attribute (klass, "System.Runtime.CompilerServices", "IsByRefLikeAttribute", TRUE);
851+
return class_has_wellknown_attribute (klass, "System.Runtime.CompilerServices", "IsByRefLikeAttribute", TRUE, FALSE, NULL);
852+
}
853+
854+
static FoundAttrUD
855+
class_has_inlinearray_attribute (MonoClass *klass)
856+
{
857+
return class_has_wellknown_attribute (klass, "System.Runtime.CompilerServices", "InlineArrayAttribute", TRUE, TRUE, has_inline_array_attribute_value_func);
808858
}
809859

810860

@@ -815,7 +865,8 @@ mono_class_setup_method_has_preserve_base_overrides_attribute (MonoMethod *metho
815865
/* FIXME: implement well known attribute check for dynamic images */
816866
if (image_is_dynamic (image))
817867
return FALSE;
818-
return method_has_wellknown_attribute (method, "System.Runtime.CompilerServices", "PreserveBaseOverridesAttribute", TRUE);
868+
FoundAttrUD attr = method_has_wellknown_attribute (method, "System.Runtime.CompilerServices", "PreserveBaseOverridesAttribute", TRUE, FALSE, NULL);
869+
return attr.has_attr;
819870
}
820871

821872
static gboolean
@@ -868,6 +919,8 @@ mono_class_create_generic_inst (MonoGenericClass *gclass)
868919
klass->this_arg.type = m_class_get_byval_arg (klass)->type;
869920
klass->this_arg.data.generic_class = klass->_byval_arg.data.generic_class = gclass;
870921
klass->this_arg.byref__ = TRUE;
922+
klass->is_inlinearray = gklass->is_inlinearray;
923+
klass->inlinearray_value = gklass->inlinearray_value;
871924
klass->enumtype = gklass->enumtype;
872925
klass->valuetype = gklass->valuetype;
873926

@@ -2184,6 +2237,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
21842237
real_size = MONO_ABI_SIZEOF (MonoObject);
21852238
}
21862239

2240+
guint32 inlined_fields = 0;
21872241
for (pass = 0; pass < passes; ++pass) {
21882242
for (i = 0; i < top; i++){
21892243
gint32 align;
@@ -2217,6 +2271,17 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
22172271
}
22182272

22192273
size = mono_type_size (field->type, &align);
2274+
if (m_class_is_inlinearray (klass)) {
2275+
// Limit the max size of array instance to 1MiB
2276+
const guint32 struct_max_size = 1024 * 1024;
2277+
// If size overflows, it returns 0
2278+
size *= m_class_inlinearray_value (klass);
2279+
inlined_fields++;
2280+
if(size == 0 || size > struct_max_size) {
2281+
mono_class_set_type_load_failure (klass, "Inline array struct size out of bounds, abnormally large.");
2282+
break;
2283+
}
2284+
}
22202285

22212286
/* FIXME (LAMESPEC): should we also change the min alignment according to pack? */
22222287
align = packing_size ? MIN (packing_size, align): align;
@@ -2244,6 +2309,8 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
22442309
instance_size &= ~(min_align - 1);
22452310
}
22462311
}
2312+
if (m_class_is_inlinearray (klass) && inlined_fields != 1)
2313+
mono_class_set_type_load_failure (klass, "Inline array struct must have a single field.");
22472314
break;
22482315
case TYPE_ATTRIBUTE_EXPLICIT_LAYOUT: {
22492316
real_size = 0;

src/mono/mono/metadata/class-private-definition.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ struct _MonoClass {
4949
guint wastypebuilder : 1; /* class was created at runtime from a TypeBuilder */
5050
guint is_array_special_interface : 1; /* gtd or ginst of once of the magic interfaces that arrays implement */
5151
guint is_byreflike : 1; /* class is a valuetype and has System.Runtime.CompilerServices.IsByRefLikeAttribute */
52+
guint is_inlinearray : 1; /* class is a valuetype and has System.Runtime.CompilerServices.InlineArrayAttribute */
5253

5354
/* next byte */
5455
guint8 min_align;
@@ -102,6 +103,8 @@ struct _MonoClass {
102103
guint16 *interface_offsets_packed;
103104
guint8 *interface_bitmap;
104105

106+
gint32 inlinearray_value; /* System.Runtime.CompilerServices.InlineArrayAttribute length value */
107+
105108
MonoClass **interfaces;
106109

107110
union _MonoClassSizes sizes;

src/mono/mono/metadata/custom-attrs-internals.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ typedef struct _MonoDecodeCustomAttr {
3535
MonoCustomAttrInfo*
3636
mono_custom_attrs_from_builders (MonoImage *alloc_img, MonoImage *image, MonoArray *cattrs);
3737

38-
typedef gboolean (*MonoAssemblyMetadataCustomAttrIterFunc) (MonoImage *image, guint32 typeref_scope_token, const gchar* nspace, const gchar* name, guint32 method_token, gpointer user_data);
38+
typedef gboolean (*MonoAssemblyMetadataCustomAttrIterFunc) (MonoImage *image, guint32 typeref_scope_token, const gchar* nspace, const gchar* name, guint32 method_token, guint32 *cols, gpointer user_data);
39+
40+
typedef void (*MonoHasValueCallback) (MonoImage *image, uint32_t method_token, uint32_t *cols, gpointer user_data);
3941

4042
void
4143
mono_assembly_metadata_foreach_custom_attr (MonoAssembly *assembly, MonoAssemblyMetadataCustomAttrIterFunc func, gpointer user_data);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2813,7 +2813,7 @@ metadata_foreach_custom_attr_from_index (MonoImage *image, guint32 idx, MonoAsse
28132813
if (!custom_attr_class_name_from_method_token (image, mtoken, &assembly_token, &nspace, &name))
28142814
continue;
28152815

2816-
stop_iterating = func (image, assembly_token, nspace, name, mtoken, user_data);
2816+
stop_iterating = func (image, assembly_token, nspace, name, mtoken, cols, user_data);
28172817
}
28182818
}
28192819

src/mono/mono/metadata/object.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,7 @@ compute_class_bitmap (MonoClass *klass, gsize *bitmap, int size, int offset, int
878878
pos += offset;
879879

880880
type = mono_type_get_underlying_type (field->type);
881+
881882
switch (type->type) {
882883
case MONO_TYPE_U:
883884
case MONO_TYPE_I:
@@ -931,6 +932,15 @@ compute_class_bitmap (MonoClass *klass, gsize *bitmap, int size, int offset, int
931932
g_error ("compute_class_bitmap: Invalid type %x for field %s:%s\n", type->type, mono_type_get_full_name (m_field_get_parent (field)), field->name);
932933
break;
933934
}
935+
936+
// A struct with the inline array attribute is handled in the same way as an array
937+
if (m_class_is_inlinearray (klass)) {
938+
g_assert ((m_field_get_offset (field) % wordsize) == 0);
939+
940+
g_assert (pos < GINT_TO_UINT32(size) || pos <= GINT_TO_UINT32(max_size));
941+
bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE);
942+
*max_set = MAX (GINT_TO_UINT32(*max_set), pos);
943+
}
934944
}
935945
if (static_fields)
936946
break;

src/tests/Loader/classloader/InlineArray/InlineArrayInvalid.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
<PropertyGroup>
33
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
44
<OutputType>Exe</OutputType>
5-
<MonoAotIncompatible>true</MonoAotIncompatible>
65
</PropertyGroup>
76
<ItemGroup>
87
<Compile Include="InlineArrayInvalid.cs" />

src/tests/Loader/classloader/InlineArray/InlineArrayValid.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
<PropertyGroup>
33
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
44
<OutputType>Exe</OutputType>
5-
<MonoAotIncompatible>true</MonoAotIncompatible>
65
</PropertyGroup>
76
<ItemGroup>
87
<Compile Include="InlineArrayValid.cs" />

src/tests/issues.targets

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,10 +1526,6 @@
15261526
<Issue>Static virtual methods are not yet implemented in the Mono runtime.</Issue>
15271527
</ExcludeList>
15281528

1529-
<ExcludeList Include="$(XunitTestBinBase)/Loader/classloader/InlineArray/**">
1530-
<Issue>https://github.com/dotnet/runtime/issues/80798</Issue>
1531-
</ExcludeList>
1532-
15331529
<ExcludeList Include="$(XunitTestBinBase)/baseservices/exceptions/stackoverflow/stackoverflowtester/**">
15341530
<Issue>needs triage</Issue>
15351531
</ExcludeList>

0 commit comments

Comments
 (0)