33 Commits

Author SHA1 Message Date
Smaug123
55677a507b Revert 2025-08-24 20:43:06 +01:00
Smaug123
3930cc5fbb More 2025-08-24 20:40:52 +01:00
Smaug123
735449d4df Revert 2025-08-24 20:40:32 +01:00
Smaug123
6c73f14a4e Merge branch 'main' into generic-edge-cases 2025-08-24 20:39:27 +01:00
Smaug123
5bd0f779c5 Merge branch 'main' into generic-edge-cases 2025-08-24 20:27:48 +01:00
Smaug123
4e8c852b56 Merge branch 'main' into generic-edge-cases 2025-08-24 20:27:39 +01:00
Smaug123
1b1b00b4dd Merge branch 'main' into generic-edge-cases 2025-08-24 10:54:23 +01:00
Smaug123
6e98652691 Revert 2025-08-24 10:07:21 +01:00
Smaug123
ec5f6ed752 Merge branch 'main' into generic-edge-cases 2025-08-24 10:06:32 +01:00
Smaug123
99e46b3756 more 2025-08-24 09:08:52 +01:00
Smaug123
35128afab4 Merge commit '9afc7efea128e46f9cd1a00adcce8e7e50aa7418' into generic-edge-cases 2025-08-24 09:06:52 +01:00
Smaug123
57b0e545c9 Merge commit '2190f148e13ed6aab310c2e867f178c7a551ec1e' into generic-edge-cases 2025-08-23 22:53:34 +01:00
Smaug123
f1db433711 Merge commit 'e2e3d5c3bf7c07fcbf89c0c69d04273aa20b0d2f' into generic-edge-cases 2025-08-23 22:53:30 +01:00
Smaug123
64339bf4ee Merge commit '92f22cff42e73c533f0d9c28e37991046a8008d7' into generic-edge-cases 2025-08-23 22:52:32 +01:00
Smaug123
2e8e6f3919 Merge commit '3bdfeaf8a1cb802ce4dce2cd12d6abd639edc75e' into generic-edge-cases 2025-08-23 22:51:41 +01:00
Smaug123
02ae05893b Merge commit '174e415c70843d905a6d40790cbdfdcf0f21cbdb' into generic-edge-cases 2025-08-23 22:51:31 +01:00
Smaug123
4b7e2ac1e6 Merge commit 'cfd67166162b7c28a6e044e5904cac53da2a194d' into generic-edge-cases 2025-08-23 22:50:11 +01:00
Smaug123
027a49c51a Merge commit 'd711d6fff5c59378a601a6366e01354cd2a43d88' into generic-edge-cases 2025-08-23 22:48:56 +01:00
Smaug123
f020d560a6 WIP 2025-08-18 23:08:15 +01:00
Smaug123
2e8245d341 WIP 2025-08-15 14:11:48 +01:00
Smaug123
fca9a6dc47 WIP 2025-08-14 08:31:32 +01:00
Smaug123
cc14fb0edd Start initobj 2025-08-12 08:39:25 +01:00
Smaug123
1dbd4b008b resolveTypeFromDefnConcrete 2025-08-12 08:15:38 +01:00
Smaug123
064deee8d5 WIP 2025-08-11 22:12:37 +01:00
Smaug123
407c37a5fb WIP 2025-08-11 21:21:12 +01:00
Smaug123
59fd8a23b7 WIP 2025-08-11 08:18:13 +01:00
Smaug123
bdedea098a Merge branch 'main' into generic-edge-cases 2025-08-10 23:29:28 +01:00
Smaug123
6f48c89ef3 More 2025-08-10 23:22:04 +01:00
Smaug123
07c5e931e4 Feature parity 2025-08-10 23:03:23 +01:00
Smaug123
e0e954b131 Remove another spare test 2025-08-10 22:52:45 +01:00
Smaug123
95f422efa9 Revert example 2025-08-10 22:50:44 +01:00
Smaug123
e89fac2780 Fix a couple of TODOs 2025-08-10 22:50:26 +01:00
Smaug123
9bafd0f4b0 WIP 2025-08-02 20:57:53 +01:00
5 changed files with 281 additions and 59 deletions

View File

@@ -22,12 +22,17 @@ module TestPureCases =
NativeImpls = MockEnv.make ()
}
{
FileName = "InitializeArray.cs"
FileName = "OverlappingStructs.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
}
{
FileName = "GenericEdgeCases.cs"
FileName = "AdvancedStructLayout.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
}
{
FileName = "InitializeArray.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
}
@@ -66,11 +71,6 @@ module TestPureCases =
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
}
{
FileName = "UnsafeAs.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
}
]
let cases : EndToEndTestCase list =
@@ -80,6 +80,21 @@ module TestPureCases =
ExpectedReturnCode = 1
NativeImpls = MockEnv.make ()
}
{
FileName = "UnsafeAs.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
}
{
FileName = "Initobj.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
}
{
FileName = "GenericEdgeCases.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
}
{
FileName = "TestShl.cs"
ExpectedReturnCode = 0
@@ -165,11 +180,6 @@ module TestPureCases =
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
}
{
FileName = "Initobj.cs"
ExpectedReturnCode = 0
NativeImpls = MockEnv.make ()
}
]
let runTest (case : EndToEndTestCase) : unit =

View File

@@ -440,6 +440,54 @@ module IlMachineState =
resolveTypeFromDefn loggerFactory baseClassTypes sign typeGenericArgsAsDefn methodGenericArgsAsDefn assy state
/// Resolve a TypeDefinition using concrete type handles from execution context
let resolveTypeFromDefnConcrete
(loggerFactory : ILoggerFactory)
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
(ty : TypeDefinitionHandle)
(assy : DumpedAssembly)
(typeGenericArgs : ConcreteTypeHandle ImmutableArray)
(methodGenericArgs : ConcreteTypeHandle ImmutableArray)
(state : IlMachineState)
: IlMachineState * DumpedAssembly * WoofWare.PawPrint.TypeInfo<TypeDefn, TypeDefn>
=
let typeDef = assy.TypeDefs.[ty]
// Convert ConcreteTypeHandle to TypeDefn for the generics
let typeGenericArgsAsDefn =
typeGenericArgs
|> Seq.map (fun handle ->
Concretization.concreteHandleToTypeDefn
baseClassTypes
handle
state.ConcreteTypes
state._LoadedAssemblies
)
|> ImmutableArray.CreateRange
let methodGenericArgsAsDefn =
methodGenericArgs
|> Seq.map (fun handle ->
Concretization.concreteHandleToTypeDefn
baseClassTypes
handle
state.ConcreteTypes
state._LoadedAssemblies
)
|> ImmutableArray.CreateRange
// Map the type definition's generics using the provided type generic arguments
let resolvedTypeDef =
typeDef
|> TypeInfo.mapGeneric (fun (param, _) ->
if param.SequenceNumber < typeGenericArgsAsDefn.Length then
typeGenericArgsAsDefn.[param.SequenceNumber]
else
failwithf "Generic type parameter %d out of range" param.SequenceNumber
)
state, assy, resolvedTypeDef
/// Get zero value for a type that's already been concretized
let cliTypeZeroOfHandle
(state : IlMachineState)
@@ -1190,6 +1238,7 @@ module IlMachineState =
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
(currentThread : ThreadId)
(assy : DumpedAssembly)
(genericMethodTypeArgs : ImmutableArray<ConcreteTypeHandle>)
(m : MemberReferenceHandle)
(state : IlMachineState)
: IlMachineState *
@@ -1328,7 +1377,7 @@ module IlMachineState =
state
(state.ActiveAssembly(currentThread).Name)
concreteExtractedTypeArgs
ImmutableArray.Empty
genericMethodTypeArgs
ty
)
@@ -1345,7 +1394,7 @@ module IlMachineState =
state
assy.Name
concreteExtractedTypeArgs
ImmutableArray.Empty
genericMethodTypeArgs
ty
)

View File

@@ -14,6 +14,10 @@ module Intrinsics =
"System.Private.CoreLib", "ArgumentNullException", "ThrowIfNull"
// https://github.com/dotnet/runtime/blob/ec11903827fc28847d775ba17e0cd1ff56cfbc2e/src/coreclr/System.Private.CoreLib/src/System/Type.CoreCLR.cs#L82
"System.Private.CoreLib", "Type", "GetTypeFromHandle"
// https://github.com/dotnet/runtime/blob/108fa7856efcfd39bc991c2d849eabbf7ba5989c/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs#L161
"System.Private.CoreLib", "ReadOnlySpan`1", "get_Length"
// https://github.com/dotnet/runtime/blob/9e5e6aa7bc36aeb2a154709a9d1192030c30a2ef/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs#L153
"System.Private.CoreLib", "RuntimeHelpers", "CreateSpan"
]
|> Set.ofList
@@ -299,15 +303,47 @@ module Intrinsics =
| [], ConcreteBool state.ConcreteTypes -> ()
| _ -> failwith "bad signature for System.Private.CoreLib.RuntimeHelpers.IsReferenceOrContainsReference"
let generic =
AllConcreteTypes.lookup (Seq.exactlyOne methodToCall.Generics) state.ConcreteTypes
let arg = Seq.exactlyOne methodToCall.Generics
let generic =
match generic with
| None -> failwith "somehow have not already concretised type in IsReferenceOrContainsReferences"
| Some generic -> generic
let result =
// Some types appear circular, because they're hardcoded in the runtime. We have to special-case them.
match arg with
| ConcreteChar state.ConcreteTypes -> false
| _ ->
failwith $"TODO: do the thing on %O{generic}"
let generic = AllConcreteTypes.lookup arg state.ConcreteTypes
let generic =
match generic with
| None -> failwith "somehow have not already concretised type in IsReferenceOrContainsReferences"
| Some generic -> generic
let td =
state.LoadedAssembly (generic.Assembly)
|> Option.get
|> fun a -> a.TypeDefs.[generic.Definition.Get]
let baseType =
td.BaseType
|> DumpedAssembly.resolveBaseType baseClassTypes state._LoadedAssemblies generic.Assembly
match baseType with
| ResolvedBaseType.Enum -> false
| ResolvedBaseType.ValueType ->
let nonStaticFields =
td.Fields
|> List.choose (fun field -> if field.IsStatic then None else Some field.Signature)
failwith $"TODO: search the fields on {td.Namespace}.{td.Name}: {nonStaticFields}"
| ResolvedBaseType.Object
| ResolvedBaseType.Delegate -> true
let state =
state
|> IlMachineState.pushToEvalStack (CliType.ofBool result) currentThread
|> IlMachineState.advanceProgramCounter currentThread
Some state
| "System.Private.CoreLib", "RuntimeHelpers", "InitializeArray" ->
// https://github.com/dotnet/runtime/blob/9e5e6aa7bc36aeb2a154709a9d1192030c30a2ef/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs#L18
match methodToCall.Signature.ParameterTypes, methodToCall.Signature.ReturnType with
@@ -319,6 +355,57 @@ module Intrinsics =
failwith "TODO: if arg1 contains null handle, throw ArgumentException"
failwith "TODO: array initialization"
| "System.Private.CoreLib", "Unsafe", "As" ->
// https://github.com/dotnet/runtime/blob/721fdf6dcb032da1f883d30884e222e35e3d3c99/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs#L64
let inputType, retType =
match methodToCall.Signature.ParameterTypes, methodToCall.Signature.ReturnType with
| [ input ], ret -> input, ret
| _ -> failwith "bad signature Unsafe.As"
let from, to_ =
match Seq.toList methodToCall.Generics with
| [ from ; to_ ] -> from, to_
| _ -> failwith "bad generics"
if ConcreteTypeHandle.Byref to_ <> retType then
failwith "bad return type"
if ConcreteTypeHandle.Byref from <> inputType then
failwith "bad input type"
let from =
match AllConcreteTypes.lookup from state.ConcreteTypes with
| None -> failwith "somehow have not concretised input type"
| Some t -> t
let to_ =
match AllConcreteTypes.lookup to_ state.ConcreteTypes with
| None -> failwith "somehow have not concretised ret type"
| Some t -> t
failwith "TODO: transmute fields etc"
let state = state |> IlMachineState.advanceProgramCounter currentThread
Some state
| "System.Private.CoreLib", "Unsafe", "SizeOf" ->
// https://github.com/dotnet/runtime/blob/721fdf6dcb032da1f883d30884e222e35e3d3c99/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs#L51
match methodToCall.Signature.ParameterTypes, methodToCall.Signature.ReturnType with
| [], ConcreteInt32 state.ConcreteTypes -> ()
| _ -> failwith "bad signature Unsafe.SizeOf"
let ty =
match Seq.toList methodToCall.Generics with
| [ ty ] -> ty
| _ -> failwith "bad generics"
let zero, state = IlMachineState.cliTypeZeroOfHandle state baseClassTypes ty
let size = CliType.sizeOf zero
state
|> IlMachineState.pushToEvalStack (CliType.Numeric (CliNumericType.Int32 size)) currentThread
|> IlMachineState.advanceProgramCounter currentThread
|> Some
| "System.Private.CoreLib", "RuntimeHelpers", "CreateSpan" ->
// https://github.com/dotnet/runtime/blob/9e5e6aa7bc36aeb2a154709a9d1192030c30a2ef/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs#L153
None

View File

@@ -52,7 +52,7 @@ module NullaryIlOp =
| EvalStackValue.NativeInt nativeIntSource ->
failwith $"TODO: Native int pointer dereferencing not implemented for {targetType}"
| EvalStackValue.ObjectRef managedHeapAddress ->
failwith "TODO: Object reference dereferencing not implemented"
IlMachineState.dereferencePointer state (ManagedPointerSource.Heap managedHeapAddress)
| other -> failwith $"Unexpected eval stack value for Ldind operation: {other}"
let loadedValue = loadedValue |> EvalStackValue.ofCliType

View File

@@ -28,6 +28,23 @@ module internal UnaryMetadataIlOp =
| MetadataToken.MethodSpecification h ->
let spec = activeAssy.MethodSpecs.[h]
let state, methodGenerics =
((state, []), spec.Signature)
||> Seq.fold (fun (state, acc) typeDefn ->
let state, concreteType =
IlMachineState.concretizeType
baseClassTypes
state
(state.ActiveAssembly thread).Name
currentMethod.DeclaringType.Generics
currentMethod.Generics
typeDefn
state, concreteType :: acc
)
let methodGenerics = List.rev methodGenerics |> ImmutableArray.CreateRange
match spec.Method with
| MetadataToken.MethodDef token ->
let method =
@@ -44,6 +61,7 @@ module internal UnaryMetadataIlOp =
baseClassTypes
thread
(state.ActiveAssembly thread)
methodGenerics
ref
state
@@ -58,6 +76,7 @@ module internal UnaryMetadataIlOp =
baseClassTypes
thread
(state.ActiveAssembly thread)
currentMethod.DeclaringType.Generics
h
state
@@ -114,6 +133,23 @@ module internal UnaryMetadataIlOp =
| MetadataToken.MethodSpecification h ->
let spec = activeAssy.MethodSpecs.[h]
let state, methodGenerics =
((state, []), spec.Signature)
||> Seq.fold (fun (state, acc) typeDefn ->
let state, concreteType =
IlMachineState.concretizeType
baseClassTypes
state
(state.ActiveAssembly thread).Name
currentMethod.DeclaringType.Generics
ImmutableArray.Empty
typeDefn
state, concreteType :: acc
)
let methodGenerics = List.rev methodGenerics |> ImmutableArray.CreateRange
match spec.Method with
| MetadataToken.MethodDef token ->
let method =
@@ -128,6 +164,7 @@ module internal UnaryMetadataIlOp =
baseClassTypes
thread
(state.ActiveAssembly thread)
methodGenerics
ref
state
@@ -142,6 +179,7 @@ module internal UnaryMetadataIlOp =
baseClassTypes
thread
(state.ActiveAssembly thread)
ImmutableArray.Empty
h
state
@@ -203,6 +241,7 @@ module internal UnaryMetadataIlOp =
baseClassTypes
thread
(state.ActiveAssembly thread)
ImmutableArray.Empty
mr
state
@@ -481,7 +520,14 @@ module internal UnaryMetadataIlOp =
state, field
| MetadataToken.MemberReference mr ->
let state, _, field, _ =
IlMachineState.resolveMember loggerFactory baseClassTypes thread activeAssy mr state
IlMachineState.resolveMember
loggerFactory
baseClassTypes
thread
activeAssy
ImmutableArray.Empty
mr
state
match field with
| Choice1Of2 _method -> failwith "member reference was unexpectedly a method"
@@ -498,6 +544,7 @@ module internal UnaryMetadataIlOp =
)
let valueToStore, state = IlMachineState.popEvalStack thread state
let currentObj, state = IlMachineState.popEvalStack thread state
let state, declaringTypeHandle, typeGenerics =
IlMachineState.concretizeFieldForExecution loggerFactory baseClassTypes thread field state
@@ -514,8 +561,6 @@ module internal UnaryMetadataIlOp =
let valueToStore = EvalStackValue.toCliTypeCoerced zero valueToStore
let currentObj, state = IlMachineState.popEvalStack thread state
if field.Attributes.HasFlag FieldAttributes.Static then
let state =
IlMachineState.setStatic declaringTypeHandle field.Name valueToStore state
@@ -601,6 +646,7 @@ module internal UnaryMetadataIlOp =
baseClassTypes
thread
(state.ActiveAssembly thread)
ImmutableArray.Empty
mr
state
@@ -660,7 +706,14 @@ module internal UnaryMetadataIlOp =
state, field
| MetadataToken.MemberReference mr ->
let state, assyName, field, _ =
IlMachineState.resolveMember loggerFactory baseClassTypes thread activeAssy mr state
IlMachineState.resolveMember
loggerFactory
baseClassTypes
thread
activeAssy
ImmutableArray.Empty
mr
state
match field with
| Choice1Of2 _method -> failwith "member reference was unexpectedly a method"
@@ -750,6 +803,7 @@ module internal UnaryMetadataIlOp =
IlMachineState.pushToEvalStack currentValue thread state
| EvalStackValue.UserDefinedValueType vt ->
let result = vt |> EvalStackValueUserType.DereferenceField field.Name
IlMachineState.pushToEvalStack' result thread state
state
@@ -779,7 +833,15 @@ module internal UnaryMetadataIlOp =
state, field
| MetadataToken.MemberReference mr ->
let state, assyName, field, _ =
IlMachineState.resolveMember loggerFactory baseClassTypes thread activeAssy mr state
// TODO: generics
IlMachineState.resolveMember
loggerFactory
baseClassTypes
thread
activeAssy
ImmutableArray.Empty
mr
state
match field with
| Choice1Of2 _method -> failwith "member reference was unexpectedly a method"
@@ -793,6 +855,7 @@ module internal UnaryMetadataIlOp =
|> IlMachineState.pushToEvalStack' result thread
|> IlMachineState.advanceProgramCounter thread
|> Tuple.withRight WhatWeDid.Executed
| Ldsfld ->
let state, field =
match metadataToken with
@@ -807,7 +870,14 @@ module internal UnaryMetadataIlOp =
state, field
| MetadataToken.MemberReference mr ->
let state, _, field, _ =
IlMachineState.resolveMember loggerFactory baseClassTypes thread activeAssy mr state
IlMachineState.resolveMember
loggerFactory
baseClassTypes
thread
activeAssy
ImmutableArray.Empty
mr
state
match field with
| Choice1Of2 _method -> failwith "member reference was unexpectedly a method"
@@ -983,6 +1053,7 @@ module internal UnaryMetadataIlOp =
IlMachineState.pushToEvalStack toPush thread state
|> IlMachineState.advanceProgramCounter thread
|> Tuple.withRight WhatWeDid.Executed
| Initobj ->
let popped, state = IlMachineState.popEvalStack thread state
let declaringTypeGenerics = currentMethod.DeclaringType.Generics
@@ -1046,6 +1117,7 @@ module internal UnaryMetadataIlOp =
state
|> IlMachineState.advanceProgramCounter thread
|> Tuple.withRight WhatWeDid.Executed
| Ldsflda ->
// TODO: check whether we should throw FieldAccessException
@@ -1142,6 +1214,40 @@ module internal UnaryMetadataIlOp =
| Stobj -> failwith "TODO: Stobj unimplemented"
| Constrained -> failwith "TODO: Constrained unimplemented"
| Ldtoken ->
// Helper function to handle type tokens and create RuntimeTypeHandle
let handleTypeToken (typeDefn : TypeDefn) (state : IlMachineState) : IlMachineState =
let ty = baseClassTypes.RuntimeTypeHandle
let field = ty.Fields |> List.exactlyOne
if field.Name <> "m_type" then
failwith $"unexpected field name ${field.Name} for BCL type RuntimeTypeHandle"
let methodGenerics = currentMethod.Generics
let typeGenerics = currentMethod.DeclaringType.Generics
let state, handle =
IlMachineState.concretizeType
baseClassTypes
state
activeAssy.Name
typeGenerics
methodGenerics
typeDefn
let alloc, state = IlMachineState.getOrAllocateType baseClassTypes handle state
let vt =
// https://github.com/dotnet/runtime/blob/2b21c73fa2c32fa0195e4a411a435dda185efd08/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs#L92
{
Name = "m_type"
Contents = CliType.ObjectRef (Some alloc)
Offset = None
}
|> List.singleton
|> CliValueType.OfFields
IlMachineState.pushToEvalStack (CliType.ValueType vt) thread state
let state =
match metadataToken with
| MetadataToken.FieldDefinition h ->
@@ -1234,40 +1340,10 @@ module internal UnaryMetadataIlOp =
IlMachineState.pushToEvalStack (CliType.ValueType vt) thread state
| MetadataToken.TypeDefinition h ->
let ty = baseClassTypes.RuntimeTypeHandle
let field = ty.Fields |> List.exactlyOne
if field.Name <> "m_type" then
failwith $"unexpected field name ${field.Name} for BCL type RuntimeTypeHandle"
let methodGenerics = currentMethod.Generics
let typeGenerics = currentMethod.DeclaringType.Generics
let state, typeDefn =
IlMachineState.lookupTypeDefn baseClassTypes state activeAssy h
let state, handle =
IlMachineState.concretizeType
baseClassTypes
state
activeAssy.Name
typeGenerics
methodGenerics
typeDefn
let alloc, state = IlMachineState.getOrAllocateType baseClassTypes handle state
let vt =
{
Name = "m_type"
Contents = CliType.ObjectRef (Some alloc)
Offset = None
}
|> List.singleton
|> CliValueType.OfFields
IlMachineState.pushToEvalStack (CliType.ValueType vt) thread state
handleTypeToken typeDefn state
| _ -> failwith $"Unexpected metadata token %O{metadataToken} in LdToken"
state