From 3768287c28333b7fd9a414d2d8bf585afe4a1f75 Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Sun, 31 Jul 2022 18:41:59 +0200 Subject: [PATCH] (#72) CodeGen: support fixed arrays inside of structs --- Cesium.CodeGen.Tests/CodeGenTestBase.cs | 23 ++++++- ...tructUsageWithMemberAccessGet.verified.txt | 1 - ...tructUsageWithMemberAccessSet.verified.txt | 1 - ...eGenTypeTests.StructWithArray.verified.txt | 19 ++++++ Cesium.CodeGen/Ir/Types/IType.cs | 4 ++ Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs | 64 +++++++++++++++++++ Cesium.CodeGen/Ir/Types/StructType.cs | 7 +- Cesium.IntegrationTests/struct.c | 10 +++ 8 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 Cesium.CodeGen.Tests/verified/CodeGenTypeTests.StructWithArray.verified.txt create mode 100644 Cesium.IntegrationTests/struct.c diff --git a/Cesium.CodeGen.Tests/CodeGenTestBase.cs b/Cesium.CodeGen.Tests/CodeGenTestBase.cs index 87523585..46951b55 100644 --- a/Cesium.CodeGen.Tests/CodeGenTestBase.cs +++ b/Cesium.CodeGen.Tests/CodeGenTestBase.cs @@ -105,9 +105,12 @@ private static void DumpTypes(IEnumerable types, StringBuilder r if (type.ClassSize != -1) result.AppendLine($"{Indent(indent)}Size: {type.ClassSize}"); + if (type.HasCustomAttributes) + PrintCustomAttributes(indent, type.CustomAttributes); + if (type.HasNestedTypes) { - result.AppendLine($"{Indent(indent)}Types:"); + result.AppendLine($"{Indent(indent)}Nested types:"); DumpTypes(type.NestedTypes, result, indent + 1); } @@ -117,8 +120,12 @@ private static void DumpTypes(IEnumerable types, StringBuilder r foreach (var field in type.Fields) { result.AppendLine($"{Indent(indent + 1)}{field}"); + + if (field.HasCustomAttributes) + PrintCustomAttributes(indent + 1, field.CustomAttributes); + var initialValue = field.InitialValue; - if (initialValue != null) + if (initialValue.Length > 0) { if (type.Name == AssemblyContext.ConstantPoolTypeName) { @@ -143,6 +150,18 @@ private static void DumpTypes(IEnumerable types, StringBuilder r DumpMethods(type, result, 2); } } + + void PrintCustomAttributes(int nestedIndent, IEnumerable customAttributes) + { + result.AppendLine($"{Indent(nestedIndent)}Custom attributes:"); + foreach (var customAttribute in customAttributes) + { + var arguments = string.Join(", ", customAttribute.ConstructorArguments.Select(a => a.Value)); + result.AppendLine($"{Indent(nestedIndent)}- {customAttribute.AttributeType.Name}({arguments})"); + } + + result.AppendLine(); + } } private static string Indent(int n = 1) => new(' ', n * 2); diff --git a/Cesium.CodeGen.Tests/verified/CodeGenTypeTests.StructUsageWithMemberAccessGet.verified.txt b/Cesium.CodeGen.Tests/verified/CodeGenTypeTests.StructUsageWithMemberAccessGet.verified.txt index 534edc55..6a441126 100644 --- a/Cesium.CodeGen.Tests/verified/CodeGenTypeTests.StructUsageWithMemberAccessGet.verified.txt +++ b/Cesium.CodeGen.Tests/verified/CodeGenTypeTests.StructUsageWithMemberAccessGet.verified.txt @@ -12,4 +12,3 @@ Type: foo Fields: System.Int32 foo::x - Init with: [] diff --git a/Cesium.CodeGen.Tests/verified/CodeGenTypeTests.StructUsageWithMemberAccessSet.verified.txt b/Cesium.CodeGen.Tests/verified/CodeGenTypeTests.StructUsageWithMemberAccessSet.verified.txt index 0ab0c5f5..36dc3ba2 100644 --- a/Cesium.CodeGen.Tests/verified/CodeGenTypeTests.StructUsageWithMemberAccessSet.verified.txt +++ b/Cesium.CodeGen.Tests/verified/CodeGenTypeTests.StructUsageWithMemberAccessSet.verified.txt @@ -14,4 +14,3 @@ Type: foo Fields: System.Int32 foo::x - Init with: [] diff --git a/Cesium.CodeGen.Tests/verified/CodeGenTypeTests.StructWithArray.verified.txt b/Cesium.CodeGen.Tests/verified/CodeGenTypeTests.StructWithArray.verified.txt new file mode 100644 index 00000000..03c63a64 --- /dev/null +++ b/Cesium.CodeGen.Tests/verified/CodeGenTypeTests.StructWithArray.verified.txt @@ -0,0 +1,19 @@ +Module: Primary + Type: + + Type: foo + Nested types: + Type: foo/x + Pack: 0 + Size: 16 + Custom attributes: + - CompilerGeneratedAttribute() + - UnsafeValueTypeAttribute() + + Fields: + System.Int32 foo/x::FixedElementField + Fields: + foo/x foo::x + Custom attributes: + - FixedBufferAttribute(System.Int32, 16) + diff --git a/Cesium.CodeGen/Ir/Types/IType.cs b/Cesium.CodeGen/Ir/Types/IType.cs index dea470a6..11fc7026 100644 --- a/Cesium.CodeGen/Ir/Types/IType.cs +++ b/Cesium.CodeGen/Ir/Types/IType.cs @@ -11,6 +11,10 @@ namespace Cesium.CodeGen.Ir.Types; internal interface IType { TypeReference Resolve(TranslationUnitContext context); + + FieldDefinition CreateFieldOfType(TranslationUnitContext context, TypeDefinition ownerType, string fieldName) => + new(fieldName, FieldAttributes.Public, Resolve(context)); + int SizeInBytes { get; } } diff --git a/Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs b/Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs index a37336c8..3ef9ef2c 100644 --- a/Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs +++ b/Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using Cesium.CodeGen.Contexts; using Mono.Cecil; using Mono.Cecil.Cil; @@ -12,6 +13,35 @@ public TypeReference Resolve(TranslationUnitContext context) return Base.Resolve(context).MakePointerType(); } + public FieldDefinition CreateFieldOfType(TranslationUnitContext context, TypeDefinition ownerType, string fieldName) + { + var itemType = Base.Resolve(context); + var bufferType = CreateFixedBufferType(context.Module, itemType, fieldName); + ownerType.NestedTypes.Add(bufferType); + + return new FieldDefinition(fieldName, FieldAttributes.Public, bufferType) + { + CustomAttributes = { GenerateCustomFieldAttribute() } + }; + + CustomAttribute GenerateCustomFieldAttribute() + { + var fixedBufferCtor = typeof(FixedBufferAttribute).GetConstructor(new[] { typeof(Type), typeof(int) }); + if (fixedBufferCtor == null) + throw new NotSupportedException( + "Cannot find a constructor with signature (Type, Int32) in type FixedBufferAttribute."); + + return new CustomAttribute(context.Module.ImportReference(fixedBufferCtor)) + { + ConstructorArguments = + { + new CustomAttributeArgument(context.Module.ImportReference(typeof(Type)), itemType), + new CustomAttributeArgument(context.TypeSystem.Int32, SizeInBytes) + } + }; + } + } + public void EmitInitializer(IDeclarationScope scope) { if (Base is not PrimitiveType) @@ -26,4 +56,38 @@ public void EmitInitializer(IDeclarationScope scope) } public int SizeInBytes => Base.SizeInBytes * Size; + + private TypeDefinition CreateFixedBufferType( + ModuleDefinition module, + TypeReference fieldType, + string fieldName) + { + // An example of what C# does for fixed int x[20]: + // + // [StructLayout(LayoutKind.Sequential, Size = 80)] + // [CompilerGenerated] + // [UnsafeValueType] + // public struct e__FixedBuffer + // { + // public int FixedElementField; + // } + + var compilerGeneratedCtor = typeof(CompilerGeneratedAttribute).GetConstructor(Array.Empty()); + var compilerGeneratedAttribute = new CustomAttribute(module.ImportReference(compilerGeneratedCtor)); + + var unsafeValueTypeCtor = typeof(UnsafeValueTypeAttribute).GetConstructor(Array.Empty()); + var unsafeValueTypeAttribute = new CustomAttribute(module.ImportReference(unsafeValueTypeCtor)); + + return new TypeDefinition( + "", + $"{fieldName}", + TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.SequentialLayout | TypeAttributes.NestedPublic, + module.ImportReference(typeof(ValueType))) + { + PackingSize = 0, + ClassSize = SizeInBytes, + CustomAttributes = { compilerGeneratedAttribute, unsafeValueTypeAttribute }, + Fields = { new FieldDefinition("FixedElementField", FieldAttributes.Public, fieldType) } + }; + } } diff --git a/Cesium.CodeGen/Ir/Types/StructType.cs b/Cesium.CodeGen/Ir/Types/StructType.cs index 35de3f61..5aef4e69 100644 --- a/Cesium.CodeGen/Ir/Types/StructType.cs +++ b/Cesium.CodeGen/Ir/Types/StructType.cs @@ -32,11 +32,8 @@ public TypeDefinition Emit(string name, TranslationUnitContext context) throw new NotSupportedException( $"CLI imports inside struct members aren't supported: {cliImportMemberName}."); - structType.Fields.Add( - new FieldDefinition( - identifier, - FieldAttributes.Public, - type.Resolve(context))); + var field = type.CreateFieldOfType(context, structType, identifier); + structType.Fields.Add(field); } return structType; diff --git a/Cesium.IntegrationTests/struct.c b/Cesium.IntegrationTests/struct.c new file mode 100644 index 00000000..44dd9341 --- /dev/null +++ b/Cesium.IntegrationTests/struct.c @@ -0,0 +1,10 @@ +typedef struct +{ + int x[4]; +} foo; + +int main() +{ + foo x; + return 42; +}