Skip to content

Commit

Permalink
Classify type with empty base as non-blittable (#46769)
Browse files Browse the repository at this point in the history
* Add tests for MarshalUtils.IsBillitableType

* Classify type with empty base as non-blittable

* Add tests for IsZeroSizedReferenceType

* Rename local variables in tests
  • Loading branch information
am11 authored Jan 20, 2021
1 parent 18645f9 commit 95d9425
Show file tree
Hide file tree
Showing 6 changed files with 347 additions and 2 deletions.
21 changes: 21 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/Common/DefType.FieldLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,27 @@ public LayoutInt InstanceByteAlignment
}
}

public bool IsZeroSizedReferenceType
{
get
{
if (Category != TypeFlags.Class)
{
throw new InvalidOperationException("Only reference types are allowed.");
}

if (!_fieldLayoutFlags.HasFlags(FieldLayoutFlags.ComputedInstanceTypeLayout))
{
ComputeInstanceLayout(InstanceLayoutKind.TypeOnly);
}

// test that size without padding is zero:
// _instanceByteCountUnaligned - _instanceByteAlignment == LayoutInt.Zero
// simplified to:
return _instanceByteCountUnaligned == _instanceByteAlignment;
}
}

/// <summary>
/// The type has stable Abi layout
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ public static bool IsBlittableType(TypeDesc type)
return false;
}

TypeDesc baseType = type.BaseType;
DefType baseType = type.BaseType;
bool hasNonTrivialParent = baseType != null
&& !baseType.IsWellKnownType(WellKnownType.Object)
&& !baseType.IsWellKnownType(WellKnownType.ValueType);

if (hasNonTrivialParent && !IsBlittableType(baseType))
// Type is blittable only if parent is also blittable and is not empty.
if (hasNonTrivialParent && (!IsBlittableType(baseType) || baseType.IsZeroSizedReferenceType))
{
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;

namespace Marshalling
{

#region Simple classes

public class SimpleEmptyClass
{
}

public class SimpleByteClass
{
public byte x;
}

public class SimpleInt16Class
{
public short x;
}

public class SimpleInt32Class
{
public int x;
}

public class SimpleInt64Class
{
public long x;
}

#endregion

#region LayoutKind.Explicit classes

[StructLayout(LayoutKind.Explicit)]
public class ExplicitEmptyBase
{
}

[StructLayout(LayoutKind.Explicit)]
public class ClassWithExplicitEmptyBase : ExplicitEmptyBase
{
[FieldOffset(0)]
public int i;
}

[StructLayout(LayoutKind.Explicit, Size = 0)]
public class ExplicitEmptySizeZeroBase
{
}

[StructLayout(LayoutKind.Explicit)]
public class ClassWithExplicitEmptySizeZeroBase : ExplicitEmptySizeZeroBase
{
[FieldOffset(0)]
public int i;
}

[StructLayout(LayoutKind.Explicit)]
public class ExplicitByteBase
{
[FieldOffset(0)]
public byte x;
}

[StructLayout(LayoutKind.Explicit)]
public class ClassWithExplicitByteBase : ExplicitByteBase
{
[FieldOffset(1)]
public int i;
}

[StructLayout(LayoutKind.Explicit)]
public class ExplicitInt16Base
{
[FieldOffset(0)]
public short x;
}

[StructLayout(LayoutKind.Explicit)]
public class ClassWithExplicitInt16Base : ExplicitInt16Base
{
[FieldOffset(1)]
public int i;
}

[StructLayout(LayoutKind.Explicit)]
public class ExplicitInt32Base
{
[FieldOffset(0)]
public int x;
}

[StructLayout(LayoutKind.Explicit)]
public class ClassWithExplicitInt32Base : ExplicitInt32Base
{
[FieldOffset(1)]
public int i;
}

[StructLayout(LayoutKind.Explicit)]
public class ExplicitInt64Base
{
[FieldOffset(0)]
public long x;
}

[StructLayout(LayoutKind.Explicit)]
public class ClassWithExplicitInt64Base : ExplicitInt64Base
{
[FieldOffset(1)]
public int i;
}

[StructLayout(LayoutKind.Sequential)]
public class SequentialEmptyBase
{
}

#endregion

#region LayoutKind.Sequential classes

[StructLayout(LayoutKind.Sequential)]
public class ClassWithSequentialEmptyBase : SequentialEmptyBase
{
public int i;
}

[StructLayout(LayoutKind.Sequential)]
public class SequentialByteBase
{
public byte x;
}

[StructLayout(LayoutKind.Sequential)]
public class ClassWithSequentialByteBase : SequentialByteBase
{
public int i;
}

[StructLayout(LayoutKind.Sequential)]
public class SequentialInt16Base
{
public short x;
}

[StructLayout(LayoutKind.Sequential)]
public class ClassWithSequentialInt16Base : SequentialInt16Base
{
public int i;
}

[StructLayout(LayoutKind.Sequential)]
public class SequentialInt32Base
{
public int x;
}

[StructLayout(LayoutKind.Sequential)]
public class ClassWithSequentialInt32Base : SequentialInt32Base
{
public int i;
}

[StructLayout(LayoutKind.Sequential)]
public class SequentialInt64Base
{
public long x;
}

[StructLayout(LayoutKind.Sequential)]
public class ClassWithSequentialInt64Base : SequentialInt64Base
{
public int i;
}

#endregion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Internal.TypeSystem;
using Internal.TypeSystem.Interop;

using Xunit;

namespace TypeSystemTests
{
public partial class DefTypeTests
{
private ModuleDesc _testModule;

public DefTypeTests()
{
var context = new TestTypeSystemContext(TargetArchitecture.X64);
var systemModule = context.CreateModuleForSimpleName("CoreTestAssembly");
context.SetSystemModule(systemModule);

_testModule = systemModule;
}

[Theory]
[InlineData("SimpleByteClass")]
[InlineData("SimpleInt16Class")]
[InlineData("SimpleInt32Class")]
[InlineData("SimpleInt64Class")]
[InlineData("ExplicitByteBase")]
[InlineData("ExplicitInt16Base")]
[InlineData("ExplicitInt32Base")]
[InlineData("ExplicitInt64Base")]
[InlineData("SequentialByteBase")]
[InlineData("SequentialInt16Base")]
[InlineData("SequentialInt32Base")]
[InlineData("SequentialInt64Base")]
public void IsZeroSizedReferenceType_NonEmptyType_ReturnsFalse(string className)
{
DefType nonEmptyClass = _testModule.GetType("Marshalling", className);
Assert.False(nonEmptyClass.IsZeroSizedReferenceType);
}

[Theory]
[InlineData("SimpleEmptyClass")]
[InlineData("ExplicitEmptyBase")]
[InlineData("ExplicitEmptySizeZeroBase")]
[InlineData("SequentialEmptyBase")]
public void IsZeroSizedReferenceType_EmptyType_ReturnsTrue(string className)
{
DefType emptyClass = _testModule.GetType("Marshalling", className);
Assert.True(emptyClass.IsZeroSizedReferenceType);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ILCompiler.ReadyToRun\ILCompiler.ReadyToRun.csproj" />
<ProjectReference Include="..\ILCompiler.TypeSystem.ReadyToRun\ILCompiler.TypeSystem.ReadyToRun.csproj" />
<!-- Make sure the test data gets built -->
<ProjectReference Include="CoreTestAssembly\CoreTestAssembly.csproj">
Expand All @@ -41,6 +42,7 @@
<Compile Include="GCPointerMapTests.cs" />
<Compile Include="GenericTypeAndMethodTests.cs" />
<Compile Include="CastingTests.cs" />
<Compile Include="DefType.FieldLayoutTests.cs" />
<Compile Include="HashcodeTests.cs" />
<Compile Include="ILDisassemblerTests.cs" />
<Compile Include="InterfacesTests.cs" />
Expand All @@ -58,5 +60,6 @@
<Compile Include="TestTypeSystemContext.cs" />
<Compile Include="WellKnownTypeTests.cs" />
<Compile Include="ExceptionStringTests.cs" />
<Compile Include="MarshalUtilsTests.cs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Internal.TypeSystem;
using Internal.TypeSystem.Interop;

using Xunit;

namespace TypeSystemTests
{
public class MarshalUtilsTests
{
private TestTypeSystemContext _context;
private ModuleDesc _testModule;

public MarshalUtilsTests()
{
_context = new TestTypeSystemContext(TargetArchitecture.X64);
var systemModule = _context.CreateModuleForSimpleName("CoreTestAssembly");
_context.SetSystemModule(systemModule);

_testModule = systemModule;
}

[Theory]
[InlineData(WellKnownType.Void)]
[InlineData(WellKnownType.Boolean)]
[InlineData(WellKnownType.Char)]
[InlineData(WellKnownType.SByte)]
[InlineData(WellKnownType.Byte)]
[InlineData(WellKnownType.Int16)]
[InlineData(WellKnownType.UInt16)]
[InlineData(WellKnownType.Int32)]
[InlineData(WellKnownType.UInt32)]
[InlineData(WellKnownType.Int64)]
[InlineData(WellKnownType.UInt64)]
[InlineData(WellKnownType.IntPtr)]
[InlineData(WellKnownType.UIntPtr)]
[InlineData(WellKnownType.Single)]
[InlineData(WellKnownType.Double)]
[InlineData(WellKnownType.RuntimeFieldHandle)]
[InlineData(WellKnownType.RuntimeTypeHandle)]
[InlineData(WellKnownType.RuntimeMethodHandle)]
public void IsBlittableType_BilittableWellKnownTypes_ReturnsTrue(WellKnownType type) =>
Assert.True(MarshalUtils.IsBlittableType(_context.GetWellKnownType(type)));

[Theory]
[InlineData(WellKnownType.String)]
[InlineData(WellKnownType.ValueType)]
[InlineData(WellKnownType.Enum)]
[InlineData(WellKnownType.Array)]
[InlineData(WellKnownType.MulticastDelegate)]
[InlineData(WellKnownType.Exception)]
[InlineData(WellKnownType.Object)]
public void IsBlittableType_NonBilittableWellKnownTypes_ReturnsFalse(WellKnownType type) =>
Assert.False(MarshalUtils.IsBlittableType(_context.GetWellKnownType(type)));

[Theory]
[InlineData("ClassWithExplicitByteBase")]
[InlineData("ClassWithExplicitInt16Base")]
[InlineData("ClassWithExplicitInt32Base")]
[InlineData("ClassWithExplicitInt64Base")]
[InlineData("ClassWithSequentialByteBase")]
[InlineData("ClassWithSequentialInt16Base")]
[InlineData("ClassWithSequentialInt32Base")]
[InlineData("ClassWithSequentialInt64Base")]
public void IsBlittableType_TypeWithBlittableBase_ReturnsTrue(string className)
{
TypeDesc classWithBlittableBase = _testModule.GetType("Marshalling", className);
Assert.True(MarshalUtils.IsBlittableType(classWithBlittableBase));
}

[Theory]
[InlineData("ClassWithExplicitEmptyBase")]
[InlineData("ClassWithExplicitEmptySizeZeroBase")]
[InlineData("ClassWithSequentialEmptyBase")]
public void IsBlittableType_TypeWithEmptyBase_ReturnsFalse(string className)
{
TypeDesc classWithEmptyBase = _testModule.GetType("Marshalling", className);
Assert.False(MarshalUtils.IsBlittableType(classWithEmptyBase));
}
}
}

0 comments on commit 95d9425

Please sign in to comment.