mirror of
https://github.com/Smaug123/WoofWare.PawPrint
synced 2025-10-05 22:28:38 +00:00
Implement sizeof completely (#119)
This commit is contained in:
@@ -33,6 +33,20 @@ type ConcreteType<'typeGeneric> =
|
||||
_Generics : ImmutableArray<'typeGeneric>
|
||||
}
|
||||
|
||||
override this.ToString () : string =
|
||||
let basic = $"%s{this.Assembly.Name}.%s{this.Namespace}.%s{this.Name}"
|
||||
|
||||
let generics =
|
||||
if this.Generics.IsEmpty then
|
||||
""
|
||||
else
|
||||
this.Generics
|
||||
|> Seq.map string
|
||||
|> String.concat ", "
|
||||
|> fun x -> "<" + x + ">"
|
||||
|
||||
basic + generics
|
||||
|
||||
member this.Assembly : AssemblyName = this._AssemblyName
|
||||
member this.Definition : ComparableTypeDefinitionHandle = this._Definition
|
||||
member this.Generics : ImmutableArray<'typeGeneric> = this._Generics
|
||||
|
@@ -57,11 +57,10 @@ module FieldInfo =
|
||||
let fieldSig = def.DecodeSignature (TypeDefn.typeProvider assembly, ())
|
||||
let declaringType = def.GetDeclaringType ()
|
||||
|
||||
let typeGenerics =
|
||||
mr.GetTypeDefinition(declaringType).GetGenericParameters ()
|
||||
|> GenericParameter.readAll mr
|
||||
|
||||
let decType = mr.GetTypeDefinition declaringType
|
||||
|
||||
let typeGenerics = decType.GetGenericParameters () |> GenericParameter.readAll mr
|
||||
|
||||
let declaringTypeNamespace = mr.GetString decType.Namespace
|
||||
let declaringTypeName = mr.GetString decType.Name
|
||||
|
||||
|
@@ -29,6 +29,10 @@ type InterfaceImplementation =
|
||||
RelativeToAssembly : AssemblyName
|
||||
}
|
||||
|
||||
type Layout =
|
||||
| Default
|
||||
| Custom of size : int * packingSize : int
|
||||
|
||||
/// <summary>
|
||||
/// Represents detailed information about a type definition in a .NET assembly.
|
||||
/// This is a strongly-typed representation of TypeDefinition from System.Reflection.Metadata.
|
||||
@@ -93,6 +97,8 @@ type TypeInfo<'generic, 'fieldGeneric> =
|
||||
Events : EventDefn ImmutableArray
|
||||
|
||||
ImplementedInterfaces : InterfaceImplementation ImmutableArray
|
||||
|
||||
Layout : Layout
|
||||
}
|
||||
|
||||
member this.IsInterface = this.TypeAttributes.HasFlag TypeAttributes.Interface
|
||||
@@ -213,6 +219,7 @@ module TypeInfo =
|
||||
Generics = gen
|
||||
Events = t.Events
|
||||
ImplementedInterfaces = t.ImplementedInterfaces
|
||||
Layout = t.Layout
|
||||
}
|
||||
|
||||
let mapGeneric<'a, 'b, 'field> (f : 'a -> 'b) (t : TypeInfo<'a, 'field>) : TypeInfo<'b, 'field> =
|
||||
@@ -308,6 +315,14 @@ module TypeInfo =
|
||||
|
||||
result.ToImmutable ()
|
||||
|
||||
let layout =
|
||||
let l = typeDef.GetLayout ()
|
||||
|
||||
if l.IsDefault then
|
||||
Layout.Default
|
||||
else
|
||||
Layout.Custom (size = l.Size, packingSize = l.PackingSize)
|
||||
|
||||
{
|
||||
Namespace = ns
|
||||
Name = name
|
||||
@@ -323,6 +338,7 @@ module TypeInfo =
|
||||
Events = events
|
||||
ImplementedInterfaces = interfaces
|
||||
DeclaringType = declaringType
|
||||
Layout = layout
|
||||
}
|
||||
|
||||
let isBaseType<'corelib>
|
||||
|
@@ -51,11 +51,6 @@ module TestPureCases =
|
||||
ExpectedReturnCode = 114
|
||||
NativeImpls = MockEnv.make ()
|
||||
}
|
||||
{
|
||||
FileName = "Sizeof.cs"
|
||||
ExpectedReturnCode = 0
|
||||
NativeImpls = MockEnv.make ()
|
||||
}
|
||||
{
|
||||
FileName = "LdtokenField.cs"
|
||||
ExpectedReturnCode = 0
|
||||
@@ -80,6 +75,16 @@ module TestPureCases =
|
||||
ExpectedReturnCode = 1
|
||||
NativeImpls = MockEnv.make ()
|
||||
}
|
||||
{
|
||||
FileName = "Sizeof.cs"
|
||||
ExpectedReturnCode = 0
|
||||
NativeImpls = MockEnv.make ()
|
||||
}
|
||||
{
|
||||
FileName = "Sizeof2.cs"
|
||||
ExpectedReturnCode = 0
|
||||
NativeImpls = MockEnv.make ()
|
||||
}
|
||||
{
|
||||
FileName = "Initobj.cs"
|
||||
ExpectedReturnCode = 0
|
||||
|
@@ -10,16 +10,16 @@ unsafe public class Program
|
||||
|
||||
public struct MediumStruct
|
||||
{
|
||||
public int Value1;
|
||||
public int Value2;
|
||||
public int MediumValue1;
|
||||
public int MediumValue2;
|
||||
}
|
||||
|
||||
public struct LargeStruct
|
||||
{
|
||||
public long Value1;
|
||||
public long Value2;
|
||||
public long Value3;
|
||||
public long Value4;
|
||||
public long LongValue1;
|
||||
public long LongValue2;
|
||||
public long LongValue3;
|
||||
public long LongValue4;
|
||||
}
|
||||
|
||||
public struct NestedStruct
|
||||
|
235
WoofWare.PawPrint.Test/sourcesPure/Sizeof2.cs
Normal file
235
WoofWare.PawPrint.Test/sourcesPure/Sizeof2.cs
Normal file
@@ -0,0 +1,235 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
unsafe public class Program
|
||||
{
|
||||
// Test for empty struct (should be 1 byte, not 0)
|
||||
public struct EmptyStruct
|
||||
{
|
||||
}
|
||||
|
||||
// Test for char alignment (should align to 2, not 1)
|
||||
public struct CharStruct
|
||||
{
|
||||
public byte B;
|
||||
public char C; // Should be at offset 2, not 1
|
||||
}
|
||||
|
||||
// Test for end padding
|
||||
public struct NeedsEndPadding
|
||||
{
|
||||
public int X;
|
||||
public byte Y;
|
||||
// Should pad to 8 bytes total (multiple of 4)
|
||||
}
|
||||
|
||||
// Test Pack=1 (no padding)
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct PackedStruct
|
||||
{
|
||||
public byte B;
|
||||
public int I; // At offset 1, not 4
|
||||
public byte B2;
|
||||
// Total 6 bytes, no padding
|
||||
}
|
||||
|
||||
// Test Pack=2
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||
public struct Pack2Struct
|
||||
{
|
||||
public byte B;
|
||||
public int I; // At offset 2 (2-byte aligned, not 4)
|
||||
public byte B2;
|
||||
// Should pad to 8 bytes (multiple of 2)
|
||||
}
|
||||
|
||||
// Test custom size smaller than natural size
|
||||
[StructLayout(LayoutKind.Sequential, Size = 12)]
|
||||
public struct CustomSizeSmaller
|
||||
{
|
||||
public long L1;
|
||||
public long L2;
|
||||
// Natural size is 16, but Size=12 is ignored (12 < 16)
|
||||
}
|
||||
|
||||
// Test custom size larger than natural size
|
||||
[StructLayout(LayoutKind.Sequential, Size = 20)]
|
||||
public struct CustomSizeLarger
|
||||
{
|
||||
public long L;
|
||||
// Natural size is 8, custom size 20 should win
|
||||
}
|
||||
|
||||
// Test custom size not multiple of alignment
|
||||
[StructLayout(LayoutKind.Sequential, Size = 15)]
|
||||
public struct CustomSizeOdd
|
||||
{
|
||||
public long L;
|
||||
// Size=15 should be honored even though not multiple of 8
|
||||
}
|
||||
|
||||
// Test Pack=0 (means default, not 0)
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 0)]
|
||||
public struct Pack0Struct
|
||||
{
|
||||
public byte B;
|
||||
public int I; // Should be at offset 4 (default packing)
|
||||
}
|
||||
|
||||
// Test both Pack and Size
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 10)]
|
||||
public struct PackAndSize
|
||||
{
|
||||
public byte B;
|
||||
public int I;
|
||||
// Natural packed size is 5, custom size 10 should win
|
||||
}
|
||||
|
||||
// Test explicit with custom Size
|
||||
[StructLayout(LayoutKind.Explicit, Size = 10)]
|
||||
public struct ExplicitWithSize
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public int I;
|
||||
[FieldOffset(2)]
|
||||
public short S;
|
||||
// Max offset+size is 4, but Size=10 should win
|
||||
}
|
||||
|
||||
public struct SmallStruct
|
||||
{
|
||||
public byte Value;
|
||||
}
|
||||
|
||||
public struct MediumStruct
|
||||
{
|
||||
public int MediumValue1;
|
||||
public int MediumValue2;
|
||||
}
|
||||
|
||||
public struct LargeStruct
|
||||
{
|
||||
public long LongValue1;
|
||||
public long LongValue2;
|
||||
public long LongValue3;
|
||||
public long LongValue4;
|
||||
}
|
||||
|
||||
public struct NestedStruct
|
||||
{
|
||||
public SmallStruct Small;
|
||||
public MediumStruct Medium;
|
||||
public int Extra;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct UnionStruct
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public int AsInt;
|
||||
[FieldOffset(0)]
|
||||
public float AsFloat;
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
// Test 1: Basic primitive types
|
||||
if (sizeof(byte) != 1) return 1;
|
||||
if (sizeof(sbyte) != 1) return 2;
|
||||
if (sizeof(short) != 2) return 3;
|
||||
if (sizeof(ushort) != 2) return 4;
|
||||
if (sizeof(int) != 4) return 5;
|
||||
if (sizeof(uint) != 4) return 6;
|
||||
if (sizeof(long) != 8) return 7;
|
||||
if (sizeof(ulong) != 8) return 8;
|
||||
if (sizeof(float) != 4) return 9;
|
||||
if (sizeof(double) != 8) return 10;
|
||||
if (sizeof(char) != 2) return 11;
|
||||
if (sizeof(bool) != 1) return 12;
|
||||
|
||||
// Test 2: Struct sizes
|
||||
if (sizeof(SmallStruct) != 1) return 13;
|
||||
if (sizeof(MediumStruct) != 8) return 14;
|
||||
if (sizeof(LargeStruct) != 32) return 15;
|
||||
|
||||
// Test 3: Nested struct size
|
||||
// SmallStruct (1) + padding (3) + MediumStruct (8) + int (4) = 16
|
||||
if (sizeof(NestedStruct) != 16) return 16;
|
||||
|
||||
// Test 4: Union struct size
|
||||
if (sizeof(UnionStruct) != 4) return 17;
|
||||
|
||||
// Test 5: Enum size (underlying type is int)
|
||||
if (sizeof(DayOfWeek) != 4) return 18;
|
||||
|
||||
// Test 6: Empty struct (should be 1, not 0)
|
||||
if (sizeof(EmptyStruct) != 1) return 19;
|
||||
|
||||
// Test 7: Char alignment
|
||||
// byte (1) + padding (1) + char (2) = 4
|
||||
if (sizeof(CharStruct) != 4) return 20;
|
||||
|
||||
// Test 8: End padding
|
||||
// int (4) + byte (1) + padding (3) = 8
|
||||
if (sizeof(NeedsEndPadding) != 8) return 21;
|
||||
|
||||
// Test 9: Pack=1 removes all padding
|
||||
// byte (1) + int (4) + byte (1) = 6
|
||||
if (sizeof(PackedStruct) != 6) return 22;
|
||||
|
||||
// Test 10: Pack=2
|
||||
// byte (1) + padding (1) + int (4) + byte (1) + padding (1) = 8
|
||||
if (sizeof(Pack2Struct) != 8) return 23;
|
||||
|
||||
// Test 11: Custom size smaller than natural (ignored)
|
||||
if (sizeof(CustomSizeSmaller) != 16) return 24;
|
||||
|
||||
// Test 12: Custom size larger than natural (honored)
|
||||
if (sizeof(CustomSizeLarger) != 20) return 25;
|
||||
|
||||
// Test 13: Custom size not multiple of alignment (honored)
|
||||
if (sizeof(CustomSizeOdd) != 15) return 26;
|
||||
|
||||
// Test 14: Pack=0 means default packing
|
||||
// byte (1) + padding (3) + int (4) = 8
|
||||
if (sizeof(Pack0Struct) != 8) return 27;
|
||||
|
||||
// Test 15: Pack and Size together
|
||||
// Natural packed: byte (1) + int (4) = 5, but Size=10
|
||||
if (sizeof(PackAndSize) != 10) return 28;
|
||||
|
||||
// Test 16: Explicit with Size
|
||||
// Max used is 4, but Size=10
|
||||
if (sizeof(ExplicitWithSize) != 10) return 29;
|
||||
|
||||
// Test 17: Pointer types
|
||||
unsafe
|
||||
{
|
||||
if (sizeof(IntPtr) != sizeof(void*)) return 30;
|
||||
if (sizeof(UIntPtr) != sizeof(void*)) return 31;
|
||||
}
|
||||
|
||||
// Test 18: Using sizeof in expressions
|
||||
int totalSize = sizeof(int) + sizeof(long) + sizeof(byte);
|
||||
if (totalSize != 13) return 32;
|
||||
|
||||
// Test 19: Array element size calculation
|
||||
int arrayElementSize = sizeof(MediumStruct);
|
||||
int arraySize = arrayElementSize * 3;
|
||||
if (arraySize != 24) return 33;
|
||||
|
||||
// Test 20: Complex nested struct with Pack
|
||||
// byte (1) + CharStruct (4) + byte (1) = 6
|
||||
if (sizeof(PackedNested) != 6) return 34;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct PackedNested
|
||||
{
|
||||
public byte B;
|
||||
public CharStruct C;
|
||||
public byte B2;
|
||||
}
|
||||
}
|
@@ -134,6 +134,12 @@ type CliRuntimePointer =
|
||||
| Unmanaged of int64
|
||||
| Managed of ManagedPointerSource
|
||||
|
||||
type SizeofResult =
|
||||
{
|
||||
Alignment : int
|
||||
Size : int
|
||||
}
|
||||
|
||||
/// This is the kind of type that can be stored in arguments, local variables, statics, array elements, fields.
|
||||
type CliType =
|
||||
/// III.1.1.1
|
||||
@@ -150,13 +156,35 @@ type CliType =
|
||||
/// as a concatenated list of its fields.
|
||||
| ValueType of CliValueType
|
||||
|
||||
static member SizeOf (t : CliType) : int =
|
||||
static member SizeOf (t : CliType) : SizeofResult =
|
||||
match t with
|
||||
| CliType.Numeric ty -> CliNumericType.SizeOf ty
|
||||
| CliType.Bool _ -> 1
|
||||
| CliType.Char _ -> 2
|
||||
| CliType.ObjectRef _ -> 8
|
||||
| CliType.RuntimePointer _ -> 8
|
||||
| CliType.Numeric ty ->
|
||||
let size = CliNumericType.SizeOf ty
|
||||
|
||||
{
|
||||
Size = size
|
||||
Alignment = size
|
||||
}
|
||||
| CliType.Bool _ ->
|
||||
{
|
||||
Size = 1
|
||||
Alignment = 1
|
||||
}
|
||||
| CliType.Char _ ->
|
||||
{
|
||||
Size = 2
|
||||
Alignment = 2
|
||||
}
|
||||
| CliType.ObjectRef _ ->
|
||||
{
|
||||
Size = 8
|
||||
Alignment = 8
|
||||
}
|
||||
| CliType.RuntimePointer _ ->
|
||||
{
|
||||
Size = 8
|
||||
Alignment = 8
|
||||
}
|
||||
| CliType.ValueType vt -> CliValueType.SizeOf vt
|
||||
|
||||
and CliField =
|
||||
@@ -171,33 +199,95 @@ and CliValueType =
|
||||
private
|
||||
{
|
||||
_Fields : CliField list
|
||||
Layout : Layout
|
||||
}
|
||||
|
||||
static member OfFields (f : CliField list) =
|
||||
static member OfFields (layout : Layout) (f : CliField list) =
|
||||
{
|
||||
_Fields = f
|
||||
Layout = layout
|
||||
}
|
||||
|
||||
static member AddField (f : CliField) (vt : CliValueType) =
|
||||
{
|
||||
_Fields = f :: vt._Fields
|
||||
Layout = vt.Layout
|
||||
}
|
||||
|
||||
static member DereferenceField (name : string) (f : CliValueType) : CliType =
|
||||
// TODO: this is wrong, it doesn't account for overlapping fields
|
||||
f._Fields |> List.find (fun f -> f.Name = name) |> _.Contents
|
||||
|
||||
static member SizeOf (vt : CliValueType) : int =
|
||||
match vt._Fields with
|
||||
| [] -> failwith "is it even possible to instantiate a value type with no fields"
|
||||
| [ field ] -> CliType.SizeOf field.Contents
|
||||
| fields ->
|
||||
// TODO: consider struct layout (there's an `Explicit` test that will exercise that)
|
||||
fields |> List.map (_.Contents >> CliType.SizeOf) |> List.sum
|
||||
static member SizeOf (vt : CliValueType) : SizeofResult =
|
||||
let minimumSize, packingSize =
|
||||
match vt.Layout with
|
||||
| Layout.Custom (size = size ; packingSize = packing) ->
|
||||
size, if packing = 0 then DEFAULT_STRUCT_ALIGNMENT else packing
|
||||
| Layout.Default -> 0, DEFAULT_STRUCT_ALIGNMENT
|
||||
|
||||
if vt._Fields.IsEmpty then
|
||||
{
|
||||
Size = minimumSize
|
||||
Alignment = 1
|
||||
}
|
||||
else
|
||||
|
||||
let seqFields, nonSeqFields =
|
||||
vt._Fields |> List.partition (fun field -> field.Offset.IsNone)
|
||||
|
||||
let finalOffset, alignment =
|
||||
match seqFields, nonSeqFields with
|
||||
| [], [] -> (1, packingSize)
|
||||
| _ :: _, [] ->
|
||||
((0, 0), seqFields)
|
||||
||> List.fold (fun (currentOffset, maxAlignmentCap) field ->
|
||||
let size = CliType.SizeOf field.Contents
|
||||
let alignmentCap = min size.Alignment packingSize
|
||||
let error = currentOffset % alignmentCap
|
||||
|
||||
let currentOffset =
|
||||
if error > 0 then
|
||||
alignmentCap - error + currentOffset
|
||||
else
|
||||
currentOffset
|
||||
|
||||
currentOffset + size.Size, max maxAlignmentCap alignmentCap
|
||||
)
|
||||
|
||||
| [], _ :: _ ->
|
||||
nonSeqFields
|
||||
|> List.map (fun field ->
|
||||
let offset = field.Offset.Value
|
||||
let size = CliType.SizeOf field.Contents
|
||||
|
||||
let alignmentCap = min size.Alignment packingSize
|
||||
|
||||
offset + size.Size, alignmentCap
|
||||
)
|
||||
|> List.fold
|
||||
(fun (finalOffset, alignment) (newFinalOffset, newAlignment) ->
|
||||
max finalOffset newFinalOffset, max alignment newAlignment
|
||||
)
|
||||
(0, 0)
|
||||
| _ :: _, _ :: _ -> failwith "unexpectedly mixed explicit and automatic layout of fields"
|
||||
|
||||
let error = finalOffset % alignment
|
||||
|
||||
let size =
|
||||
if error = 0 then
|
||||
finalOffset
|
||||
else
|
||||
finalOffset + (alignment - error)
|
||||
|
||||
{
|
||||
Size = max size minimumSize
|
||||
Alignment = alignment
|
||||
}
|
||||
|
||||
static member WithFieldSet (field : string) (value : CliType) (cvt : CliValueType) : CliValueType =
|
||||
// TODO: this doesn't account for overlapping fields
|
||||
{
|
||||
Layout = cvt.Layout
|
||||
_Fields =
|
||||
cvt._Fields
|
||||
|> List.replaceWhere (fun f ->
|
||||
@@ -241,7 +331,7 @@ module CliType =
|
||||
|
||||
let ofManagedObject (ptr : ManagedHeapAddress) : CliType = CliType.ObjectRef (Some ptr)
|
||||
|
||||
let sizeOf (ty : CliType) : int = CliType.SizeOf ty
|
||||
let sizeOf (ty : CliType) : int = CliType.SizeOf(ty).Size
|
||||
|
||||
let zeroOfPrimitive (primitiveType : PrimitiveType) : CliType =
|
||||
match primitiveType with
|
||||
@@ -267,19 +357,19 @@ module CliType =
|
||||
{
|
||||
Name = "_value"
|
||||
Contents = CliType.RuntimePointer (CliRuntimePointer.Managed ManagedPointerSource.Null)
|
||||
Offset = Some 0
|
||||
Offset = None
|
||||
}
|
||||
|> List.singleton
|
||||
|> CliValueType.OfFields
|
||||
|> CliValueType.OfFields Layout.Default
|
||||
|> CliType.ValueType
|
||||
| PrimitiveType.UIntPtr ->
|
||||
{
|
||||
Name = "_value"
|
||||
Contents = CliType.RuntimePointer (CliRuntimePointer.Managed ManagedPointerSource.Null)
|
||||
Offset = Some 0
|
||||
Offset = None
|
||||
}
|
||||
|> List.singleton
|
||||
|> CliValueType.OfFields
|
||||
|> CliValueType.OfFields Layout.Default
|
||||
|> CliType.ValueType
|
||||
| PrimitiveType.Object -> CliType.ObjectRef None
|
||||
|
||||
@@ -441,7 +531,7 @@ module CliType =
|
||||
Offset = field.Offset
|
||||
}
|
||||
)
|
||||
|> CliValueType.OfFields
|
||||
|> CliValueType.OfFields typeDef.Layout
|
||||
|
||||
CliType.ValueType vt, currentConcreteTypes
|
||||
else
|
||||
|
@@ -8,3 +8,6 @@ module Constants =
|
||||
|
||||
[<Literal>]
|
||||
let SIZEOF_OBJ = 8
|
||||
|
||||
[<Literal>]
|
||||
let DEFAULT_STRUCT_ALIGNMENT = 8
|
||||
|
@@ -36,6 +36,7 @@ and EvalStackValueField =
|
||||
and EvalStackValueUserType =
|
||||
{
|
||||
Fields : EvalStackValueField list
|
||||
Layout : Layout
|
||||
}
|
||||
|
||||
static member DereferenceField (name : string) (this : EvalStackValueUserType) =
|
||||
@@ -48,9 +49,10 @@ and EvalStackValueUserType =
|
||||
None
|
||||
)
|
||||
|
||||
static member OfFields (fields : EvalStackValueField list) =
|
||||
static member OfFields (layout : Layout) (fields : EvalStackValueField list) =
|
||||
{
|
||||
Fields = fields
|
||||
Layout = layout
|
||||
}
|
||||
|
||||
static member TrySequentialFields (cvt : EvalStackValueUserType) : EvalStackValueField list option =
|
||||
@@ -263,8 +265,8 @@ module EvalStackValue =
|
||||
| popped -> failwith $"Unexpectedly wanted a char from {popped}"
|
||||
| CliType.ValueType vt ->
|
||||
match popped with
|
||||
| EvalStackValue.UserDefinedValueType popped ->
|
||||
match CliValueType.TrySequentialFields vt, EvalStackValueUserType.TrySequentialFields popped with
|
||||
| EvalStackValue.UserDefinedValueType popped' ->
|
||||
match CliValueType.TrySequentialFields vt, EvalStackValueUserType.TrySequentialFields popped' with
|
||||
| Some vt, Some popped ->
|
||||
if vt.Length <> popped.Length then
|
||||
failwith
|
||||
@@ -286,7 +288,7 @@ module EvalStackValue =
|
||||
Offset = field1.Offset
|
||||
}
|
||||
)
|
||||
|> CliValueType.OfFields
|
||||
|> CliValueType.OfFields popped'.Layout
|
||||
|> CliType.ValueType
|
||||
| _, _ -> failwith "TODO: overlapping fields going onto eval stack"
|
||||
| popped ->
|
||||
@@ -322,7 +324,6 @@ module EvalStackValue =
|
||||
| CliRuntimePointer.Unmanaged ptrInt -> NativeIntSource.Verbatim ptrInt |> EvalStackValue.NativeInt
|
||||
| CliRuntimePointer.Managed ptr -> ptr |> EvalStackValue.ManagedPointer
|
||||
| CliType.ValueType fields ->
|
||||
// TODO: this is a bit dubious; we're being a bit sloppy with possibly-overlapping fields here
|
||||
// The only allowable use of _Fields
|
||||
fields._Fields
|
||||
|> List.map (fun field ->
|
||||
@@ -334,7 +335,7 @@ module EvalStackValue =
|
||||
ContentsEval = contents
|
||||
}
|
||||
)
|
||||
|> EvalStackValueUserType.OfFields
|
||||
|> EvalStackValueUserType.OfFields fields.Layout
|
||||
|> EvalStackValue.UserDefinedValueType
|
||||
|
||||
type EvalStack =
|
||||
|
@@ -59,7 +59,7 @@ module FieldHandleRegistry =
|
||||
Offset = None
|
||||
}
|
||||
|> List.singleton
|
||||
|> CliValueType.OfFields
|
||||
|> CliValueType.OfFields Layout.Default
|
||||
|> CliType.ValueType
|
||||
|
||||
let handle =
|
||||
@@ -92,7 +92,7 @@ module FieldHandleRegistry =
|
||||
Offset = None // no struct layout was specified
|
||||
}
|
||||
|> List.singleton
|
||||
|> CliValueType.OfFields
|
||||
|> CliValueType.OfFields Layout.Default
|
||||
|> CliType.ValueType
|
||||
|
||||
// https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs#L1074
|
||||
@@ -103,36 +103,36 @@ module FieldHandleRegistry =
|
||||
{
|
||||
Name = "m_keepalive"
|
||||
Contents = CliType.ObjectRef None
|
||||
Offset = Some 0
|
||||
Offset = None
|
||||
}
|
||||
{
|
||||
Name = "m_c"
|
||||
Contents = CliType.ObjectRef None
|
||||
Offset = Some SIZEOF_OBJ
|
||||
Offset = None
|
||||
}
|
||||
{
|
||||
Name = "m_d"
|
||||
Contents = CliType.ObjectRef None
|
||||
Offset = Some (SIZEOF_OBJ * 2)
|
||||
Offset = None
|
||||
}
|
||||
{
|
||||
Name = "m_b"
|
||||
Contents = CliType.Numeric (CliNumericType.Int32 0)
|
||||
Offset = Some (SIZEOF_OBJ * 3)
|
||||
Offset = None
|
||||
}
|
||||
{
|
||||
Name = "m_e"
|
||||
Contents = CliType.ObjectRef None
|
||||
Offset = Some (SIZEOF_OBJ * 3 + SIZEOF_INT)
|
||||
Offset = None
|
||||
}
|
||||
// RuntimeFieldHandleInternal: https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs#L1048
|
||||
{
|
||||
Name = "m_fieldHandle"
|
||||
Contents = runtimeFieldHandleInternal
|
||||
Offset = Some (SIZEOF_OBJ * 4 + SIZEOF_INT)
|
||||
Offset = None
|
||||
}
|
||||
]
|
||||
|> CliValueType.OfFields
|
||||
|> CliValueType.OfFields Layout.Default // explicitly sequential but no custom packing size
|
||||
|
||||
let alloc, state = allocate runtimeFieldInfoStub allocState
|
||||
|
||||
|
@@ -1652,7 +1652,21 @@ module IlMachineState =
|
||||
match obj with
|
||||
| CliType.ValueType vt -> vt |> CliValueType.DereferenceField name
|
||||
| v -> failwith $"could not find field {name} on object {v}"
|
||||
| ManagedPointerSource.InterpretedAsType (src, ty) -> failwith "TODO"
|
||||
| ManagedPointerSource.InterpretedAsType (src, ty) ->
|
||||
let src = dereferencePointer state src
|
||||
|
||||
let concrete =
|
||||
match
|
||||
AllConcreteTypes.findExistingConcreteType
|
||||
state.ConcreteTypes
|
||||
(ty.Assembly, ty.Namespace, ty.Name, ty.Generics)
|
||||
with
|
||||
| Some ty -> ty
|
||||
| None -> failwith "not concretised type"
|
||||
|
||||
match concrete with
|
||||
| ConcreteUInt32 state.ConcreteTypes -> failwith "TODO: cast"
|
||||
| _ -> failwith "TODO"
|
||||
|
||||
let lookupTypeDefn
|
||||
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
||||
|
@@ -173,10 +173,10 @@ module Intrinsics =
|
||||
{
|
||||
Name = "m_type"
|
||||
Contents = CliType.ObjectRef arg
|
||||
Offset = Some 0
|
||||
Offset = None
|
||||
}
|
||||
|> List.singleton
|
||||
|> CliValueType.OfFields
|
||||
|> CliValueType.OfFields Layout.Default
|
||||
|
||||
IlMachineState.pushToEvalStack (CliType.ValueType vt) currentThread state
|
||||
|> IlMachineState.advanceProgramCounter currentThread
|
||||
|
@@ -58,7 +58,7 @@ module TypeHandleRegistry =
|
||||
Offset = None
|
||||
}
|
||||
]
|
||||
|> CliValueType.OfFields
|
||||
|> CliValueType.OfFields Layout.Default
|
||||
|
||||
let alloc, state = allocate fields allocState
|
||||
|
||||
|
@@ -316,7 +316,7 @@ module internal UnaryMetadataIlOp =
|
||||
state, field :: zeros
|
||||
)
|
||||
|
||||
let fields = List.rev fieldZeros |> CliValueType.OfFields
|
||||
let fields = List.rev fieldZeros |> CliValueType.OfFields ctorType.Layout
|
||||
|
||||
// Note: this is a bit unorthodox for value types, which *aren't* heap-allocated.
|
||||
// We'll perform their construction on the heap, though, to keep the interface
|
||||
@@ -498,7 +498,8 @@ module internal UnaryMetadataIlOp =
|
||||
if v.ConcreteType = targetConcreteType then
|
||||
actualObj
|
||||
else
|
||||
failwith $"TODO: is {v.ConcreteType} an instance of {targetType} ({targetConcreteType})"
|
||||
failwith
|
||||
$"TODO: is {AllConcreteTypes.lookup v.ConcreteType state.ConcreteTypes |> Option.get} an instance of {AllConcreteTypes.lookup targetConcreteType state.ConcreteTypes |> Option.get}"
|
||||
| false, _ ->
|
||||
|
||||
match state.ManagedHeap.Arrays.TryGetValue addr with
|
||||
@@ -1240,7 +1241,7 @@ module internal UnaryMetadataIlOp =
|
||||
Offset = None
|
||||
}
|
||||
|> List.singleton
|
||||
|> CliValueType.OfFields
|
||||
|> CliValueType.OfFields Layout.Default
|
||||
|
||||
IlMachineState.pushToEvalStack (CliType.ValueType vt) thread state
|
||||
|
||||
@@ -1299,7 +1300,7 @@ module internal UnaryMetadataIlOp =
|
||||
Offset = None
|
||||
}
|
||||
|> List.singleton
|
||||
|> CliValueType.OfFields
|
||||
|> CliValueType.OfFields Layout.Default
|
||||
|
||||
IlMachineState.pushToEvalStack (CliType.ValueType vt) thread state
|
||||
| MetadataToken.TypeReference h ->
|
||||
@@ -1336,7 +1337,7 @@ module internal UnaryMetadataIlOp =
|
||||
Offset = None
|
||||
}
|
||||
|> List.singleton
|
||||
|> CliValueType.OfFields
|
||||
|> CliValueType.OfFields Layout.Default
|
||||
|
||||
IlMachineState.pushToEvalStack (CliType.ValueType vt) thread state
|
||||
| MetadataToken.TypeDefinition h ->
|
||||
|
@@ -63,7 +63,7 @@ module internal UnaryStringTokenIlOp =
|
||||
Offset = None
|
||||
}
|
||||
]
|
||||
|> CliValueType.OfFields
|
||||
|> CliValueType.OfFields Layout.Default
|
||||
|
||||
let state, stringType =
|
||||
DumpedAssembly.typeInfoToTypeDefn' baseClassTypes state._LoadedAssemblies baseClassTypes.String
|
||||
|
Reference in New Issue
Block a user