public static AlignAndSize AlignAndSizeOfType(MetadataType mtype, int bits) { if (mtype == MetadataType.Boolean || mtype == MetadataType.Byte || mtype == MetadataType.SByte) { return(AlignAndSize.One); } if (mtype == MetadataType.Int16 || mtype == MetadataType.UInt16 || mtype == MetadataType.Char) { return(AlignAndSize.Two); } if (mtype == MetadataType.Int32 || mtype == MetadataType.UInt32 || mtype == MetadataType.Single) { return(AlignAndSize.Four); } if (mtype == MetadataType.Int64 || mtype == MetadataType.UInt64 || mtype == MetadataType.Double) { return(AlignAndSize.Eight); } if (mtype == MetadataType.IntPtr || mtype == MetadataType.UIntPtr) { return(AlignAndSize.Pointer(bits)); } if (mtype == MetadataType.String) { return(AlignAndSize.NativeString(bits)); } throw new ArgumentException($"Metadata type {mtype} is a special type which is not supported"); }
public static void PreprocessTypeFields(TypeDefinition valuetype, int bits) { if (bits == 32) { bits = 0; } else if (bits == 64) { bits = 1; } int size = 0; int highestFieldAlignment = 0; bool isComplex = false; // have we already preprocessed this? if (ValueTypeAlignment[bits].ContainsKey(valuetype) && !ValueTypeAlignment[bits][valuetype].IsSentinel) { return; } // For each field, calculate its layout as if it was a C++ struct //Console.WriteLine($"Type {valuetype}"); foreach (var fs in valuetype.Fields) { if (fs.IsStatic) { continue; } var sz = AlignAndSizeOfType(fs.FieldType, bits); isComplex = isComplex || fs.FieldType.IsComplex(); // In C++, all members of a struct must have their own address. // If we have a "struct {}" as a member, treat its size as at least one byte. sz = new AlignAndSize(sz.align, Math.Max(sz.size, 1)); highestFieldAlignment = Math.Max(highestFieldAlignment, sz.align); size = AlignUp(size, sz.align); //Console.WriteLine($" Field: {fs.Name} ({fs.GetType()}) - offset: {size} alignment {sz.align} sz {sz.size}"); StructFieldAlignment[bits].Add(fs /*VERIFY*/, new AlignAndSize(sz.align, sz.size, size)); size += sz.size; } // same min size for outer struct size = Math.Max(size, 1); // C++ aligns struct sizes up to the highest alignment required size = AlignUp(size, highestFieldAlignment); // Alignment requirements are > 0 highestFieldAlignment = Math.Max(highestFieldAlignment, 1); ValueTypeAlignment[bits].Remove(valuetype); ValueTypeAlignment[bits].Add(valuetype, new AlignAndSize(highestFieldAlignment, size, 0, valuetype.Fields.Count == 0)); //Console.WriteLine($"ValueType: {valuetype.Name} ({valuetype.GetType()}) - alignment {highestFieldAlignment} sz {size}"); ValueTypeIsComplex[bits].Add(valuetype, isComplex); }
public static AlignAndSize AlignAndSizeOfType(TypeReference typeRef, int bits) { // This is a gross hack and i'm not proud of it; we use bits as an array index, // and we call this method recursively. if (bits == 32) { bits = 0; } else if (bits == 64) { bits = 1; } if (typeRef.IsPointer) { return(AlignAndSize.Pointer(bits)); } TypeDefinition fixedSpecialType = typeRef.FixedSpecialType(); if (fixedSpecialType != null) { return(AlignAndSizeOfType(fixedSpecialType.MetadataType, bits)); } var type = typeRef.Resolve(); // Handle the case where we have a fixed buffer. Cecil will name it: "<MyMemberName>e_FixedBuffer" if (type.ClassSize != -1 && typeRef.Name.Contains(">e__FixedBuffer")) { // Fixed buffers can only be of primitive types so inspect the fields of the buffer (there should only be one) // and determine the packing requirement for the type if (type.Fields.Count() != 1) { throw new ArgumentException("A FixedBuffer type contains more than one field, this should not happen"); } var fieldAlignAndSize = AlignAndSizeOfType(type.Fields[0].FieldType.MetadataType, bits); return(new AlignAndSize(fieldAlignAndSize.align, type.ClassSize)); } else if (type.IsExplicitLayout && type.ClassSize > 0 && type.PackingSize > 0) { return(new AlignAndSize(type.PackingSize, type.ClassSize)); } if (ValueTypeAlignment[bits].ContainsKey(typeRef)) { var sz = ValueTypeAlignment[bits][typeRef]; if (sz.IsSentinel) { throw new ArgumentException($"Type {typeRef} triggered sentinel; recursive value type definition"); } return(sz); } if (typeRef.IsArray) { throw new ArgumentException($"Can't represent {typeRef}: C# array types cannot be represented directly, use DynamicArray<T>"); } if (typeRef.IsDynamicArray()) { var elementType = typeRef.DynamicArrayElementType(); //Console.WriteLine($"{nts.TypeArguments[0]}"); // call this just for the side effect checks if (AlignAndSizeOfType(elementType, bits).size == 0) { throw new Exception("Unexpected type with size 0: " + elementType); } // arrays match std::array, for emscripten at least return(AlignAndSize.DynamicArray(bits)); } if (type.IsEnum) { // Inspect the __value member to determine the underlying type size var enumBaseType = type.Fields.First(f => f.Name == "value__").FieldType; return(AlignAndSizeOfType(enumBaseType, bits)); } if (!type.IsValueType) { // Why not throw? Really should expect this: //throw new ArgumentException($"Type {type} ({type.Name}) was expected to be a value type"); // However, the DisposeSentinel is a ManagedType that sits in the NativeContainers. // Treat it as an opaque pointer and skip over it. return(AlignAndSizeOfType(MetadataType.IntPtr, bits)); } ValueTypeAlignment[bits].Add(typeRef, AlignAndSize.Sentinel); PreprocessTypeFields(typeRef, bits); return(ValueTypeAlignment[bits][typeRef]); }