Skip to content

Commit

Permalink
(#72) CodeGen: support fixed arrays inside of structs
Browse files Browse the repository at this point in the history
  • Loading branch information
ForNeVeR committed Jul 31, 2022
1 parent b203a88 commit 224856b
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 9 deletions.
25 changes: 23 additions & 2 deletions Cesium.CodeGen.Tests/CodeGenTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,14 @@ private static void DumpTypes(IEnumerable<TypeDefinition> 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);
}

Expand All @@ -117,8 +122,12 @@ private static void DumpTypes(IEnumerable<TypeDefinition> 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)
{
Expand All @@ -143,6 +152,18 @@ private static void DumpTypes(IEnumerable<TypeDefinition> types, StringBuilder r
DumpMethods(type, result, 2);
}
}

void PrintCustomAttributes(int nestedIndent, IEnumerable<CustomAttribute> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,3 @@
Type: foo
Fields:
System.Int32 foo::x
Init with: []
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,3 @@
Type: foo
Fields:
System.Int32 foo::x
Init with: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Module: Primary
Type: <Module>

Type: foo
Nested types:
Type: foo/<SyntheticBuffer>x
Pack: 0
Size: 16
Custom attributes:
- CompilerGeneratedAttribute()
- UnsafeValueTypeAttribute()

Fields:
System.Int32 foo/<SyntheticBuffer>x::FixedElementField
Fields:
foo/<SyntheticBuffer>x foo::x
Custom attributes:
- FixedBufferAttribute(System.Int32, 16)

4 changes: 4 additions & 0 deletions Cesium.CodeGen/Ir/Types/IType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
}

Expand Down
64 changes: 64 additions & 0 deletions Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
using Cesium.CodeGen.Contexts;
using Mono.Cecil;
using Mono.Cecil.Cil;
Expand All @@ -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)
Expand All @@ -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 <x>e__FixedBuffer
// {
// public int FixedElementField;
// }

var compilerGeneratedCtor = typeof(CompilerGeneratedAttribute).GetConstructor(Array.Empty<Type>());
var compilerGeneratedAttribute = new CustomAttribute(module.ImportReference(compilerGeneratedCtor));

var unsafeValueTypeCtor = typeof(UnsafeValueTypeAttribute).GetConstructor(Array.Empty<Type>());
var unsafeValueTypeAttribute = new CustomAttribute(module.ImportReference(unsafeValueTypeCtor));

return new TypeDefinition(
"",
$"<SyntheticBuffer>{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) }
};
}
}
7 changes: 2 additions & 5 deletions Cesium.CodeGen/Ir/Types/StructType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions Cesium.IntegrationTests/struct.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
typedef struct
{
int x[4];
} foo;

int main()
{
foo x;
return 42;
}

0 comments on commit 224856b

Please sign in to comment.