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 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]); }