Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<ItemGroup>
<Compile Include="Platform.cs" />
<Compile Include="InstanceFieldLayout.cs" />
<Compile Include="StaticFieldLayout.cs" />
</ItemGroup>

<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
Expand Down
66 changes: 66 additions & 0 deletions src/TypeSystem/tests/CoreTestAssembly/StaticFieldLayout.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;

#pragma warning disable 169

namespace StaticFieldLayout
{
struct NoPointers
{
static int int1;
static byte byte1;
static char char1;
}

struct StillNoPointers
{
NoPointers noPointers1;
static bool bool1;
}

class ClassNoPointers
{
static int int1;
static byte byte1;
static char char1;
}

struct HasPointers
{
bool bool1;
static string string1;
static ClassNoPointers class1;
char char1;
}

class MixPointersAndNonPointers
{
static string string1;
static int int1;
static ClassNoPointers class1;
static int int2;
static string string2;
}

class EnsureInheritanceResetsStaticOffsets : MixPointersAndNonPointers
{
static int int3;
static string string3;
}

class RvaTestClass
{
static void RvaTest()
{
int[] foo = new int[] { 0, 1, 2, 3, 4, 45, 5, 5 };
}

}

struct StaticSelfRef
{
static StaticSelfRef selfRef1;
}
}
230 changes: 230 additions & 0 deletions src/TypeSystem/tests/StaticFieldLayoutTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;

using Internal.TypeSystem.Ecma;
using Internal.TypeSystem;

using Xunit;

namespace TypeSystemTests
{
public class StaticFieldLayoutTests
{
TestTypeSystemContext _context;
EcmaModule _testModule;

public StaticFieldLayoutTests()
{
_context = new TestTypeSystemContext(TargetArchitecture.X64);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we detect the architecture from the environment as opposed to hard-coding it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would mean we can only test say - field layout for ARM64 - only when the test runs on an ARM64 device.

We should test other platforms, but we can run all the tests on all platforms irrespective of the current platform.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If X64 is translated as "any 64bit" target, then the above works. However, what happens for 32bit (x86 vs Arm32)? If the target architecture is passed to the test infra at command-line (or such mechanism) that allows the architecture to be not hard-coded, your goal of running arm64 validations on x64 should still work.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x64 is exactly AMD64 (or x86-64). Other 64 bit architectures might have e.g. different requirements for packing or alignments. I don't know and the type system doesn't know either (because all I implemented is x64).

var systemModule = _context.CreateModuleForSimpleName("CoreTestAssembly");
_context.SetSystemModule(systemModule);

_testModule = systemModule;
}

[Fact]
public void TestNoPointers()
{
MetadataType t = _testModule.GetType("StaticFieldLayout", "NoPointers");

foreach (var field in t.GetFields())
{
if (!field.IsStatic)
continue;

switch (field.Name)
{
case "int1":
Assert.Equal(0, field.Offset);
break;
case "byte1":
Assert.Equal(4, field.Offset);
break;
case "char1":
Assert.Equal(6, field.Offset);
break;
default:
throw new Exception(field.Name);
}
}
}

[Fact]
public void TestStillNoPointers()
{
//
// Test that static offsets ignore instance fields preceeding them
//

MetadataType t = _testModule.GetType("StaticFieldLayout", "StillNoPointers");

foreach (var field in t.GetFields())
{
if (!field.IsStatic)
continue;

switch (field.Name)
{
case "bool1":
Assert.Equal(0, field.Offset);
break;
default:
throw new Exception(field.Name);
}
}
}

[Fact]
public void TestClassNoPointers()
{
//
// Ensure classes behave the same as structs when containing statics
//

MetadataType t = _testModule.GetType("StaticFieldLayout", "ClassNoPointers");

foreach (var field in t.GetFields())
{
if (!field.IsStatic)
continue;

switch (field.Name)
{
case "int1":
Assert.Equal(0, field.Offset);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To ensure that the layout is same as structs containing statics, should the assertions be made against the field offsets of NoPointers struct as opposed to hard-coded expected values?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I can do that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I filed issue #34 for the general problem that the tests can only test x64. We don't need this for extensibility because #34 will address that. So, Won't fix for now.

break;
case "byte1":
Assert.Equal(4, field.Offset);
break;
case "char1":
Assert.Equal(6, field.Offset);
break;
default:
throw new Exception(field.Name);
}
}
}

[Fact]
public void TestHasPointers()
{
//
// Test a struct containing static types with pointers
//

MetadataType t = _testModule.GetType("StaticFieldLayout", "HasPointers");

foreach (var field in t.GetFields())
{
if (!field.IsStatic)
continue;

switch (field.Name)
{
case "string1":
Assert.Equal(0, field.Offset);
break;
case "class1":
Assert.Equal(8, field.Offset);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest that we do something like this:

int string1Offset = 0;

For "class1", we should do "string1Offset+IntPtr.Size" instead of hardcoding the offset.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment about matching target platform.

Also note that the type system currently doesn't have any support for architectures other than x64. Adding a new architecture shouldn't be hard, but it's not in scope. I would have to manually compute what the expected offsets on non-x64 platforms are because the existing NUTC tests don't test other arches either and I don't have places to copy from. I'm not saying I can't do it, I'm saying there might be more important things right now.

break;
default:
throw new Exception(field.Name);
}
}
}

[Fact]
public void TestMixPointersAndNonPointers()
{
//
// Test that static fields with GC pointers get separate offsets from non-GC fields
//

MetadataType t = _testModule.GetType("StaticFieldLayout", "MixPointersAndNonPointers");

foreach (var field in t.GetFields())
{
if (!field.IsStatic)
continue;

switch (field.Name)
{
case "string1":
Assert.Equal(0, field.Offset);
break;
case "int1":
Assert.Equal(0, field.Offset);
break;
case "class1":
Assert.Equal(8, field.Offset);
break;
case "int2":
Assert.Equal(4, field.Offset);
break;
case "string2":
Assert.Equal(16, field.Offset);
break;
default:
throw new Exception(field.Name);
}
}
}

[Fact]
public void TestEnsureInheritanceResetsStaticOffsets()
{
//
// Test that when inheriting a class with static fields, the derived slice's static fields
// are again offset from 0
//

MetadataType t = _testModule.GetType("StaticFieldLayout", "EnsureInheritanceResetsStaticOffsets");

foreach (var field in t.GetFields())
{
if (!field.IsStatic)
continue;

switch (field.Name)
{
case "int3":
Assert.Equal(0, field.Offset);
break;
case "string3":
Assert.Equal(0, field.Offset);
break;
default:
throw new Exception(field.Name);
}
}
}

[Fact]
public void TestStaticSelfRef()
{
//
// Test that we can load a struct which has a static field referencing itself without
// going into an infinite loop
//

MetadataType t = _testModule.GetType("StaticFieldLayout", "StaticSelfRef");

foreach (var field in t.GetFields())
{
if (!field.IsStatic)
continue;

switch (field.Name)
{
case "selfRef1":
Assert.Equal(0, field.Offset);
break;
default:
throw new Exception(field.Name);
}
}
}
}
}
1 change: 1 addition & 0 deletions src/TypeSystem/tests/TypeSystem.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="InstanceFieldLayoutTests.cs" />
<Compile Include="StaticFieldLayoutTests.cs" />
<Compile Include="TestTypeSystemContext.cs" />
</ItemGroup>

Expand Down