未验证 提交 053a472b 编写于 作者: A Aaron Robinson 提交者: GitHub

Update tests to help uncover CrossGen2 scenario validation (#65477)

* Update CrossGen2 to support ByRef fields in all scenarios

* Update tests to help uncover CrossGen2 scenario validation

* Add support in Mono for the ByReference`1 being considered a ref field
  during Explicit offset validation.
上级 ea5376ad
......@@ -14,6 +14,7 @@ private enum FieldLayoutTag : byte
Empty,
NonORef,
ORef,
ByRef,
}
private struct FieldLayoutInterval : IComparable<FieldLayoutInterval>
......@@ -92,15 +93,9 @@ private void AddToFieldLayout(int offset, TypeDesc fieldType)
}
else if (fieldType.IsValueType)
{
if (fieldType.IsByRefLike && offset % _pointerSize != 0)
{
// Misaligned ByRefLike
ThrowFieldLayoutError(offset);
}
MetadataType mdType = (MetadataType)fieldType;
int fieldSize = mdType.InstanceByteCountUnaligned.AsInt;
if (!mdType.ContainsGCPointers)
if (!mdType.ContainsGCPointers && !mdType.IsByRefLike)
{
// Plain value type, mark the entire range as NonORef
SetFieldLayout(offset, fieldSize, FieldLayoutTag.NonORef);
......@@ -109,27 +104,27 @@ private void AddToFieldLayout(int offset, TypeDesc fieldType)
{
if (offset % _pointerSize != 0)
{
// Misaligned struct with GC pointers
// Misaligned struct with GC pointers or ByRef
ThrowFieldLayoutError(offset);
}
List<FieldLayoutInterval> fieldORefMap = new List<FieldLayoutInterval>();
MarkORefLocations(mdType, fieldORefMap, offset: 0);
List<FieldLayoutInterval> fieldRefMap = new();
MarkByRefAndORefLocations(mdType, fieldRefMap, offset: 0);
// Merge in fieldORefMap from structure specifying not attributed intervals as NonORef
// Merge in fieldRefMap from structure specifying not attributed intervals as NonORef
int lastGCRegionReportedEnd = 0;
foreach (var gcRegion in fieldORefMap)
foreach (var gcRegion in fieldRefMap)
{
SetFieldLayout(offset + lastGCRegionReportedEnd, gcRegion.Start - lastGCRegionReportedEnd, FieldLayoutTag.NonORef);
Debug.Assert(gcRegion.Tag == FieldLayoutTag.ORef);
Debug.Assert(gcRegion.Tag == FieldLayoutTag.ORef || gcRegion.Tag == FieldLayoutTag.ByRef);
SetFieldLayout(offset + gcRegion.Start, gcRegion.Size, gcRegion.Tag);
lastGCRegionReportedEnd = gcRegion.EndSentinel;
}
if (fieldORefMap.Count > 0)
if (fieldRefMap.Count > 0)
{
int trailingRegionStart = fieldORefMap[fieldORefMap.Count - 1].EndSentinel;
int trailingRegionStart = fieldRefMap[fieldRefMap.Count - 1].EndSentinel;
int trailingRegionSize = fieldSize - trailingRegionStart;
SetFieldLayout(offset + trailingRegionStart, trailingRegionSize, FieldLayoutTag.NonORef);
}
......@@ -142,7 +137,7 @@ private void AddToFieldLayout(int offset, TypeDesc fieldType)
// Misaligned pointer field
ThrowFieldLayoutError(offset);
}
SetFieldLayout(offset, _pointerSize, FieldLayoutTag.NonORef);
SetFieldLayout(offset, _pointerSize, FieldLayoutTag.ByRef);
}
else
{
......@@ -150,7 +145,7 @@ private void AddToFieldLayout(int offset, TypeDesc fieldType)
}
}
private void MarkORefLocations(MetadataType type, List<FieldLayoutInterval> orefMap, int offset)
private void MarkByRefAndORefLocations(MetadataType type, List<FieldLayoutInterval> refMap, int offset)
{
// Recurse into struct fields
foreach (FieldDesc field in type.GetFields())
......@@ -160,14 +155,18 @@ private void MarkORefLocations(MetadataType type, List<FieldLayoutInterval> oref
int fieldOffset = offset + field.Offset.AsInt;
if (field.FieldType.IsGCPointer)
{
SetFieldLayout(orefMap, offset, _pointerSize, FieldLayoutTag.ORef);
SetFieldLayout(refMap, offset, _pointerSize, FieldLayoutTag.ORef);
}
else if (field.FieldType.IsByRef || field.FieldType.IsByReferenceOfT)
{
SetFieldLayout(refMap, offset, _pointerSize, FieldLayoutTag.ByRef);
}
else if (field.FieldType.IsValueType)
{
MetadataType mdFieldType = (MetadataType)field.FieldType;
if (mdFieldType.ContainsGCPointers)
if (mdFieldType.ContainsGCPointers || mdFieldType.IsByRefLike)
{
MarkORefLocations(mdFieldType, orefMap, fieldOffset);
MarkByRefAndORefLocations(mdFieldType, refMap, fieldOffset);
}
}
}
......
......@@ -1843,12 +1843,35 @@ class_has_ref_fields (MonoClass *klass)
return klass->has_ref_fields;
}
static gboolean
class_is_byreference (MonoClass* klass)
{
const char* klass_name_space = m_class_get_name_space (klass);
const char* klass_name = m_class_get_name (klass);
MonoImage* klass_image = m_class_get_image (klass);
gboolean in_corlib = klass_image == mono_defaults.corlib;
if (in_corlib &&
!strcmp (klass_name_space, "System") &&
!strcmp (klass_name, "ByReference`1")) {
return TRUE;
}
return FALSE;
}
static gboolean
type_has_ref_fields (MonoType *ftype)
{
if (m_type_is_byref (ftype) || (MONO_TYPE_ISSTRUCT (ftype) && class_has_ref_fields (mono_class_from_mono_type_internal (ftype))))
return TRUE;
/* Check for the ByReference`1 type */
if (MONO_TYPE_ISSTRUCT (ftype)) {
MonoClass* klass = mono_class_from_mono_type_internal (ftype);
return class_is_byreference (klass);
}
return FALSE;
}
......@@ -1942,7 +1965,7 @@ validate_struct_fields_overlaps (guint8 *layout_check, int layout_size, MonoClas
if (mono_type_is_struct (ftype)) {
// recursively check the layout of the embedded struct
MonoClass *embedded_class = mono_class_from_mono_type_internal (ftype);
mono_class_setup_fields (embedded_class);
mono_class_setup_fields (embedded_class);
const int embedded_fields_count = mono_class_get_field_count (embedded_class);
int *embedded_offsets = g_new0 (int, embedded_fields_count);
......@@ -1962,7 +1985,7 @@ validate_struct_fields_overlaps (guint8 *layout_check, int layout_size, MonoClas
} else {
int align = 0;
int size = mono_type_size (field->type, &align);
guint8 type = type_has_references (klass, ftype) ? 1 : m_type_is_byref (ftype) ? 2 : 3;
guint8 type = type_has_references (klass, ftype) ? 1 : (m_type_is_byref (ftype) || class_is_byreference (klass)) ? 2 : 3;
// Mark the bytes used by this fields type based on if it contains references or not.
// Make sure there are no overlaps between object and non-object fields.
......
......@@ -61,6 +61,7 @@
01 00 00 00
)
.field public int32& Field
.field public int32 Size
}
.class public explicit ansi sealed beforefieldinit InvalidCSharp.IntPtrOverlapWithInnerFieldType
......
......@@ -4,12 +4,20 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using InvalidCSharp;
using Xunit;
class Validate
{
[StructLayout(LayoutKind.Explicit)]
private ref struct Explicit
{
[FieldOffset(0)] public Span<byte> Bytes;
[FieldOffset(0)] public Guid Guid;
}
[Fact]
public static void Validate_Invalid_RefField_Fails()
{
......@@ -17,8 +25,31 @@ public static void Validate_Invalid_RefField_Fails()
Assert.Throws<TypeLoadException>(() => { var t = typeof(InvalidStructWithRefField); });
Assert.Throws<TypeLoadException>(() => { var t = typeof(InvalidRefFieldAlignment); });
Assert.Throws<TypeLoadException>(() => { var t = typeof(InvalidObjectRefRefFieldOverlap); });
Assert.Throws<TypeLoadException>(() => { var t = typeof(IntPtrRefFieldOverlap); });
Assert.Throws<TypeLoadException>(() => { var t = typeof(IntPtrOverlapWithInnerFieldType); });
Assert.Throws<TypeLoadException>(() =>
{
var t = new IntPtrRefFieldOverlap()
{
Field = IntPtr.Zero
};
return t.Field.ToString();
});
Assert.Throws<TypeLoadException>(() =>
{
var t = new IntPtrOverlapWithInnerFieldType()
{
Field = IntPtr.Zero
};
ref var i = ref t.Invalid;
return i.Size;
});
Assert.Throws<TypeLoadException>(() =>
{
var t = new Explicit()
{
Guid = Guid.NewGuid()
};
return t.Bytes.Length;
});
}
[Fact]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册