-
Notifications
You must be signed in to change notification settings - Fork 503
Add tests for static field layout #33
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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; | ||
| } | ||
| } |
| 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); | ||
| 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); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I can do that.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| 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); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).