mirror of
https://github.com/Smaug123/WoofWare.PawPrint
synced 2025-10-17 19:38:41 +00:00
Compare commits
20 Commits
9afc7efea1
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
08a4497ebf | ||
|
fc62651d55 | ||
|
2e9fdbed48 | ||
|
fb5c4a6313 | ||
|
95987e592c | ||
|
0e31d74586 | ||
|
8112b122fb | ||
|
5173805562 | ||
|
cb5d76f059 | ||
|
5e7bd969ba | ||
|
07fabfff65 | ||
|
655ba4400a | ||
|
c58c8ce678 | ||
|
239ae0f0cd | ||
|
4de0dbd816 | ||
|
91aff34d1e | ||
|
f9e186ba8f | ||
|
622d0782ae | ||
|
3e4b0a7b7e | ||
|
5f35c7a7cd |
@@ -67,13 +67,12 @@ dotnet run --project WoofWare.PawPrint.App/WoofWare.PawPrint.App.fsproj -- CShar
|
|||||||
- `Corelib.fs`: Core library type definitions (String, Array, etc.)
|
- `Corelib.fs`: Core library type definitions (String, Array, etc.)
|
||||||
|
|
||||||
**WoofWare.PawPrint.Test**
|
**WoofWare.PawPrint.Test**
|
||||||
- Uses Expecto as the test framework
|
- Uses NUnit as the test framework
|
||||||
- Test cases are defined in `TestPureCases.fs` and `TestImpureCases.fs`
|
- Test cases are defined in `TestPureCases.fs` and `TestImpureCases.fs`
|
||||||
- C# source files in `sources{Pure,Impure}/` are compiled and executed by the runtime as test cases
|
- C# source files in `sources{Pure,Impure}/` are compiled and executed by the runtime as test cases; files in `sourcesPure` are automatically turned into test cases with no further action (see TestPureCases.fs for the mechanism)
|
||||||
- `TestHarness.fs` provides infrastructure for running test assemblies through the interpreter
|
- `TestHarness.fs` provides infrastructure for running test assemblies through the interpreter
|
||||||
- Run all tests with `dotnet run --project WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj -- --no-spinner` (note the additional `--`)
|
- Run all tests with `dotnet run --project WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj -- --no-spinner` (note the additional `--`)
|
||||||
- Run a specific test with `dotnet run --project WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj -- --filter-test-case StringWithinTestName --no-spinner`
|
- Run a specific test with `dotnet run --project WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj -- --filter-test-case StringWithinTestName --no-spinner`
|
||||||
- Pending test definitions must be moved into the non-pending test case list before they can be run.
|
|
||||||
|
|
||||||
**WoofWare.PawPrint.App**
|
**WoofWare.PawPrint.App**
|
||||||
- Entry point application for running the interpreter
|
- Entry point application for running the interpreter
|
||||||
|
@@ -423,8 +423,8 @@ module Assembly =
|
|||||||
let rec resolveTypeRef
|
let rec resolveTypeRef
|
||||||
(assemblies : ImmutableDictionary<string, DumpedAssembly>)
|
(assemblies : ImmutableDictionary<string, DumpedAssembly>)
|
||||||
(referencedInAssembly : DumpedAssembly)
|
(referencedInAssembly : DumpedAssembly)
|
||||||
(target : TypeRef)
|
|
||||||
(genericArgs : ImmutableArray<TypeDefn>)
|
(genericArgs : ImmutableArray<TypeDefn>)
|
||||||
|
(target : TypeRef)
|
||||||
: TypeResolutionResult
|
: TypeResolutionResult
|
||||||
=
|
=
|
||||||
match target.ResolutionScope with
|
match target.ResolutionScope with
|
||||||
@@ -495,7 +495,7 @@ module Assembly =
|
|||||||
| None ->
|
| None ->
|
||||||
|
|
||||||
match assy.TypeRef ns name with
|
match assy.TypeRef ns name with
|
||||||
| Some typeRef -> resolveTypeRef assemblies assy typeRef genericArgs
|
| Some typeRef -> resolveTypeRef assemblies assy genericArgs typeRef
|
||||||
| None ->
|
| None ->
|
||||||
|
|
||||||
match assy.ExportedType (Some ns) name with
|
match assy.ExportedType (Some ns) name with
|
||||||
@@ -532,7 +532,7 @@ module DumpedAssembly =
|
|||||||
| Some (BaseTypeInfo.TypeRef r) ->
|
| Some (BaseTypeInfo.TypeRef r) ->
|
||||||
let assy = loadedAssemblies.[source.FullName]
|
let assy = loadedAssemblies.[source.FullName]
|
||||||
// TODO: generics
|
// TODO: generics
|
||||||
match Assembly.resolveTypeRef loadedAssemblies assy assy.TypeRefs.[r] ImmutableArray.Empty with
|
match Assembly.resolveTypeRef loadedAssemblies assy ImmutableArray.Empty assy.TypeRefs.[r] with
|
||||||
| TypeResolutionResult.FirstLoadAssy _ ->
|
| TypeResolutionResult.FirstLoadAssy _ ->
|
||||||
failwith
|
failwith
|
||||||
"seems pretty unlikely that we could have constructed this object without loading its base type"
|
"seems pretty unlikely that we could have constructed this object without loading its base type"
|
||||||
@@ -560,3 +560,36 @@ module DumpedAssembly =
|
|||||||
| None -> ResolvedBaseType.Object
|
| None -> ResolvedBaseType.Object
|
||||||
|
|
||||||
go source baseTypeInfo
|
go source baseTypeInfo
|
||||||
|
|
||||||
|
let typeInfoToTypeDefn
|
||||||
|
(bct : BaseClassTypes<DumpedAssembly>)
|
||||||
|
(assemblies : ImmutableDictionary<string, DumpedAssembly>)
|
||||||
|
(ti : TypeInfo<TypeDefn, TypeDefn>)
|
||||||
|
: TypeDefn
|
||||||
|
=
|
||||||
|
ti
|
||||||
|
|> TypeInfo.toTypeDefn
|
||||||
|
bct
|
||||||
|
(fun n -> assemblies.[n.FullName])
|
||||||
|
_.Name
|
||||||
|
(fun x y -> x.TypeDefs.[y])
|
||||||
|
(fun x y ->
|
||||||
|
let r = x.TypeRefs.[y] |> Assembly.resolveTypeRef assemblies x ImmutableArray.Empty
|
||||||
|
|
||||||
|
match r with
|
||||||
|
| TypeResolutionResult.FirstLoadAssy assemblyReference -> failwith "todo"
|
||||||
|
| TypeResolutionResult.Resolved (dumpedAssembly, typeInfo) ->
|
||||||
|
let result =
|
||||||
|
typeInfo |> TypeInfo.mapGeneric (fun typeDef -> failwith "TODO: generics")
|
||||||
|
|
||||||
|
dumpedAssembly, result
|
||||||
|
)
|
||||||
|
|
||||||
|
let typeInfoToTypeDefn'
|
||||||
|
(bct : BaseClassTypes<DumpedAssembly>)
|
||||||
|
(assemblies : ImmutableDictionary<string, DumpedAssembly>)
|
||||||
|
(ti : TypeInfo<GenericParamFromMetadata, TypeDefn>)
|
||||||
|
=
|
||||||
|
ti
|
||||||
|
|> TypeInfo.mapGeneric (fun (par, _) -> TypeDefn.GenericTypeParameter par.SequenceNumber)
|
||||||
|
|> typeInfoToTypeDefn bct assemblies
|
||||||
|
@@ -33,6 +33,20 @@ type ConcreteType<'typeGeneric> =
|
|||||||
_Generics : ImmutableArray<'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.Assembly : AssemblyName = this._AssemblyName
|
||||||
member this.Definition : ComparableTypeDefinitionHandle = this._Definition
|
member this.Definition : ComparableTypeDefinitionHandle = this._Definition
|
||||||
member this.Generics : ImmutableArray<'typeGeneric> = this._Generics
|
member this.Generics : ImmutableArray<'typeGeneric> = this._Generics
|
||||||
|
16
WoofWare.PawPrint.Domain/Constants.fs
Normal file
16
WoofWare.PawPrint.Domain/Constants.fs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
namespace WoofWare.PawPrint
|
||||||
|
|
||||||
|
[<AutoOpen>]
|
||||||
|
module Constants =
|
||||||
|
|
||||||
|
[<Literal>]
|
||||||
|
let SIZEOF_INT = 4
|
||||||
|
|
||||||
|
[<Literal>]
|
||||||
|
let SIZEOF_OBJ = 8
|
||||||
|
|
||||||
|
[<Literal>]
|
||||||
|
let DEFAULT_STRUCT_ALIGNMENT = 8
|
||||||
|
|
||||||
|
[<Literal>]
|
||||||
|
let NATIVE_INT_SIZE = 8
|
@@ -32,9 +32,14 @@ type FieldInfo<'typeGeneric, 'fieldGeneric> =
|
|||||||
/// literal, and other characteristics.
|
/// literal, and other characteristics.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Attributes : FieldAttributes
|
Attributes : FieldAttributes
|
||||||
|
|
||||||
|
/// Static fields don't have an offset at all; also, instance fields which don't have an explicit offset (but
|
||||||
|
/// which of course do have one implicitly, which is most fields) are None here.
|
||||||
|
Offset : int option
|
||||||
}
|
}
|
||||||
|
|
||||||
member this.HasFieldRVA = this.Attributes.HasFlag FieldAttributes.HasFieldRVA
|
member this.HasFieldRVA = this.Attributes.HasFlag FieldAttributes.HasFieldRVA
|
||||||
|
member this.IsStatic = this.Attributes.HasFlag FieldAttributes.Static
|
||||||
|
|
||||||
override this.ToString () : string =
|
override this.ToString () : string =
|
||||||
$"%s{this.DeclaringType.Assembly.Name}.{this.DeclaringType.Name}.%s{this.Name}"
|
$"%s{this.DeclaringType.Assembly.Name}.{this.DeclaringType.Name}.%s{this.Name}"
|
||||||
@@ -52,23 +57,28 @@ module FieldInfo =
|
|||||||
let fieldSig = def.DecodeSignature (TypeDefn.typeProvider assembly, ())
|
let fieldSig = def.DecodeSignature (TypeDefn.typeProvider assembly, ())
|
||||||
let declaringType = def.GetDeclaringType ()
|
let declaringType = def.GetDeclaringType ()
|
||||||
|
|
||||||
let typeGenerics =
|
|
||||||
mr.GetTypeDefinition(declaringType).GetGenericParameters ()
|
|
||||||
|> GenericParameter.readAll mr
|
|
||||||
|
|
||||||
let decType = mr.GetTypeDefinition declaringType
|
let decType = mr.GetTypeDefinition declaringType
|
||||||
|
|
||||||
|
let typeGenerics = decType.GetGenericParameters () |> GenericParameter.readAll mr
|
||||||
|
|
||||||
let declaringTypeNamespace = mr.GetString decType.Namespace
|
let declaringTypeNamespace = mr.GetString decType.Namespace
|
||||||
let declaringTypeName = mr.GetString decType.Name
|
let declaringTypeName = mr.GetString decType.Name
|
||||||
|
|
||||||
let declaringType =
|
let declaringType =
|
||||||
ConcreteType.make assembly declaringType declaringTypeNamespace declaringTypeName typeGenerics
|
ConcreteType.make assembly declaringType declaringTypeNamespace declaringTypeName typeGenerics
|
||||||
|
|
||||||
|
let offset =
|
||||||
|
match def.GetOffset () with
|
||||||
|
| -1 -> None
|
||||||
|
| s -> Some s
|
||||||
|
|
||||||
{
|
{
|
||||||
Name = name
|
Name = name
|
||||||
Signature = fieldSig
|
Signature = fieldSig
|
||||||
DeclaringType = declaringType
|
DeclaringType = declaringType
|
||||||
Handle = handle
|
Handle = handle
|
||||||
Attributes = def.Attributes
|
Attributes = def.Attributes
|
||||||
|
Offset = offset
|
||||||
}
|
}
|
||||||
|
|
||||||
let mapTypeGenerics<'a, 'b, 'field> (f : int -> 'a -> 'b) (input : FieldInfo<'a, 'field>) : FieldInfo<'b, 'field> =
|
let mapTypeGenerics<'a, 'b, 'field> (f : int -> 'a -> 'b) (input : FieldInfo<'a, 'field>) : FieldInfo<'b, 'field> =
|
||||||
@@ -80,5 +90,5 @@ module FieldInfo =
|
|||||||
DeclaringType = declaringType
|
DeclaringType = declaringType
|
||||||
Signature = input.Signature
|
Signature = input.Signature
|
||||||
Attributes = input.Attributes
|
Attributes = input.Attributes
|
||||||
|
Offset = input.Offset
|
||||||
}
|
}
|
||||||
|
@@ -138,6 +138,23 @@ module ConcreteActivePatterns =
|
|||||||
| None -> None
|
| None -> None
|
||||||
| _ -> None
|
| _ -> None
|
||||||
|
|
||||||
|
let (|ConcreteChar|_|) (concreteTypes : AllConcreteTypes) (handle : ConcreteTypeHandle) : unit option =
|
||||||
|
match handle with
|
||||||
|
| ConcreteTypeHandle.Concrete id ->
|
||||||
|
match concreteTypes.Mapping |> Map.tryFind id with
|
||||||
|
| Some ct ->
|
||||||
|
if
|
||||||
|
ct.Assembly.Name = "System.Private.CoreLib"
|
||||||
|
&& ct.Namespace = "System"
|
||||||
|
&& ct.Name = "Char"
|
||||||
|
&& ct.Generics.IsEmpty
|
||||||
|
then
|
||||||
|
Some ()
|
||||||
|
else
|
||||||
|
None
|
||||||
|
| None -> None
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
let (|ConcreteRuntimeFieldHandle|_|) (concreteTypes : AllConcreteTypes) (handle : ConcreteTypeHandle) =
|
let (|ConcreteRuntimeFieldHandle|_|) (concreteTypes : AllConcreteTypes) (handle : ConcreteTypeHandle) =
|
||||||
match handle with
|
match handle with
|
||||||
| ConcreteTypeHandle.Concrete id ->
|
| ConcreteTypeHandle.Concrete id ->
|
||||||
@@ -166,6 +183,24 @@ module ConcreteActivePatterns =
|
|||||||
| _ -> None
|
| _ -> None
|
||||||
| _ -> None
|
| _ -> None
|
||||||
|
|
||||||
|
let (|ConcreteGenericArray|_|)
|
||||||
|
(concreteTypes : AllConcreteTypes)
|
||||||
|
(eltType : ConcreteTypeHandle)
|
||||||
|
(handle : ConcreteTypeHandle)
|
||||||
|
=
|
||||||
|
match handle with
|
||||||
|
| ConcreteTypeHandle.Concrete id ->
|
||||||
|
match concreteTypes.Mapping |> Map.tryFind id with
|
||||||
|
| Some ct when
|
||||||
|
ct.Assembly.Name = "System.Private.CoreLib"
|
||||||
|
&& ct.Namespace = "System"
|
||||||
|
&& ct.Name = "Array"
|
||||||
|
&& Seq.tryExactlyOne ct.Generics = Some eltType
|
||||||
|
->
|
||||||
|
Some ()
|
||||||
|
| _ -> None
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
let (|ConcreteObj|_|) (concreteTypes : AllConcreteTypes) (handle : ConcreteTypeHandle) : unit option =
|
let (|ConcreteObj|_|) (concreteTypes : AllConcreteTypes) (handle : ConcreteTypeHandle) : unit option =
|
||||||
match handle with
|
match handle with
|
||||||
| ConcreteTypeHandle.Concrete id ->
|
| ConcreteTypeHandle.Concrete id ->
|
||||||
@@ -285,6 +320,40 @@ module ConcreteActivePatterns =
|
|||||||
| None -> None
|
| None -> None
|
||||||
| _ -> None
|
| _ -> None
|
||||||
|
|
||||||
|
let (|ConcreteUInt32|_|) (concreteTypes : AllConcreteTypes) (handle : ConcreteTypeHandle) : unit option =
|
||||||
|
match handle with
|
||||||
|
| ConcreteTypeHandle.Concrete id ->
|
||||||
|
match concreteTypes.Mapping |> Map.tryFind id with
|
||||||
|
| Some ct ->
|
||||||
|
if
|
||||||
|
ct.Assembly.Name = "System.Private.CoreLib"
|
||||||
|
&& ct.Namespace = "System"
|
||||||
|
&& ct.Name = "UInt32"
|
||||||
|
&& ct.Generics.IsEmpty
|
||||||
|
then
|
||||||
|
Some ()
|
||||||
|
else
|
||||||
|
None
|
||||||
|
| None -> None
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
|
let (|ConcreteUInt64|_|) (concreteTypes : AllConcreteTypes) (handle : ConcreteTypeHandle) : unit option =
|
||||||
|
match handle with
|
||||||
|
| ConcreteTypeHandle.Concrete id ->
|
||||||
|
match concreteTypes.Mapping |> Map.tryFind id with
|
||||||
|
| Some ct ->
|
||||||
|
if
|
||||||
|
ct.Assembly.Name = "System.Private.CoreLib"
|
||||||
|
&& ct.Namespace = "System"
|
||||||
|
&& ct.Name = "UInt64"
|
||||||
|
&& ct.Generics.IsEmpty
|
||||||
|
then
|
||||||
|
Some ()
|
||||||
|
else
|
||||||
|
None
|
||||||
|
| None -> None
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
let (|ConcreteSingle|_|) (concreteTypes : AllConcreteTypes) (handle : ConcreteTypeHandle) : unit option =
|
let (|ConcreteSingle|_|) (concreteTypes : AllConcreteTypes) (handle : ConcreteTypeHandle) : unit option =
|
||||||
match handle with
|
match handle with
|
||||||
| ConcreteTypeHandle.Concrete id ->
|
| ConcreteTypeHandle.Concrete id ->
|
||||||
@@ -314,6 +383,13 @@ module ConcreteActivePatterns =
|
|||||||
| ConcreteTypeHandle.Pointer inner -> Some inner
|
| ConcreteTypeHandle.Pointer inner -> Some inner
|
||||||
| _ -> None
|
| _ -> None
|
||||||
|
|
||||||
|
type IAssemblyLoad =
|
||||||
|
abstract LoadAssembly :
|
||||||
|
loadedAssemblies : ImmutableDictionary<string, DumpedAssembly> ->
|
||||||
|
referencedIn : AssemblyName ->
|
||||||
|
handle : AssemblyReferenceHandle ->
|
||||||
|
ImmutableDictionary<string, DumpedAssembly> * DumpedAssembly
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module TypeConcretization =
|
module TypeConcretization =
|
||||||
|
|
||||||
@@ -389,8 +465,7 @@ module TypeConcretization =
|
|||||||
|
|
||||||
// Helper function for assembly loading with retry pattern
|
// Helper function for assembly loading with retry pattern
|
||||||
let private loadAssemblyAndResolveTypeRef
|
let private loadAssemblyAndResolveTypeRef
|
||||||
(loadAssembly :
|
(loadAssembly : IAssemblyLoad)
|
||||||
AssemblyName -> AssemblyReferenceHandle -> ImmutableDictionary<string, DumpedAssembly> * DumpedAssembly)
|
|
||||||
(ctx : ConcretizationContext<'corelib>)
|
(ctx : ConcretizationContext<'corelib>)
|
||||||
(currentAssembly : AssemblyName)
|
(currentAssembly : AssemblyName)
|
||||||
(typeRef : TypeRef)
|
(typeRef : TypeRef)
|
||||||
@@ -403,7 +478,7 @@ module TypeConcretization =
|
|||||||
|
|
||||||
// First try to resolve without loading new assemblies
|
// First try to resolve without loading new assemblies
|
||||||
let resolutionResult =
|
let resolutionResult =
|
||||||
Assembly.resolveTypeRef ctx.LoadedAssemblies currentAssy typeRef ImmutableArray.Empty
|
Assembly.resolveTypeRef ctx.LoadedAssemblies currentAssy ImmutableArray.Empty typeRef
|
||||||
|
|
||||||
match resolutionResult with
|
match resolutionResult with
|
||||||
| TypeResolutionResult.Resolved (targetAssy, typeInfo) -> (targetAssy, typeInfo), ctx
|
| TypeResolutionResult.Resolved (targetAssy, typeInfo) -> (targetAssy, typeInfo), ctx
|
||||||
@@ -411,7 +486,8 @@ module TypeConcretization =
|
|||||||
// Need to load the assembly
|
// Need to load the assembly
|
||||||
match typeRef.ResolutionScope with
|
match typeRef.ResolutionScope with
|
||||||
| TypeRefResolutionScope.Assembly assyRef ->
|
| TypeRefResolutionScope.Assembly assyRef ->
|
||||||
let newAssemblies, _ = loadAssembly currentAssembly assyRef
|
let newAssemblies, _ =
|
||||||
|
loadAssembly.LoadAssembly ctx.LoadedAssemblies currentAssembly assyRef
|
||||||
|
|
||||||
let newCtx =
|
let newCtx =
|
||||||
{ ctx with
|
{ ctx with
|
||||||
@@ -420,7 +496,7 @@ module TypeConcretization =
|
|||||||
|
|
||||||
// Now try to resolve again with the loaded assembly
|
// Now try to resolve again with the loaded assembly
|
||||||
let resolutionResult2 =
|
let resolutionResult2 =
|
||||||
Assembly.resolveTypeRef newCtx.LoadedAssemblies currentAssy typeRef ImmutableArray.Empty
|
Assembly.resolveTypeRef newCtx.LoadedAssemblies currentAssy ImmutableArray.Empty typeRef
|
||||||
|
|
||||||
match resolutionResult2 with
|
match resolutionResult2 with
|
||||||
| TypeResolutionResult.Resolved (targetAssy, typeInfo) -> (targetAssy, typeInfo), newCtx
|
| TypeResolutionResult.Resolved (targetAssy, typeInfo) -> (targetAssy, typeInfo), newCtx
|
||||||
@@ -567,8 +643,7 @@ module TypeConcretization =
|
|||||||
ImmutableArray.Empty // No generic parameters
|
ImmutableArray.Empty // No generic parameters
|
||||||
|
|
||||||
let private concretizeTypeReference
|
let private concretizeTypeReference
|
||||||
(loadAssembly :
|
(loadAssembly : IAssemblyLoad)
|
||||||
AssemblyName -> AssemblyReferenceHandle -> ImmutableDictionary<string, DumpedAssembly> * DumpedAssembly)
|
|
||||||
(ctx : ConcretizationContext<'corelib>)
|
(ctx : ConcretizationContext<'corelib>)
|
||||||
(currentAssembly : AssemblyName)
|
(currentAssembly : AssemblyName)
|
||||||
(typeRef : TypeRef)
|
(typeRef : TypeRef)
|
||||||
@@ -591,14 +666,13 @@ module TypeConcretization =
|
|||||||
|
|
||||||
/// Concretize a type in a specific generic context
|
/// Concretize a type in a specific generic context
|
||||||
let rec concretizeType
|
let rec concretizeType
|
||||||
(ctx : ConcretizationContext<'corelib>)
|
(ctx : ConcretizationContext<DumpedAssembly>)
|
||||||
(loadAssembly :
|
(loadAssembly : IAssemblyLoad)
|
||||||
AssemblyName -> AssemblyReferenceHandle -> ImmutableDictionary<string, DumpedAssembly> * DumpedAssembly)
|
|
||||||
(assembly : AssemblyName)
|
(assembly : AssemblyName)
|
||||||
(typeGenerics : ImmutableArray<ConcreteTypeHandle>)
|
(typeGenerics : ImmutableArray<ConcreteTypeHandle>)
|
||||||
(methodGenerics : ImmutableArray<ConcreteTypeHandle>)
|
(methodGenerics : ImmutableArray<ConcreteTypeHandle>)
|
||||||
(typeDefn : TypeDefn)
|
(typeDefn : TypeDefn)
|
||||||
: ConcreteTypeHandle * ConcretizationContext<'corelib>
|
: ConcreteTypeHandle * ConcretizationContext<DumpedAssembly>
|
||||||
=
|
=
|
||||||
|
|
||||||
let key = (assembly, typeDefn)
|
let key = (assembly, typeDefn)
|
||||||
@@ -688,15 +762,14 @@ module TypeConcretization =
|
|||||||
| _ -> failwithf "TODO: Concretization of %A not implemented" typeDefn
|
| _ -> failwithf "TODO: Concretization of %A not implemented" typeDefn
|
||||||
|
|
||||||
and private concretizeGenericInstantiation
|
and private concretizeGenericInstantiation
|
||||||
(ctx : ConcretizationContext<'corelib>)
|
(ctx : ConcretizationContext<DumpedAssembly>)
|
||||||
(loadAssembly :
|
(loadAssembly : IAssemblyLoad)
|
||||||
AssemblyName -> AssemblyReferenceHandle -> ImmutableDictionary<string, DumpedAssembly> * DumpedAssembly)
|
|
||||||
(assembly : AssemblyName)
|
(assembly : AssemblyName)
|
||||||
(typeGenerics : ImmutableArray<ConcreteTypeHandle>)
|
(typeGenerics : ImmutableArray<ConcreteTypeHandle>)
|
||||||
(methodGenerics : ImmutableArray<ConcreteTypeHandle>)
|
(methodGenerics : ImmutableArray<ConcreteTypeHandle>)
|
||||||
(genericDef : TypeDefn)
|
(genericDef : TypeDefn)
|
||||||
(args : ImmutableArray<TypeDefn>)
|
(args : ImmutableArray<TypeDefn>)
|
||||||
: ConcreteTypeHandle * ConcretizationContext<'corelib>
|
: ConcreteTypeHandle * ConcretizationContext<DumpedAssembly>
|
||||||
=
|
=
|
||||||
// First, concretize all type arguments
|
// First, concretize all type arguments
|
||||||
let argHandles, ctxAfterArgs =
|
let argHandles, ctxAfterArgs =
|
||||||
@@ -769,7 +842,8 @@ module TypeConcretization =
|
|||||||
|
|
||||||
| false, _ ->
|
| false, _ ->
|
||||||
// Need to load the assembly
|
// Need to load the assembly
|
||||||
let newAssemblies, loadedAssy = loadAssembly assembly assyRef
|
let newAssemblies, loadedAssy =
|
||||||
|
loadAssembly.LoadAssembly ctx.LoadedAssemblies assembly assyRef
|
||||||
|
|
||||||
let ctxWithNewAssy =
|
let ctxWithNewAssy =
|
||||||
{ ctxAfterArgs with
|
{ ctxAfterArgs with
|
||||||
@@ -847,14 +921,13 @@ module Concretization =
|
|||||||
|
|
||||||
/// Helper to concretize an array of types
|
/// Helper to concretize an array of types
|
||||||
let private concretizeTypeArray
|
let private concretizeTypeArray
|
||||||
(ctx : TypeConcretization.ConcretizationContext<'corelib>)
|
(ctx : TypeConcretization.ConcretizationContext<DumpedAssembly>)
|
||||||
(loadAssembly :
|
(loadAssembly : IAssemblyLoad)
|
||||||
AssemblyName -> AssemblyReferenceHandle -> ImmutableDictionary<string, DumpedAssembly> * DumpedAssembly)
|
|
||||||
(assembly : AssemblyName)
|
(assembly : AssemblyName)
|
||||||
(typeArgs : ImmutableArray<ConcreteTypeHandle>)
|
(typeArgs : ImmutableArray<ConcreteTypeHandle>)
|
||||||
(methodArgs : ImmutableArray<ConcreteTypeHandle>)
|
(methodArgs : ImmutableArray<ConcreteTypeHandle>)
|
||||||
(types : ImmutableArray<TypeDefn>)
|
(types : ImmutableArray<TypeDefn>)
|
||||||
: ImmutableArray<ConcreteTypeHandle> * TypeConcretization.ConcretizationContext<'corelib>
|
: ImmutableArray<ConcreteTypeHandle> * TypeConcretization.ConcretizationContext<DumpedAssembly>
|
||||||
=
|
=
|
||||||
|
|
||||||
let handles = ImmutableArray.CreateBuilder types.Length
|
let handles = ImmutableArray.CreateBuilder types.Length
|
||||||
@@ -871,14 +944,13 @@ module Concretization =
|
|||||||
|
|
||||||
/// Helper to concretize a method signature
|
/// Helper to concretize a method signature
|
||||||
let private concretizeMethodSignature
|
let private concretizeMethodSignature
|
||||||
(ctx : TypeConcretization.ConcretizationContext<'corelib>)
|
(ctx : TypeConcretization.ConcretizationContext<DumpedAssembly>)
|
||||||
(loadAssembly :
|
(loadAssembly : IAssemblyLoad)
|
||||||
AssemblyName -> AssemblyReferenceHandle -> ImmutableDictionary<string, DumpedAssembly> * DumpedAssembly)
|
|
||||||
(assembly : AssemblyName)
|
(assembly : AssemblyName)
|
||||||
(typeArgs : ImmutableArray<ConcreteTypeHandle>)
|
(typeArgs : ImmutableArray<ConcreteTypeHandle>)
|
||||||
(methodArgs : ImmutableArray<ConcreteTypeHandle>)
|
(methodArgs : ImmutableArray<ConcreteTypeHandle>)
|
||||||
(signature : TypeMethodSignature<TypeDefn>)
|
(signature : TypeMethodSignature<TypeDefn>)
|
||||||
: TypeMethodSignature<ConcreteTypeHandle> * TypeConcretization.ConcretizationContext<'corelib>
|
: TypeMethodSignature<ConcreteTypeHandle> * TypeConcretization.ConcretizationContext<DumpedAssembly>
|
||||||
=
|
=
|
||||||
|
|
||||||
// Concretize return type
|
// Concretize return type
|
||||||
@@ -909,8 +981,7 @@ module Concretization =
|
|||||||
|
|
||||||
/// Helper to ensure base type assembly is loaded
|
/// Helper to ensure base type assembly is loaded
|
||||||
let rec private ensureBaseTypeAssembliesLoaded
|
let rec private ensureBaseTypeAssembliesLoaded
|
||||||
(loadAssembly :
|
(loadAssembly : IAssemblyLoad)
|
||||||
AssemblyName -> AssemblyReferenceHandle -> ImmutableDictionary<string, DumpedAssembly> * DumpedAssembly)
|
|
||||||
(assemblies : ImmutableDictionary<string, DumpedAssembly>)
|
(assemblies : ImmutableDictionary<string, DumpedAssembly>)
|
||||||
(assyName : AssemblyName)
|
(assyName : AssemblyName)
|
||||||
(baseTypeInfo : BaseTypeInfo option)
|
(baseTypeInfo : BaseTypeInfo option)
|
||||||
@@ -930,7 +1001,7 @@ module Concretization =
|
|||||||
| true, _ -> assemblies
|
| true, _ -> assemblies
|
||||||
| false, _ ->
|
| false, _ ->
|
||||||
// Need to load the assembly - pass the assembly that contains the reference
|
// Need to load the assembly - pass the assembly that contains the reference
|
||||||
let newAssemblies, _ = loadAssembly assy.Name assyRef
|
let newAssemblies, _ = loadAssembly.LoadAssembly assemblies assy.Name assyRef
|
||||||
newAssemblies
|
newAssemblies
|
||||||
| _ -> assemblies
|
| _ -> assemblies
|
||||||
| Some (BaseTypeInfo.TypeDef _)
|
| Some (BaseTypeInfo.TypeDef _)
|
||||||
@@ -940,8 +1011,7 @@ module Concretization =
|
|||||||
/// Concretize a method's signature and body
|
/// Concretize a method's signature and body
|
||||||
let concretizeMethod
|
let concretizeMethod
|
||||||
(ctx : AllConcreteTypes)
|
(ctx : AllConcreteTypes)
|
||||||
(loadAssembly :
|
(loadAssembly : IAssemblyLoad)
|
||||||
AssemblyName -> AssemblyReferenceHandle -> ImmutableDictionary<string, DumpedAssembly> * DumpedAssembly)
|
|
||||||
(assemblies : ImmutableDictionary<string, DumpedAssembly>)
|
(assemblies : ImmutableDictionary<string, DumpedAssembly>)
|
||||||
(baseTypes : BaseClassTypes<DumpedAssembly>)
|
(baseTypes : BaseClassTypes<DumpedAssembly>)
|
||||||
(method : WoofWare.PawPrint.MethodInfo<'ty, GenericParamFromMetadata, TypeDefn>)
|
(method : WoofWare.PawPrint.MethodInfo<'ty, GenericParamFromMetadata, TypeDefn>)
|
||||||
@@ -1140,8 +1210,7 @@ module Concretization =
|
|||||||
// Recursively convert generic arguments
|
// Recursively convert generic arguments
|
||||||
let genericArgs =
|
let genericArgs =
|
||||||
concreteType.Generics
|
concreteType.Generics
|
||||||
|> Seq.map (fun h -> concreteHandleToTypeDefn baseClassTypes h concreteTypes assemblies)
|
|> ImmutableArray.map (fun h -> concreteHandleToTypeDefn baseClassTypes h concreteTypes assemblies)
|
||||||
|> ImmutableArray.CreateRange
|
|
||||||
|
|
||||||
let baseDef =
|
let baseDef =
|
||||||
TypeDefn.FromDefinition (concreteType.Definition, concreteType.Assembly.FullName, signatureTypeKind)
|
TypeDefn.FromDefinition (concreteType.Definition, concreteType.Assembly.FullName, signatureTypeKind)
|
||||||
|
@@ -6,6 +6,8 @@ open System.Reflection.Metadata
|
|||||||
open System.Reflection.Metadata.Ecma335
|
open System.Reflection.Metadata.Ecma335
|
||||||
open Microsoft.FSharp.Core
|
open Microsoft.FSharp.Core
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
[<NoComparison>]
|
||||||
type ResolvedBaseType =
|
type ResolvedBaseType =
|
||||||
| Enum
|
| Enum
|
||||||
| ValueType
|
| ValueType
|
||||||
@@ -167,8 +169,8 @@ module PrimitiveType =
|
|||||||
| PrimitiveType.Double -> 8
|
| PrimitiveType.Double -> 8
|
||||||
| PrimitiveType.String -> 8
|
| PrimitiveType.String -> 8
|
||||||
| PrimitiveType.TypedReference -> failwith "todo"
|
| PrimitiveType.TypedReference -> failwith "todo"
|
||||||
| PrimitiveType.IntPtr -> 8
|
| PrimitiveType.IntPtr -> NATIVE_INT_SIZE
|
||||||
| PrimitiveType.UIntPtr -> 8
|
| PrimitiveType.UIntPtr -> NATIVE_INT_SIZE
|
||||||
| PrimitiveType.Object -> 8
|
| PrimitiveType.Object -> 8
|
||||||
|
|
||||||
type TypeDefn =
|
type TypeDefn =
|
||||||
|
@@ -29,6 +29,10 @@ type InterfaceImplementation =
|
|||||||
RelativeToAssembly : AssemblyName
|
RelativeToAssembly : AssemblyName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Layout =
|
||||||
|
| Default
|
||||||
|
| Custom of size : int * packingSize : int
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents detailed information about a type definition in a .NET assembly.
|
/// Represents detailed information about a type definition in a .NET assembly.
|
||||||
/// This is a strongly-typed representation of TypeDefinition from System.Reflection.Metadata.
|
/// This is a strongly-typed representation of TypeDefinition from System.Reflection.Metadata.
|
||||||
@@ -93,6 +97,8 @@ type TypeInfo<'generic, 'fieldGeneric> =
|
|||||||
Events : EventDefn ImmutableArray
|
Events : EventDefn ImmutableArray
|
||||||
|
|
||||||
ImplementedInterfaces : InterfaceImplementation ImmutableArray
|
ImplementedInterfaces : InterfaceImplementation ImmutableArray
|
||||||
|
|
||||||
|
Layout : Layout
|
||||||
}
|
}
|
||||||
|
|
||||||
member this.IsInterface = this.TypeAttributes.HasFlag TypeAttributes.Interface
|
member this.IsInterface = this.TypeAttributes.HasFlag TypeAttributes.Interface
|
||||||
@@ -184,6 +190,18 @@ type BaseClassTypes<'corelib> =
|
|||||||
TypedReference : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
TypedReference : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
IntPtr : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
IntPtr : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
UIntPtr : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
UIntPtr : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
|
Exception : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
|
ArithmeticException : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
|
DivideByZeroException : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
|
OverflowException : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
|
StackOverflowException : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
|
TypeLoadException : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
|
IndexOutOfRangeException : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
|
InvalidCastException : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
|
MissingFieldException : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
|
MissingMethodException : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
|
NullReferenceException : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
|
OutOfMemoryException : TypeInfo<GenericParamFromMetadata, TypeDefn>
|
||||||
}
|
}
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
@@ -213,6 +231,7 @@ module TypeInfo =
|
|||||||
Generics = gen
|
Generics = gen
|
||||||
Events = t.Events
|
Events = t.Events
|
||||||
ImplementedInterfaces = t.ImplementedInterfaces
|
ImplementedInterfaces = t.ImplementedInterfaces
|
||||||
|
Layout = t.Layout
|
||||||
}
|
}
|
||||||
|
|
||||||
let mapGeneric<'a, 'b, 'field> (f : 'a -> 'b) (t : TypeInfo<'a, 'field>) : TypeInfo<'b, 'field> =
|
let mapGeneric<'a, 'b, 'field> (f : 'a -> 'b) (t : TypeInfo<'a, 'field>) : TypeInfo<'b, 'field> =
|
||||||
@@ -308,6 +327,14 @@ module TypeInfo =
|
|||||||
|
|
||||||
result.ToImmutable ()
|
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
|
Namespace = ns
|
||||||
Name = name
|
Name = name
|
||||||
@@ -323,6 +350,7 @@ module TypeInfo =
|
|||||||
Events = events
|
Events = events
|
||||||
ImplementedInterfaces = interfaces
|
ImplementedInterfaces = interfaces
|
||||||
DeclaringType = declaringType
|
DeclaringType = declaringType
|
||||||
|
Layout = layout
|
||||||
}
|
}
|
||||||
|
|
||||||
let isBaseType<'corelib>
|
let isBaseType<'corelib>
|
||||||
@@ -348,10 +376,10 @@ module TypeInfo =
|
|||||||
|
|
||||||
let rec resolveBaseType<'corelib, 'generic, 'field>
|
let rec resolveBaseType<'corelib, 'generic, 'field>
|
||||||
(baseClassTypes : BaseClassTypes<'corelib>)
|
(baseClassTypes : BaseClassTypes<'corelib>)
|
||||||
|
(sourceAssy : 'corelib)
|
||||||
(getName : 'corelib -> AssemblyName)
|
(getName : 'corelib -> AssemblyName)
|
||||||
(getTypeDef : 'corelib -> TypeDefinitionHandle -> TypeInfo<'generic, 'field>)
|
(getTypeDef : 'corelib -> TypeDefinitionHandle -> TypeInfo<'generic, 'field>)
|
||||||
(getTypeRef : 'corelib -> TypeReferenceHandle -> TypeInfo<'generic, 'field>)
|
(getTypeRef : 'corelib -> TypeReferenceHandle -> 'corelib * TypeInfo<'generic, 'field>)
|
||||||
(sourceAssembly : AssemblyName)
|
|
||||||
(value : BaseTypeInfo option)
|
(value : BaseTypeInfo option)
|
||||||
: ResolvedBaseType
|
: ResolvedBaseType
|
||||||
=
|
=
|
||||||
@@ -361,40 +389,48 @@ module TypeInfo =
|
|||||||
|
|
||||||
match value with
|
match value with
|
||||||
| BaseTypeInfo.TypeDef typeDefinitionHandle ->
|
| BaseTypeInfo.TypeDef typeDefinitionHandle ->
|
||||||
match isBaseType baseClassTypes getName sourceAssembly typeDefinitionHandle with
|
match isBaseType baseClassTypes getName (getName sourceAssy) typeDefinitionHandle with
|
||||||
| Some x -> x
|
| Some x -> x
|
||||||
| None ->
|
| None ->
|
||||||
let baseType = getTypeDef baseClassTypes.Corelib typeDefinitionHandle
|
let baseType = getTypeDef baseClassTypes.Corelib typeDefinitionHandle
|
||||||
resolveBaseType baseClassTypes getName getTypeDef getTypeRef sourceAssembly baseType.BaseType
|
resolveBaseType baseClassTypes sourceAssy getName getTypeDef getTypeRef baseType.BaseType
|
||||||
| BaseTypeInfo.TypeRef typeReferenceHandle ->
|
| BaseTypeInfo.TypeRef typeReferenceHandle ->
|
||||||
let typeRef = getTypeRef baseClassTypes.Corelib typeReferenceHandle
|
let targetAssy, typeRef = getTypeRef sourceAssy typeReferenceHandle
|
||||||
failwith $"{typeRef}"
|
|
||||||
|
match isBaseType baseClassTypes getName (getName targetAssy) typeRef.TypeDefHandle with
|
||||||
|
| Some x -> x
|
||||||
|
| None ->
|
||||||
|
let baseType = getTypeDef baseClassTypes.Corelib typeRef.TypeDefHandle
|
||||||
|
resolveBaseType baseClassTypes sourceAssy getName getTypeDef getTypeRef baseType.BaseType
|
||||||
| BaseTypeInfo.TypeSpec typeSpecificationHandle -> failwith "todo"
|
| BaseTypeInfo.TypeSpec typeSpecificationHandle -> failwith "todo"
|
||||||
| BaseTypeInfo.ForeignAssemblyType (assemblyName, typeDefinitionHandle) ->
|
| BaseTypeInfo.ForeignAssemblyType (assemblyName, typeDefinitionHandle) ->
|
||||||
resolveBaseType
|
resolveBaseType
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
|
sourceAssy
|
||||||
getName
|
getName
|
||||||
getTypeDef
|
getTypeDef
|
||||||
getTypeRef
|
getTypeRef
|
||||||
assemblyName
|
|
||||||
(Some (BaseTypeInfo.TypeDef typeDefinitionHandle))
|
(Some (BaseTypeInfo.TypeDef typeDefinitionHandle))
|
||||||
|
|
||||||
let toTypeDefn
|
let toTypeDefn
|
||||||
(baseClassTypes : BaseClassTypes<'corelib>)
|
(baseClassTypes : BaseClassTypes<'corelib>)
|
||||||
|
(assemblies : AssemblyName -> 'corelib)
|
||||||
(getName : 'corelib -> AssemblyName)
|
(getName : 'corelib -> AssemblyName)
|
||||||
(getTypeDef : 'corelib -> TypeDefinitionHandle -> TypeInfo<'generic, 'field>)
|
(getTypeDef : 'corelib -> TypeDefinitionHandle -> TypeInfo<'generic, 'field>)
|
||||||
(getTypeRef : 'corelib -> TypeReferenceHandle -> TypeInfo<'generic, 'field>)
|
(getTypeRef : 'corelib -> TypeReferenceHandle -> 'corelib * TypeInfo<'generic, 'field>)
|
||||||
(ty : TypeInfo<TypeDefn, TypeDefn>)
|
(ty : TypeInfo<TypeDefn, TypeDefn>)
|
||||||
: TypeDefn
|
: TypeDefn
|
||||||
=
|
=
|
||||||
let stk =
|
let stk =
|
||||||
match resolveBaseType baseClassTypes getName getTypeDef getTypeRef ty.Assembly ty.BaseType with
|
match resolveBaseType baseClassTypes (assemblies ty.Assembly) getName getTypeDef getTypeRef ty.BaseType with
|
||||||
| ResolvedBaseType.Enum
|
| ResolvedBaseType.Enum
|
||||||
| ResolvedBaseType.ValueType -> SignatureTypeKind.ValueType
|
| ResolvedBaseType.ValueType -> SignatureTypeKind.ValueType
|
||||||
| ResolvedBaseType.Object
|
| ResolvedBaseType.Object
|
||||||
| ResolvedBaseType.Delegate -> SignatureTypeKind.Class
|
| ResolvedBaseType.Delegate -> SignatureTypeKind.Class
|
||||||
|
|
||||||
let defn =
|
let defn =
|
||||||
|
// The only allowed construction of FromDefinition!
|
||||||
|
// All other constructions should use DumpedAssembly.typeInfoToTypeDefn.
|
||||||
TypeDefn.FromDefinition (ComparableTypeDefinitionHandle.Make ty.TypeDefHandle, ty.Assembly.FullName, stk)
|
TypeDefn.FromDefinition (ComparableTypeDefinitionHandle.Make ty.TypeDefHandle, ty.Assembly.FullName, stk)
|
||||||
|
|
||||||
if ty.Generics.IsEmpty then
|
if ty.Generics.IsEmpty then
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="StringToken.fs" />
|
<Compile Include="StringToken.fs" />
|
||||||
|
<Compile Include="Constants.fs" />
|
||||||
<Compile Include="ImmutableArray.fs" />
|
<Compile Include="ImmutableArray.fs" />
|
||||||
<Compile Include="Tokens.fs" />
|
<Compile Include="Tokens.fs" />
|
||||||
<Compile Include="TypeRef.fs" />
|
<Compile Include="TypeRef.fs" />
|
||||||
|
@@ -23,7 +23,7 @@ module LoggerFactory =
|
|||||||
let makeTest () : (unit -> LogLine list) * ILoggerFactory =
|
let makeTest () : (unit -> LogLine list) * ILoggerFactory =
|
||||||
// Shared sink for all loggers created by the factory.
|
// Shared sink for all loggers created by the factory.
|
||||||
let sink = ResizeArray ()
|
let sink = ResizeArray ()
|
||||||
let isEnabled (logLevel : LogLevel) : bool = logLevel >= LogLevel.Debug
|
let isEnabled (logLevel : LogLevel) : bool = logLevel >= LogLevel.Information
|
||||||
|
|
||||||
let createLogger (category : string) : ILogger =
|
let createLogger (category : string) : ILogger =
|
||||||
{ new ILogger with
|
{ new ILogger with
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
namespace WoofWare.Pawprint.Test
|
namespace WoofWare.Pawprint.Test
|
||||||
|
|
||||||
|
open System
|
||||||
open System.Collections.Immutable
|
open System.Collections.Immutable
|
||||||
open System.IO
|
open System.IO
|
||||||
open FsUnitTyped
|
open FsUnitTyped
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
namespace WoofWare.Pawprint.Test
|
namespace WoofWare.Pawprint.Test
|
||||||
|
|
||||||
|
open System
|
||||||
open System.Collections.Immutable
|
open System.Collections.Immutable
|
||||||
open System.IO
|
open System.IO
|
||||||
open FsUnitTyped
|
open FsUnitTyped
|
||||||
@@ -16,141 +17,65 @@ module TestPureCases =
|
|||||||
|
|
||||||
let unimplemented =
|
let unimplemented =
|
||||||
[
|
[
|
||||||
{
|
"CrossAssemblyTypes.cs"
|
||||||
FileName = "CrossAssemblyTypes.cs"
|
"OverlappingStructs.cs"
|
||||||
ExpectedReturnCode = 0
|
"AdvancedStructLayout.cs"
|
||||||
NativeImpls = MockEnv.make ()
|
"InitializeArray.cs"
|
||||||
}
|
"Threads.cs"
|
||||||
{
|
"ComplexTryCatch.cs"
|
||||||
FileName = "InitializeArray.cs"
|
"ResizeArray.cs"
|
||||||
ExpectedReturnCode = 0
|
"LdtokenField.cs"
|
||||||
NativeImpls = MockEnv.make ()
|
"GenericEdgeCases.cs"
|
||||||
}
|
"UnsafeAs.cs"
|
||||||
{
|
|
||||||
FileName = "GenericEdgeCases.cs"
|
|
||||||
ExpectedReturnCode = 0
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "Threads.cs"
|
|
||||||
ExpectedReturnCode = 3
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "ComplexTryCatch.cs"
|
|
||||||
ExpectedReturnCode = 14
|
|
||||||
NativeImpls = NativeImpls.PassThru ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "ResizeArray.cs"
|
|
||||||
ExpectedReturnCode = 109
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "Sizeof.cs"
|
|
||||||
ExpectedReturnCode = 0
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "LdtokenField.cs"
|
|
||||||
ExpectedReturnCode = 0
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
|> Set.ofList
|
||||||
|
|
||||||
|
let requiresMocks =
|
||||||
|
let empty = MockEnv.make ()
|
||||||
|
|
||||||
let cases : EndToEndTestCase list =
|
|
||||||
[
|
[
|
||||||
{
|
"BasicLock.cs",
|
||||||
FileName = "NoOp.cs"
|
(1,
|
||||||
ExpectedReturnCode = 1
|
{ empty with
|
||||||
NativeImpls = MockEnv.make ()
|
System_Threading_Monitor = System_Threading_Monitor.passThru
|
||||||
}
|
})
|
||||||
{
|
|
||||||
FileName = "TestShl.cs"
|
|
||||||
ExpectedReturnCode = 0
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "TestShr.cs"
|
|
||||||
ExpectedReturnCode = 0
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "StaticVariables.cs"
|
|
||||||
ExpectedReturnCode = 0
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "Ldind.cs"
|
|
||||||
ExpectedReturnCode = 0
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "CustomDelegate.cs"
|
|
||||||
ExpectedReturnCode = 8
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "ArgumentOrdering.cs"
|
|
||||||
ExpectedReturnCode = 0
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "BasicLock.cs"
|
|
||||||
ExpectedReturnCode = 1
|
|
||||||
NativeImpls =
|
|
||||||
let mock = MockEnv.make ()
|
|
||||||
|
|
||||||
{ mock with
|
|
||||||
System_Threading_Monitor = System_Threading_Monitor.passThru
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "TriangleNumber.cs"
|
|
||||||
ExpectedReturnCode = 10
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "ExceptionWithNoOpFinally.cs"
|
|
||||||
ExpectedReturnCode = 3
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "ExceptionWithNoOpCatch.cs"
|
|
||||||
ExpectedReturnCode = 10
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "Floats.cs"
|
|
||||||
ExpectedReturnCode = 0
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "TryCatchWithThrowInBody.cs"
|
|
||||||
ExpectedReturnCode = 4
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "Ldelema.cs"
|
|
||||||
ExpectedReturnCode = 0
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "TypeConcretization.cs"
|
|
||||||
ExpectedReturnCode = 0
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "TestOr.cs"
|
|
||||||
ExpectedReturnCode = 0
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
FileName = "InterfaceDispatch.cs"
|
|
||||||
ExpectedReturnCode = 0
|
|
||||||
NativeImpls = MockEnv.make ()
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
|> Map.ofList
|
||||||
|
|
||||||
|
let customExitCodes =
|
||||||
|
[
|
||||||
|
"NoOp.cs", 1
|
||||||
|
"CustomDelegate.cs", 8
|
||||||
|
"ExceptionWithNoOpFinally.cs", 3
|
||||||
|
"ExceptionWithNoOpCatch.cs", 10
|
||||||
|
"TryCatchWithThrowInBody.cs", 4
|
||||||
|
"ResizeArray.cs", 114
|
||||||
|
"Threads.cs", 3
|
||||||
|
"TriangleNumber.cs", 10
|
||||||
|
]
|
||||||
|
|> Map.ofList
|
||||||
|
|
||||||
|
let allPure =
|
||||||
|
assy.GetManifestResourceNames ()
|
||||||
|
|> Seq.choose (fun res ->
|
||||||
|
let s = "WoofWare.PawPrint.Test.sourcesPure."
|
||||||
|
|
||||||
|
if res.StartsWith (s, StringComparison.OrdinalIgnoreCase) then
|
||||||
|
res.Substring s.Length |> Some
|
||||||
|
else
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|> Set.ofSeq
|
||||||
|
|
||||||
|
let simpleCases : string list =
|
||||||
|
allPure
|
||||||
|
|> Seq.filter (fun s ->
|
||||||
|
(customExitCodes.ContainsKey s
|
||||||
|
|| requiresMocks.ContainsKey s
|
||||||
|
|| unimplemented.Contains s)
|
||||||
|
|> not
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
let runTest (case : EndToEndTestCase) : unit =
|
let runTest (case : EndToEndTestCase) : unit =
|
||||||
let source = Assembly.getEmbeddedResourceAsString case.FileName assy
|
let source = Assembly.getEmbeddedResourceAsString case.FileName assy
|
||||||
@@ -163,11 +88,12 @@ module TestPureCases =
|
|||||||
use peImage = new MemoryStream (image)
|
use peImage = new MemoryStream (image)
|
||||||
|
|
||||||
try
|
try
|
||||||
|
let realResult = RealRuntime.executeWithRealRuntime [||] image
|
||||||
|
realResult.ExitCode |> shouldEqual case.ExpectedReturnCode
|
||||||
|
|
||||||
let terminalState, terminatingThread =
|
let terminalState, terminatingThread =
|
||||||
Program.run loggerFactory (Some case.FileName) peImage dotnetRuntimes case.NativeImpls []
|
Program.run loggerFactory (Some case.FileName) peImage dotnetRuntimes case.NativeImpls []
|
||||||
|
|
||||||
let realResult = RealRuntime.executeWithRealRuntime [||] image
|
|
||||||
|
|
||||||
let exitCode =
|
let exitCode =
|
||||||
match terminalState.ThreadState.[terminatingThread].MethodState.EvaluationStack.Values with
|
match terminalState.ThreadState.[terminatingThread].MethodState.EvaluationStack.Values with
|
||||||
| [] -> failwith "expected program to return a value, but it returned void"
|
| [] -> failwith "expected program to return a value, but it returned void"
|
||||||
@@ -178,16 +104,49 @@ module TestPureCases =
|
|||||||
|
|
||||||
exitCode |> shouldEqual realResult.ExitCode
|
exitCode |> shouldEqual realResult.ExitCode
|
||||||
|
|
||||||
exitCode |> shouldEqual case.ExpectedReturnCode
|
|
||||||
with _ ->
|
with _ ->
|
||||||
for message in messages () do
|
for message in messages () do
|
||||||
System.Console.Error.WriteLine $"{message}"
|
System.Console.Error.WriteLine $"{message}"
|
||||||
|
|
||||||
reraise ()
|
reraise ()
|
||||||
|
|
||||||
|
[<TestCaseSource(nameof simpleCases)>]
|
||||||
|
let ``Standard tests`` (fileName : string) =
|
||||||
|
{
|
||||||
|
FileName = fileName
|
||||||
|
ExpectedReturnCode = 0
|
||||||
|
NativeImpls = MockEnv.make ()
|
||||||
|
}
|
||||||
|
|> runTest
|
||||||
|
|
||||||
|
[<TestCaseSource(nameof customExitCodes)>]
|
||||||
|
let ``Custom exit code tests`` (KeyValue (fileName : string, exitCode : int)) =
|
||||||
|
if unimplemented.Contains fileName then
|
||||||
|
Assert.Inconclusive ()
|
||||||
|
|
||||||
|
{
|
||||||
|
FileName = fileName
|
||||||
|
ExpectedReturnCode = exitCode
|
||||||
|
NativeImpls = MockEnv.make ()
|
||||||
|
}
|
||||||
|
|> runTest
|
||||||
|
|
||||||
|
[<TestCaseSource(nameof requiresMocks)>]
|
||||||
|
let ``Tests which require mocks`` (KeyValue (fileName : string, (exitCode : int, mock : NativeImpls))) =
|
||||||
|
{
|
||||||
|
FileName = fileName
|
||||||
|
ExpectedReturnCode = exitCode
|
||||||
|
NativeImpls = mock
|
||||||
|
}
|
||||||
|
|> runTest
|
||||||
|
|
||||||
|
|
||||||
[<TestCaseSource(nameof unimplemented)>]
|
[<TestCaseSource(nameof unimplemented)>]
|
||||||
[<Explicit>]
|
[<Explicit>]
|
||||||
let ``Can evaluate C# files, unimplemented`` (case : EndToEndTestCase) = runTest case
|
let ``Can evaluate C# files, unimplemented`` (fileName : string) =
|
||||||
|
{
|
||||||
[<TestCaseSource(nameof cases)>]
|
FileName = fileName
|
||||||
let ``Can evaluate C# files`` (case : EndToEndTestCase) = runTest case
|
ExpectedReturnCode = 0
|
||||||
|
NativeImpls = MockEnv.make ()
|
||||||
|
}
|
||||||
|
|> runTest
|
||||||
|
480
WoofWare.PawPrint.Test/sourcesPure/AdvancedStructLayout.cs
Normal file
480
WoofWare.PawPrint.Test/sourcesPure/AdvancedStructLayout.cs
Normal file
@@ -0,0 +1,480 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
public class StructLayoutTestsAdvanced
|
||||||
|
{
|
||||||
|
// Test structs
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct PointerTestStruct
|
||||||
|
{
|
||||||
|
public int A;
|
||||||
|
public byte B;
|
||||||
|
public short C;
|
||||||
|
public int D;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
unsafe struct FixedBufferStruct
|
||||||
|
{
|
||||||
|
public int Header;
|
||||||
|
public fixed byte Buffer[64];
|
||||||
|
public int Footer;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
unsafe struct NestedFixedStruct
|
||||||
|
{
|
||||||
|
public fixed int IntArray[4];
|
||||||
|
public fixed double DoubleArray[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||||
|
struct MarshalStringStruct
|
||||||
|
{
|
||||||
|
public int Id;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
|
||||||
|
public string Name;
|
||||||
|
public double Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct MarshalArrayStruct
|
||||||
|
{
|
||||||
|
public int Count;
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||||
|
public int[] Values;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct BlittableStruct
|
||||||
|
{
|
||||||
|
public int X;
|
||||||
|
public double Y;
|
||||||
|
public long Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref struct RefStruct
|
||||||
|
{
|
||||||
|
public int Value;
|
||||||
|
public Span<int> Span;
|
||||||
|
|
||||||
|
public RefStruct(int value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
Span = new Span<int>(new int[] { value, value * 2, value * 3 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly struct ReadOnlyStruct
|
||||||
|
{
|
||||||
|
public readonly int X;
|
||||||
|
public readonly int Y;
|
||||||
|
|
||||||
|
public ReadOnlyStruct(int x, int y)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Sum => X + Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly ref struct ReadOnlyRefStruct
|
||||||
|
{
|
||||||
|
public readonly int Value;
|
||||||
|
public readonly ReadOnlySpan<byte> Data;
|
||||||
|
|
||||||
|
public ReadOnlyRefStruct(int value, ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Generic<T> where T : struct
|
||||||
|
{
|
||||||
|
public T Value;
|
||||||
|
public int Index;
|
||||||
|
|
||||||
|
public Generic(T value, int index)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
Index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DoubleGeneric<T, U>
|
||||||
|
{
|
||||||
|
public T First;
|
||||||
|
public U Second;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IIndexable
|
||||||
|
{
|
||||||
|
int GetIndex();
|
||||||
|
void SetIndex(int value);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StructWithInterface : IIndexable
|
||||||
|
{
|
||||||
|
public int Index;
|
||||||
|
public string Data;
|
||||||
|
|
||||||
|
public int GetIndex() => Index;
|
||||||
|
public void SetIndex(int value) => Index = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IMutable
|
||||||
|
{
|
||||||
|
void Mutate();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MutableStruct : IMutable
|
||||||
|
{
|
||||||
|
public int Counter;
|
||||||
|
|
||||||
|
public void Mutate()
|
||||||
|
{
|
||||||
|
Counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RefReturnStruct
|
||||||
|
{
|
||||||
|
public int A;
|
||||||
|
public int B;
|
||||||
|
public int C;
|
||||||
|
|
||||||
|
public static ref int GetRef(ref RefReturnStruct s, int index)
|
||||||
|
{
|
||||||
|
if (index == 0) return ref s.A;
|
||||||
|
if (index == 1) return ref s.B;
|
||||||
|
return ref s.C;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsafe int TestUnsafePointers()
|
||||||
|
{
|
||||||
|
var s = new PointerTestStruct { A = 0x12345678, B = 0xAB, C = 0x1234, D = unchecked((int)0xDEADBEEF) };
|
||||||
|
|
||||||
|
// Test sizeof
|
||||||
|
int size = sizeof(PointerTestStruct);
|
||||||
|
if (size == 0) return 1;
|
||||||
|
|
||||||
|
// Test pointer access
|
||||||
|
PointerTestStruct* ptr = &s;
|
||||||
|
if (ptr->A != 0x12345678) return 2;
|
||||||
|
if (ptr->B != 0xAB) return 3;
|
||||||
|
if (ptr->C != 0x1234) return 4;
|
||||||
|
if (ptr->D != unchecked((int)0xDEADBEEF)) return 5;
|
||||||
|
|
||||||
|
// Test pointer arithmetic and casting
|
||||||
|
byte* bytePtr = (byte*)ptr;
|
||||||
|
int* intPtr = (int*)bytePtr;
|
||||||
|
if (*intPtr != 0x12345678) return 6; // First int field
|
||||||
|
|
||||||
|
// Verify field offsets
|
||||||
|
int* dPtr = &(ptr->D);
|
||||||
|
int* aPtr = &(ptr->A);
|
||||||
|
long ptrDiff = (byte*)dPtr - (byte*)aPtr;
|
||||||
|
if (ptrDiff < 8) return 7; // D should be at least 8 bytes from A
|
||||||
|
|
||||||
|
// Test modification through pointer
|
||||||
|
ptr->A = 999;
|
||||||
|
if (s.A != 999) return 8;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsafe int TestFixedBuffers()
|
||||||
|
{
|
||||||
|
var f = new FixedBufferStruct();
|
||||||
|
f.Header = 0xFEED;
|
||||||
|
f.Footer = 0xBEEF;
|
||||||
|
|
||||||
|
// Test fixed buffer access
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
f.Buffer[i] = (byte)(i % 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f.Header != 0xFEED) return 10;
|
||||||
|
if (f.Footer != 0xBEEF) return 11;
|
||||||
|
|
||||||
|
// Verify buffer contents
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
if (f.Buffer[i] != (byte)(i % 256)) return 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test pointer to fixed buffer
|
||||||
|
byte* bufPtr = f.Buffer;
|
||||||
|
bufPtr[0] = 255;
|
||||||
|
if (f.Buffer[0] != 255) return 13;
|
||||||
|
|
||||||
|
// Test nested fixed arrays
|
||||||
|
var n = new NestedFixedStruct();
|
||||||
|
n.IntArray[0] = 100;
|
||||||
|
n.IntArray[3] = 400;
|
||||||
|
n.DoubleArray[0] = 1.5;
|
||||||
|
n.DoubleArray[1] = 2.5;
|
||||||
|
|
||||||
|
if (n.IntArray[0] != 100) return 14;
|
||||||
|
if (n.IntArray[3] != 400) return 15;
|
||||||
|
if (Math.Abs(n.DoubleArray[0] - 1.5) > 0.0001) return 16;
|
||||||
|
if (Math.Abs(n.DoubleArray[1] - 2.5) > 0.0001) return 17;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsafe int TestMarshaling()
|
||||||
|
{
|
||||||
|
// Test string marshaling
|
||||||
|
var ms = new MarshalStringStruct
|
||||||
|
{
|
||||||
|
Id = 42,
|
||||||
|
Name = "TestString",
|
||||||
|
Value = 3.14159
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ms.Id != 42) return 20;
|
||||||
|
if (ms.Name != "TestString") return 21;
|
||||||
|
if (Math.Abs(ms.Value - 3.14159) > 0.00001) return 22;
|
||||||
|
|
||||||
|
// Test Marshal.SizeOf
|
||||||
|
int marshalSize = Marshal.SizeOf(typeof(MarshalStringStruct));
|
||||||
|
if (marshalSize == 0) return 23;
|
||||||
|
|
||||||
|
// Test array marshaling
|
||||||
|
var ma = new MarshalArrayStruct
|
||||||
|
{
|
||||||
|
Count = 5,
|
||||||
|
Values = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ma.Count != 5) return 24;
|
||||||
|
if (ma.Values.Length != 8) return 25;
|
||||||
|
if (ma.Values[7] != 8) return 26;
|
||||||
|
|
||||||
|
// Test StructureToPtr and PtrToStructure
|
||||||
|
var blittable = new BlittableStruct { X = 100, Y = 200.5, Z = 300 };
|
||||||
|
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(BlittableStruct)));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Marshal.StructureToPtr(blittable, ptr, false);
|
||||||
|
var recovered = (BlittableStruct)Marshal.PtrToStructure(ptr, typeof(BlittableStruct));
|
||||||
|
|
||||||
|
if (recovered.X != 100) return 27;
|
||||||
|
if (Math.Abs(recovered.Y - 200.5) > 0.00001) return 28;
|
||||||
|
if (recovered.Z != 300) return 29;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestRefStructs()
|
||||||
|
{
|
||||||
|
// Test ref struct
|
||||||
|
var rs = new RefStruct(10);
|
||||||
|
if (rs.Value != 10) return 30;
|
||||||
|
if (rs.Span.Length != 3) return 31;
|
||||||
|
if (rs.Span[0] != 10) return 32;
|
||||||
|
if (rs.Span[1] != 20) return 33;
|
||||||
|
if (rs.Span[2] != 30) return 34;
|
||||||
|
|
||||||
|
// Modify through span
|
||||||
|
rs.Span[0] = 100;
|
||||||
|
if (rs.Span[0] != 100) return 35;
|
||||||
|
|
||||||
|
// Test readonly struct
|
||||||
|
var ros = new ReadOnlyStruct(5, 7);
|
||||||
|
if (ros.X != 5) return 36;
|
||||||
|
if (ros.Y != 7) return 37;
|
||||||
|
if (ros.Sum != 12) return 38;
|
||||||
|
|
||||||
|
// Verify immutability - create new instance
|
||||||
|
var ros2 = new ReadOnlyStruct(10, 20);
|
||||||
|
if (ros.X != 5) return 39; // Original should be unchanged
|
||||||
|
|
||||||
|
// Test readonly ref struct
|
||||||
|
byte[] data = { 1, 2, 3, 4 };
|
||||||
|
var rors = new ReadOnlyRefStruct(42, new ReadOnlySpan<byte>(data));
|
||||||
|
if (rors.Value != 42) return 40;
|
||||||
|
if (rors.Data.Length != 4) return 41;
|
||||||
|
if (rors.Data[3] != 4) return 42;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestGenerics()
|
||||||
|
{
|
||||||
|
// Test single generic parameter
|
||||||
|
var g1 = new Generic<int>(42, 1);
|
||||||
|
if (g1.Value != 42) return 50;
|
||||||
|
if (g1.Index != 1) return 51;
|
||||||
|
|
||||||
|
var g2 = new Generic<double>(3.14, 2);
|
||||||
|
if (Math.Abs(g2.Value - 3.14) > 0.00001) return 52;
|
||||||
|
if (g2.Index != 2) return 53;
|
||||||
|
|
||||||
|
// Test with custom struct
|
||||||
|
var inner = new ReadOnlyStruct(10, 20);
|
||||||
|
var g3 = new Generic<ReadOnlyStruct>(inner, 3);
|
||||||
|
if (g3.Value.X != 10) return 54;
|
||||||
|
if (g3.Value.Y != 20) return 55;
|
||||||
|
if (g3.Index != 3) return 56;
|
||||||
|
|
||||||
|
// Test double generic
|
||||||
|
var dg = new DoubleGeneric<int, string> { First = 100, Second = "test" };
|
||||||
|
if (dg.First != 100) return 57;
|
||||||
|
if (dg.Second != "test") return 58;
|
||||||
|
|
||||||
|
// Test with different type combinations
|
||||||
|
var dg2 = new DoubleGeneric<double, long> { First = 2.718, Second = long.MaxValue };
|
||||||
|
if (Math.Abs(dg2.First - 2.718) > 0.00001) return 59;
|
||||||
|
if (dg2.Second != long.MaxValue) return 60;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestByRefReturns()
|
||||||
|
{
|
||||||
|
var r = new RefReturnStruct { A = 10, B = 20, C = 30 };
|
||||||
|
|
||||||
|
// Test ref return
|
||||||
|
ref int refA = ref RefReturnStruct.GetRef(ref r, 0);
|
||||||
|
if (refA != 10) return 70;
|
||||||
|
|
||||||
|
// Modify through ref
|
||||||
|
refA = 100;
|
||||||
|
if (r.A != 100) return 71;
|
||||||
|
|
||||||
|
ref int refB = ref RefReturnStruct.GetRef(ref r, 1);
|
||||||
|
refB = 200;
|
||||||
|
if (r.B != 200) return 72;
|
||||||
|
|
||||||
|
ref int refC = ref RefReturnStruct.GetRef(ref r, 2);
|
||||||
|
refC = 300;
|
||||||
|
if (r.C != 300) return 73;
|
||||||
|
|
||||||
|
// Test ref local
|
||||||
|
ref int localRef = ref r.A;
|
||||||
|
localRef = 1000;
|
||||||
|
if (r.A != 1000) return 74;
|
||||||
|
|
||||||
|
// Test that ref points to actual field
|
||||||
|
localRef = 2000;
|
||||||
|
if (refA != 2000) return 75; // Both should see the change
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestStructInterfaces()
|
||||||
|
{
|
||||||
|
// Test struct implementing interface
|
||||||
|
var s = new StructWithInterface { Index = 42, Data = "test" };
|
||||||
|
if (s.GetIndex() != 42) return 80;
|
||||||
|
|
||||||
|
s.SetIndex(100);
|
||||||
|
if (s.Index != 100) return 81;
|
||||||
|
|
||||||
|
// Test boxing to interface
|
||||||
|
IIndexable boxed = s; // Boxing occurs here
|
||||||
|
if (boxed.GetIndex() != 100) return 82;
|
||||||
|
|
||||||
|
// Modify through interface (modifies boxed copy)
|
||||||
|
boxed.SetIndex(200);
|
||||||
|
if (boxed.GetIndex() != 200) return 83;
|
||||||
|
if (s.Index != 100) return 84; // Original should be unchanged
|
||||||
|
|
||||||
|
// Test mutable interface
|
||||||
|
var m = new MutableStruct { Counter = 0 };
|
||||||
|
m.Mutate();
|
||||||
|
if (m.Counter != 1) return 85;
|
||||||
|
|
||||||
|
// Box to interface and mutate
|
||||||
|
IMutable boxedMutable = m; // Boxing
|
||||||
|
boxedMutable.Mutate();
|
||||||
|
if (m.Counter != 1) return 86; // Original unchanged
|
||||||
|
|
||||||
|
// Cast back to see boxed mutation
|
||||||
|
var unboxed = (MutableStruct)boxedMutable;
|
||||||
|
if (unboxed.Counter != 2) return 87;
|
||||||
|
|
||||||
|
// Direct interface call on boxed struct maintains state
|
||||||
|
boxedMutable.Mutate();
|
||||||
|
boxedMutable.Mutate();
|
||||||
|
var unboxed2 = (MutableStruct)boxedMutable;
|
||||||
|
if (unboxed2.Counter != 4) return 88;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsafe int TestCombinedScenarios()
|
||||||
|
{
|
||||||
|
// Test generic with fixed buffer struct
|
||||||
|
var f = new FixedBufferStruct();
|
||||||
|
f.Header = 999;
|
||||||
|
f.Buffer[0] = 123;
|
||||||
|
f.Footer = 111;
|
||||||
|
|
||||||
|
var generic = new Generic<FixedBufferStruct>(f, 42);
|
||||||
|
if (generic.Value.Header != 999) return 90;
|
||||||
|
if (generic.Value.Buffer[0] != 123) return 91;
|
||||||
|
if (generic.Value.Footer != 111) return 92;
|
||||||
|
if (generic.Index != 42) return 93;
|
||||||
|
|
||||||
|
// Test marshaling with generic
|
||||||
|
var marshalable = new BlittableStruct { X = 10, Y = 20.0, Z = 30 };
|
||||||
|
var genericMarshal = new Generic<BlittableStruct>(marshalable, 5);
|
||||||
|
|
||||||
|
if (genericMarshal.Value.X != 10) return 94;
|
||||||
|
if (Math.Abs(genericMarshal.Value.Y - 20.0) > 0.00001) return 95;
|
||||||
|
if (genericMarshal.Value.Z != 30) return 96;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Main(string[] argv)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
result = TestUnsafePointers();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestFixedBuffers();
|
||||||
|
if (result != 0) return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = TestMarshaling();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestRefStructs();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestGenerics();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestByRefReturns();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestStructInterfaces();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
result = TestCombinedScenarios();
|
||||||
|
if (result != 0) return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // All tests passed
|
||||||
|
}
|
||||||
|
}
|
255
WoofWare.PawPrint.Test/sourcesPure/ComparisonOperations.cs
Normal file
255
WoofWare.PawPrint.Test/sourcesPure/ComparisonOperations.cs
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
public class TestComparisonOperations
|
||||||
|
{
|
||||||
|
// Test Ceq: Compare equal
|
||||||
|
public static int TestCompareEqual()
|
||||||
|
{
|
||||||
|
// Integer equality
|
||||||
|
if ((5 == 5) != true) return 1;
|
||||||
|
if ((5 == 6) != false) return 2;
|
||||||
|
if ((int.MaxValue == int.MaxValue) != true) return 3;
|
||||||
|
if ((int.MinValue == int.MaxValue) != false) return 4;
|
||||||
|
|
||||||
|
// Negative numbers
|
||||||
|
if ((-1 == -1) != true) return 5;
|
||||||
|
if ((-5 == 5) != false) return 6;
|
||||||
|
|
||||||
|
// Long equality
|
||||||
|
if ((100L == 100L) != true) return 7;
|
||||||
|
if ((100L == 101L) != false) return 8;
|
||||||
|
|
||||||
|
// Mixed sizes (after promotion)
|
||||||
|
int i = 42;
|
||||||
|
long l = 42L;
|
||||||
|
if ((l == (long)i) != true) return 9;
|
||||||
|
|
||||||
|
// Zero comparisons
|
||||||
|
if ((0 == 0) != true) return 10;
|
||||||
|
if ((0 == 1) != false) return 11;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Cgt: Compare greater than (signed)
|
||||||
|
public static int TestCompareGreaterThan()
|
||||||
|
{
|
||||||
|
// Positive integers
|
||||||
|
if ((10 > 5) != true) return 20;
|
||||||
|
if ((5 > 10) != false) return 21;
|
||||||
|
if ((5 > 5) != false) return 22;
|
||||||
|
|
||||||
|
// Negative integers
|
||||||
|
if ((-5 > -10) != true) return 23;
|
||||||
|
if ((-10 > -5) != false) return 24;
|
||||||
|
if ((5 > -5) != true) return 25;
|
||||||
|
if ((-5 > 5) != false) return 26;
|
||||||
|
|
||||||
|
// Boundary values
|
||||||
|
if ((int.MaxValue > int.MinValue) != true) return 27;
|
||||||
|
if ((int.MinValue > int.MaxValue) != false) return 28;
|
||||||
|
if ((int.MaxValue > (int.MaxValue - 1)) != true) return 29;
|
||||||
|
|
||||||
|
// Zero comparisons
|
||||||
|
if ((1 > 0) != true) return 30;
|
||||||
|
if ((0 > 1) != false) return 31;
|
||||||
|
if ((-1 > 0) != false) return 32;
|
||||||
|
if ((0 > -1) != true) return 33;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Cgt_un: Compare greater than (unsigned)
|
||||||
|
public static int TestCompareGreaterThanUnsigned()
|
||||||
|
{
|
||||||
|
uint a = 10;
|
||||||
|
uint b = 5;
|
||||||
|
|
||||||
|
// Basic unsigned comparison
|
||||||
|
if ((a > b) != true) return 40;
|
||||||
|
if ((b > a) != false) return 41;
|
||||||
|
if ((a > a) != false) return 42;
|
||||||
|
|
||||||
|
// High bit set (would be negative if signed)
|
||||||
|
uint high = 0x80000000;
|
||||||
|
uint low = 0x7FFFFFFF;
|
||||||
|
if ((high > low) != true) return 43; // Unsigned: high > low
|
||||||
|
|
||||||
|
// Maximum values
|
||||||
|
uint max = uint.MaxValue;
|
||||||
|
uint min = uint.MinValue;
|
||||||
|
if ((max > min) != true) return 44;
|
||||||
|
if ((min > max) != false) return 45;
|
||||||
|
|
||||||
|
// Interpret negative as unsigned
|
||||||
|
uint negAsUint = unchecked((uint)-1);
|
||||||
|
uint one = 1;
|
||||||
|
if ((negAsUint > one) != true) return 46; // 0xFFFFFFFF > 1
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Clt: Compare less than (signed)
|
||||||
|
public static int TestCompareLessThan()
|
||||||
|
{
|
||||||
|
// Positive integers
|
||||||
|
if ((5 < 10) != true) return 50;
|
||||||
|
if ((10 < 5) != false) return 51;
|
||||||
|
if ((5 < 5) != false) return 52;
|
||||||
|
|
||||||
|
// Negative integers
|
||||||
|
if ((-10 < -5) != true) return 53;
|
||||||
|
if ((-5 < -10) != false) return 54;
|
||||||
|
if ((-5 < 5) != true) return 55;
|
||||||
|
if ((5 < -5) != false) return 56;
|
||||||
|
|
||||||
|
// Boundary values
|
||||||
|
if ((int.MinValue < int.MaxValue) != true) return 57;
|
||||||
|
if ((int.MaxValue < int.MinValue) != false) return 58;
|
||||||
|
|
||||||
|
// Zero comparisons
|
||||||
|
if ((0 < 1) != true) return 59;
|
||||||
|
if ((1 < 0) != false) return 60;
|
||||||
|
if ((0 < -1) != false) return 61;
|
||||||
|
if ((-1 < 0) != true) return 62;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Clt_un: Compare less than (unsigned)
|
||||||
|
public static int TestCompareLessThanUnsigned()
|
||||||
|
{
|
||||||
|
uint a = 5;
|
||||||
|
uint b = 10;
|
||||||
|
|
||||||
|
// Basic unsigned comparison
|
||||||
|
if ((a < b) != true) return 70;
|
||||||
|
if ((b < a) != false) return 71;
|
||||||
|
if ((a < a) != false) return 72;
|
||||||
|
|
||||||
|
// High bit set
|
||||||
|
uint high = 0x80000000;
|
||||||
|
uint low = 0x7FFFFFFF;
|
||||||
|
if ((low < high) != true) return 73; // Unsigned: low < high
|
||||||
|
|
||||||
|
// Boundary values
|
||||||
|
uint max = uint.MaxValue;
|
||||||
|
uint min = uint.MinValue;
|
||||||
|
if ((min < max) != true) return 74;
|
||||||
|
if ((max < min) != false) return 75;
|
||||||
|
|
||||||
|
// Negative as unsigned
|
||||||
|
uint one = 1;
|
||||||
|
uint negAsUint = unchecked((uint)-1);
|
||||||
|
if ((one < negAsUint) != true) return 76; // 1 < 0xFFFFFFFF
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test comparison combinations
|
||||||
|
public static int TestComparisonCombinations()
|
||||||
|
{
|
||||||
|
int x = 10;
|
||||||
|
int y = 20;
|
||||||
|
int z = 10;
|
||||||
|
|
||||||
|
// Equality chains
|
||||||
|
if ((x == z) != true) return 80;
|
||||||
|
if ((x == y) != false) return 81;
|
||||||
|
|
||||||
|
// Inequality combinations
|
||||||
|
if ((x < y && y > x) != true) return 82;
|
||||||
|
if ((x < y && x == y) != false) return 83;
|
||||||
|
|
||||||
|
// Transitive comparisons
|
||||||
|
if (x < y && y < 30)
|
||||||
|
{
|
||||||
|
if ((x < 30) != true) return 84;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 85;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test comparisons with different types
|
||||||
|
public static int TestMixedTypeComparisons()
|
||||||
|
{
|
||||||
|
// byte comparisons (unsigned by default)
|
||||||
|
byte b1 = 200;
|
||||||
|
byte b2 = 100;
|
||||||
|
if ((b1 > b2) != true) return 90;
|
||||||
|
|
||||||
|
// sbyte comparisons (signed)
|
||||||
|
sbyte sb1 = -50;
|
||||||
|
sbyte sb2 = 50;
|
||||||
|
if ((sb1 < sb2) != true) return 91;
|
||||||
|
|
||||||
|
// short comparisons
|
||||||
|
short s1 = -1000;
|
||||||
|
short s2 = 1000;
|
||||||
|
if ((s1 < s2) != true) return 92;
|
||||||
|
if ((s1 == s2) != false) return 93;
|
||||||
|
|
||||||
|
// long comparisons
|
||||||
|
long l1 = long.MaxValue;
|
||||||
|
long l2 = long.MinValue;
|
||||||
|
if ((l1 > l2) != true) return 94;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test null comparisons
|
||||||
|
public static int TestNullComparisons()
|
||||||
|
{
|
||||||
|
object obj1 = null;
|
||||||
|
object obj2 = null;
|
||||||
|
object obj3 = new object();
|
||||||
|
|
||||||
|
// Null equality
|
||||||
|
if ((obj1 == obj2) != true) return 100;
|
||||||
|
if ((obj1 == obj3) != false) return 101;
|
||||||
|
if ((obj3 == obj1) != false) return 102;
|
||||||
|
|
||||||
|
// String null comparisons
|
||||||
|
string s1 = null;
|
||||||
|
string s2 = null;
|
||||||
|
string s3 = "";
|
||||||
|
|
||||||
|
if ((s1 == s2) != true) return 103;
|
||||||
|
if ((s1 == s3) != false) return 104;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Main(string[] argv)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
result = TestCompareEqual();
|
||||||
|
if (result != 0) return 100 + result;
|
||||||
|
|
||||||
|
result = TestCompareGreaterThan();
|
||||||
|
if (result != 0) return 200 + result;
|
||||||
|
|
||||||
|
result = TestCompareGreaterThanUnsigned();
|
||||||
|
if (result != 0) return 300 + result;
|
||||||
|
|
||||||
|
result = TestCompareLessThan();
|
||||||
|
if (result != 0) return 400 + result;
|
||||||
|
|
||||||
|
result = TestCompareLessThanUnsigned();
|
||||||
|
if (result != 0) return 500 + result;
|
||||||
|
|
||||||
|
result = TestComparisonCombinations();
|
||||||
|
if (result != 0) return 600 + result;
|
||||||
|
|
||||||
|
result = TestMixedTypeComparisons();
|
||||||
|
if (result != 0) return 700 + result;
|
||||||
|
|
||||||
|
result = TestNullComparisons();
|
||||||
|
if (result != 0) return 800 + result;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
@@ -8,7 +8,7 @@ namespace HelloWorldApp
|
|||||||
{
|
{
|
||||||
int[] array = new[] { 1, 2, 3 };
|
int[] array = new[] { 1, 2, 3 };
|
||||||
|
|
||||||
if (array.Sum() != 60)
|
if (array.Sum() != 6)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
503
WoofWare.PawPrint.Test/sourcesPure/Initobj.cs
Normal file
503
WoofWare.PawPrint.Test/sourcesPure/Initobj.cs
Normal file
@@ -0,0 +1,503 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
public class TestInitobj
|
||||||
|
{
|
||||||
|
// Simple struct with various primitive types
|
||||||
|
private struct SimpleStruct
|
||||||
|
{
|
||||||
|
public int IntField;
|
||||||
|
public bool BoolField;
|
||||||
|
public char CharField;
|
||||||
|
public double DoubleField;
|
||||||
|
public byte ByteField;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nested struct
|
||||||
|
private struct NestedStruct
|
||||||
|
{
|
||||||
|
public SimpleStruct Inner;
|
||||||
|
public int OuterField;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct with arrays and references
|
||||||
|
private struct ComplexStruct
|
||||||
|
{
|
||||||
|
public object ObjectRef;
|
||||||
|
public string StringRef;
|
||||||
|
public int[] ArrayRef;
|
||||||
|
public int ValueField;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic struct
|
||||||
|
private struct GenericStruct<T>
|
||||||
|
{
|
||||||
|
public T Value;
|
||||||
|
public int Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct for field tests
|
||||||
|
private struct StructWithStructField
|
||||||
|
{
|
||||||
|
public SimpleStruct NestedField;
|
||||||
|
public int OtherField;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class with struct field for heap test
|
||||||
|
private class ClassWithStructField
|
||||||
|
{
|
||||||
|
public SimpleStruct StructField;
|
||||||
|
public int IntField;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 1: Initialize simple struct with local variable
|
||||||
|
public static int Test1()
|
||||||
|
{
|
||||||
|
SimpleStruct s = new SimpleStruct
|
||||||
|
{
|
||||||
|
IntField = 42,
|
||||||
|
BoolField = true,
|
||||||
|
CharField = 'X',
|
||||||
|
DoubleField = 3.14,
|
||||||
|
ByteField = 255
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify initial values
|
||||||
|
if (s.IntField != 42) return 1;
|
||||||
|
if (s.BoolField != true) return 2;
|
||||||
|
if (s.CharField != 'X') return 3;
|
||||||
|
if (s.DoubleField != 3.14) return 4;
|
||||||
|
if (s.ByteField != 255) return 5;
|
||||||
|
|
||||||
|
// Use default to reset the struct (generates initobj)
|
||||||
|
s = default(SimpleStruct);
|
||||||
|
|
||||||
|
// Verify all fields are zeroed
|
||||||
|
if (s.IntField != 0) return 6;
|
||||||
|
if (s.BoolField != false) return 7;
|
||||||
|
if (s.CharField != '\0') return 8;
|
||||||
|
if (s.DoubleField != 0.0) return 9;
|
||||||
|
if (s.ByteField != 0) return 10;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 2: Initialize nested struct
|
||||||
|
public static int Test2()
|
||||||
|
{
|
||||||
|
NestedStruct n = new NestedStruct
|
||||||
|
{
|
||||||
|
Inner = new SimpleStruct
|
||||||
|
{
|
||||||
|
IntField = 100,
|
||||||
|
BoolField = true,
|
||||||
|
CharField = 'A',
|
||||||
|
DoubleField = 1.23,
|
||||||
|
ByteField = 128
|
||||||
|
},
|
||||||
|
OuterField = 999
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify initial values
|
||||||
|
if (n.Inner.IntField != 100) return 20;
|
||||||
|
if (n.OuterField != 999) return 21;
|
||||||
|
|
||||||
|
// Reset using default keyword (which should generate initobj)
|
||||||
|
n = default(NestedStruct);
|
||||||
|
|
||||||
|
// Verify all fields are zeroed
|
||||||
|
if (n.Inner.IntField != 0) return 22;
|
||||||
|
if (n.Inner.BoolField != false) return 23;
|
||||||
|
if (n.Inner.CharField != '\0') return 24;
|
||||||
|
if (n.Inner.DoubleField != 0.0) return 25;
|
||||||
|
if (n.Inner.ByteField != 0) return 26;
|
||||||
|
if (n.OuterField != 0) return 27;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 3: Initialize struct with reference types
|
||||||
|
public static int Test3()
|
||||||
|
{
|
||||||
|
ComplexStruct c = new ComplexStruct
|
||||||
|
{
|
||||||
|
ObjectRef = new object(),
|
||||||
|
StringRef = "Hello",
|
||||||
|
ArrayRef = new int[3],
|
||||||
|
ValueField = 42
|
||||||
|
};
|
||||||
|
c.ArrayRef[0] = 1;
|
||||||
|
c.ArrayRef[1] = 2;
|
||||||
|
c.ArrayRef[2] = 3;
|
||||||
|
|
||||||
|
// Verify initial values
|
||||||
|
if (c.ObjectRef == null) return 30;
|
||||||
|
if (c.StringRef != "Hello") return 31;
|
||||||
|
if (c.ArrayRef == null || c.ArrayRef.Length != 3) return 32;
|
||||||
|
if (c.ValueField != 42) return 33;
|
||||||
|
|
||||||
|
// Reset using default
|
||||||
|
c = default(ComplexStruct);
|
||||||
|
|
||||||
|
// Verify references are null and value is zero
|
||||||
|
if (c.ObjectRef != null) return 34;
|
||||||
|
if (c.StringRef != null) return 35;
|
||||||
|
if (c.ArrayRef != null) return 36;
|
||||||
|
if (c.ValueField != 0) return 37;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 4: Initialize generic struct
|
||||||
|
public static int Test4()
|
||||||
|
{
|
||||||
|
GenericStruct<int> gi = new GenericStruct<int>
|
||||||
|
{
|
||||||
|
Value = 123,
|
||||||
|
Count = 456
|
||||||
|
};
|
||||||
|
|
||||||
|
if (gi.Value != 123) return 40;
|
||||||
|
if (gi.Count != 456) return 41;
|
||||||
|
|
||||||
|
gi = default(GenericStruct<int>);
|
||||||
|
|
||||||
|
if (gi.Value != 0) return 42;
|
||||||
|
if (gi.Count != 0) return 43;
|
||||||
|
|
||||||
|
// Test with reference type
|
||||||
|
GenericStruct<string> gs = new GenericStruct<string>
|
||||||
|
{
|
||||||
|
Value = "Test",
|
||||||
|
Count = 789
|
||||||
|
};
|
||||||
|
|
||||||
|
if (gs.Value != "Test") return 44;
|
||||||
|
if (gs.Count != 789) return 45;
|
||||||
|
|
||||||
|
gs = default(GenericStruct<string>);
|
||||||
|
|
||||||
|
if (gs.Value != null) return 46;
|
||||||
|
if (gs.Count != 0) return 47;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 5: Initialize struct in array element using ref
|
||||||
|
public static int Test5()
|
||||||
|
{
|
||||||
|
SimpleStruct[] array = new SimpleStruct[3];
|
||||||
|
|
||||||
|
// Set values in first element
|
||||||
|
array[0].IntField = 111;
|
||||||
|
array[0].BoolField = true;
|
||||||
|
array[0].CharField = 'Z';
|
||||||
|
|
||||||
|
if (array[0].IntField != 111) return 50;
|
||||||
|
if (array[0].BoolField != true) return 51;
|
||||||
|
if (array[0].CharField != 'Z') return 52;
|
||||||
|
|
||||||
|
// Reset first element using default assignment
|
||||||
|
array[0] = default(SimpleStruct);
|
||||||
|
|
||||||
|
if (array[0].IntField != 0) return 53;
|
||||||
|
if (array[0].BoolField != false) return 54;
|
||||||
|
if (array[0].CharField != '\0') return 55;
|
||||||
|
|
||||||
|
// Also test with ref local
|
||||||
|
array[1].IntField = 222;
|
||||||
|
ref SimpleStruct secondElement = ref array[1];
|
||||||
|
secondElement = default(SimpleStruct);
|
||||||
|
|
||||||
|
if (array[1].IntField != 0) return 56;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 6: Initialize struct through method parameter
|
||||||
|
public static int Test6()
|
||||||
|
{
|
||||||
|
SimpleStruct s = new SimpleStruct
|
||||||
|
{
|
||||||
|
IntField = 200,
|
||||||
|
BoolField = true,
|
||||||
|
CharField = 'M',
|
||||||
|
DoubleField = 2.71,
|
||||||
|
ByteField = 64
|
||||||
|
};
|
||||||
|
|
||||||
|
ResetStruct(ref s);
|
||||||
|
|
||||||
|
if (s.IntField != 0) return 60;
|
||||||
|
if (s.BoolField != false) return 61;
|
||||||
|
if (s.CharField != '\0') return 62;
|
||||||
|
if (s.DoubleField != 0.0) return 63;
|
||||||
|
if (s.ByteField != 0) return 64;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ResetStruct(ref SimpleStruct s)
|
||||||
|
{
|
||||||
|
s = default(SimpleStruct);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 7: Initialize multiple structs
|
||||||
|
public static int Test7()
|
||||||
|
{
|
||||||
|
SimpleStruct s1 = new SimpleStruct { IntField = 1 };
|
||||||
|
SimpleStruct s2 = new SimpleStruct { IntField = 2 };
|
||||||
|
SimpleStruct s3 = new SimpleStruct { IntField = 3 };
|
||||||
|
|
||||||
|
if (s1.IntField != 1) return 70;
|
||||||
|
if (s2.IntField != 2) return 71;
|
||||||
|
if (s3.IntField != 3) return 72;
|
||||||
|
|
||||||
|
s1 = default(SimpleStruct);
|
||||||
|
s2 = default(SimpleStruct);
|
||||||
|
s3 = default(SimpleStruct);
|
||||||
|
|
||||||
|
if (s1.IntField != 0) return 73;
|
||||||
|
if (s2.IntField != 0) return 74;
|
||||||
|
if (s3.IntField != 0) return 75;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 8: Initialize struct passed as argument (tests Argument case)
|
||||||
|
public static int Test8()
|
||||||
|
{
|
||||||
|
SimpleStruct s = new SimpleStruct
|
||||||
|
{
|
||||||
|
IntField = 333,
|
||||||
|
BoolField = true,
|
||||||
|
CharField = 'Q',
|
||||||
|
DoubleField = 4.56,
|
||||||
|
ByteField = 77
|
||||||
|
};
|
||||||
|
|
||||||
|
int result = InitializeArgumentStruct(ref s);
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
// Verify struct was reset
|
||||||
|
if (s.IntField != 0) return 80;
|
||||||
|
if (s.BoolField != false) return 81;
|
||||||
|
if (s.CharField != '\0') return 82;
|
||||||
|
if (s.DoubleField != 0.0) return 83;
|
||||||
|
if (s.ByteField != 0) return 84;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int InitializeArgumentStruct(ref SimpleStruct arg)
|
||||||
|
{
|
||||||
|
// Verify initial values
|
||||||
|
if (arg.IntField != 333) return 85;
|
||||||
|
if (arg.BoolField != true) return 86;
|
||||||
|
|
||||||
|
// Reset using default - this should use initobj on the argument
|
||||||
|
arg = default(SimpleStruct);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 9: Initialize struct field (tests Field case)
|
||||||
|
public static int Test9()
|
||||||
|
{
|
||||||
|
StructWithStructField container = new StructWithStructField
|
||||||
|
{
|
||||||
|
NestedField = new SimpleStruct
|
||||||
|
{
|
||||||
|
IntField = 444,
|
||||||
|
BoolField = true,
|
||||||
|
CharField = 'F',
|
||||||
|
DoubleField = 7.89,
|
||||||
|
ByteField = 88
|
||||||
|
},
|
||||||
|
OtherField = 555
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify initial values
|
||||||
|
if (container.NestedField.IntField != 444) return 90;
|
||||||
|
if (container.OtherField != 555) return 91;
|
||||||
|
|
||||||
|
// Reset the nested field using ref
|
||||||
|
ref SimpleStruct fieldRef = ref container.NestedField;
|
||||||
|
fieldRef = default(SimpleStruct);
|
||||||
|
|
||||||
|
// Verify nested field was reset but other field unchanged
|
||||||
|
if (container.NestedField.IntField != 0) return 92;
|
||||||
|
if (container.NestedField.BoolField != false) return 93;
|
||||||
|
if (container.NestedField.CharField != '\0') return 94;
|
||||||
|
if (container.NestedField.DoubleField != 0.0) return 95;
|
||||||
|
if (container.NestedField.ByteField != 0) return 96;
|
||||||
|
if (container.OtherField != 555) return 97;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 10: Initialize struct in heap-allocated object (tests Heap case)
|
||||||
|
public static int Test10()
|
||||||
|
{
|
||||||
|
ClassWithStructField obj = new ClassWithStructField
|
||||||
|
{
|
||||||
|
StructField = new SimpleStruct
|
||||||
|
{
|
||||||
|
IntField = 666,
|
||||||
|
BoolField = true,
|
||||||
|
CharField = 'H',
|
||||||
|
DoubleField = 9.99,
|
||||||
|
ByteField = 99
|
||||||
|
},
|
||||||
|
IntField = 777
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify initial values
|
||||||
|
if (obj.StructField.IntField != 666) return 100;
|
||||||
|
if (obj.IntField != 777) return 101;
|
||||||
|
|
||||||
|
// Reset the struct field
|
||||||
|
obj.StructField = default(SimpleStruct);
|
||||||
|
|
||||||
|
// Verify struct field was reset but other field unchanged
|
||||||
|
if (obj.StructField.IntField != 0) return 102;
|
||||||
|
if (obj.StructField.BoolField != false) return 103;
|
||||||
|
if (obj.StructField.CharField != '\0') return 104;
|
||||||
|
if (obj.StructField.DoubleField != 0.0) return 105;
|
||||||
|
if (obj.StructField.ByteField != 0) return 106;
|
||||||
|
if (obj.IntField != 777) return 107;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 11: Initialize struct through unsafe pointer manipulation
|
||||||
|
public static unsafe int Test11()
|
||||||
|
{
|
||||||
|
SimpleStruct s = new SimpleStruct
|
||||||
|
{
|
||||||
|
IntField = 888,
|
||||||
|
BoolField = true,
|
||||||
|
CharField = 'P',
|
||||||
|
DoubleField = 11.11,
|
||||||
|
ByteField = 111
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get a pointer to the struct
|
||||||
|
SimpleStruct* ptr = &s;
|
||||||
|
|
||||||
|
// Initialize through pointer
|
||||||
|
*ptr = default(SimpleStruct);
|
||||||
|
|
||||||
|
// Verify all fields are zeroed
|
||||||
|
if (s.IntField != 0) return 110;
|
||||||
|
if (s.BoolField != false) return 111;
|
||||||
|
if (s.CharField != '\0') return 112;
|
||||||
|
if (s.DoubleField != 0.0) return 113;
|
||||||
|
if (s.ByteField != 0) return 114;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 12: Initialize struct through Unsafe.AsRef
|
||||||
|
public static int Test12()
|
||||||
|
{
|
||||||
|
SimpleStruct s = new SimpleStruct
|
||||||
|
{
|
||||||
|
IntField = 999,
|
||||||
|
BoolField = true,
|
||||||
|
CharField = 'U',
|
||||||
|
DoubleField = 12.34,
|
||||||
|
ByteField = 200
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use Unsafe to get a ref and initialize it
|
||||||
|
ref SimpleStruct sRef = ref s;
|
||||||
|
sRef = default(SimpleStruct);
|
||||||
|
|
||||||
|
// Verify all fields are zeroed
|
||||||
|
if (s.IntField != 0) return 120;
|
||||||
|
if (s.BoolField != false) return 121;
|
||||||
|
if (s.CharField != '\0') return 122;
|
||||||
|
if (s.DoubleField != 0.0) return 123;
|
||||||
|
if (s.ByteField != 0) return 124;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 13: Initialize readonly struct
|
||||||
|
public static int Test13()
|
||||||
|
{
|
||||||
|
ReadonlyStruct ros = new ReadonlyStruct(100, true);
|
||||||
|
|
||||||
|
// Verify initial values through properties
|
||||||
|
if (ros.IntValue != 100) return 130;
|
||||||
|
if (ros.BoolValue != true) return 131;
|
||||||
|
|
||||||
|
// Reset using default
|
||||||
|
ros = default(ReadonlyStruct);
|
||||||
|
|
||||||
|
// Verify zeroed
|
||||||
|
if (ros.IntValue != 0) return 132;
|
||||||
|
if (ros.BoolValue != false) return 133;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly struct ReadonlyStruct
|
||||||
|
{
|
||||||
|
public readonly int IntValue;
|
||||||
|
public readonly bool BoolValue;
|
||||||
|
|
||||||
|
public ReadonlyStruct(int i, bool b)
|
||||||
|
{
|
||||||
|
IntValue = i;
|
||||||
|
BoolValue = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Main(string[] argv)
|
||||||
|
{
|
||||||
|
var result = Test1();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test2();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test3();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test4();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test5();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test6();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test7();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test8();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test9();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test10();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test11();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test12();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test13();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
// All tests passed
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
364
WoofWare.PawPrint.Test/sourcesPure/OverlappingStructs.cs
Normal file
364
WoofWare.PawPrint.Test/sourcesPure/OverlappingStructs.cs
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
public class StructLayoutTests
|
||||||
|
{
|
||||||
|
// Test structs with various layouts
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct SequentialStruct
|
||||||
|
{
|
||||||
|
public int A;
|
||||||
|
public byte B;
|
||||||
|
public long C;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
struct ExplicitUnion
|
||||||
|
{
|
||||||
|
[FieldOffset(0)] public int AsInt;
|
||||||
|
[FieldOffset(0)] public float AsFloat;
|
||||||
|
[FieldOffset(0)] public byte Byte0;
|
||||||
|
[FieldOffset(1)] public byte Byte1;
|
||||||
|
[FieldOffset(2)] public byte Byte2;
|
||||||
|
[FieldOffset(3)] public byte Byte3;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 16)]
|
||||||
|
struct FixedSizeStruct
|
||||||
|
{
|
||||||
|
[FieldOffset(0)] public long First;
|
||||||
|
[FieldOffset(8)] public int Second;
|
||||||
|
[FieldOffset(12)] public short Third;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
struct PackedStruct
|
||||||
|
{
|
||||||
|
public byte A;
|
||||||
|
public int B;
|
||||||
|
public byte C;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Auto)]
|
||||||
|
struct AutoLayoutStruct
|
||||||
|
{
|
||||||
|
public int X;
|
||||||
|
public string Y;
|
||||||
|
public double Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
struct NestedUnion
|
||||||
|
{
|
||||||
|
[FieldOffset(0)] public ExplicitUnion Inner;
|
||||||
|
[FieldOffset(0)] public long AsLong;
|
||||||
|
[FieldOffset(4)] public int UpperInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
struct LargeUnion
|
||||||
|
{
|
||||||
|
[FieldOffset(0)] public long Long1;
|
||||||
|
[FieldOffset(8)] public long Long2;
|
||||||
|
[FieldOffset(0)] public double Double1;
|
||||||
|
[FieldOffset(8)] public double Double2;
|
||||||
|
[FieldOffset(0)] public decimal AsDecimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static fields for testing
|
||||||
|
static SequentialStruct staticSequential;
|
||||||
|
static ExplicitUnion staticUnion;
|
||||||
|
static FixedSizeStruct staticFixed;
|
||||||
|
|
||||||
|
// Instance fields for testing
|
||||||
|
class FieldContainer
|
||||||
|
{
|
||||||
|
public SequentialStruct instanceSequential;
|
||||||
|
public ExplicitUnion instanceUnion;
|
||||||
|
public PackedStruct instancePacked;
|
||||||
|
public NestedUnion instanceNested;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestSequentialLayout()
|
||||||
|
{
|
||||||
|
var s = new SequentialStruct { A = 42, B = 255, C = long.MaxValue };
|
||||||
|
|
||||||
|
// Test field access
|
||||||
|
if (s.A != 42) return 1;
|
||||||
|
if (s.B != 255) return 2;
|
||||||
|
if (s.C != long.MaxValue) return 3;
|
||||||
|
|
||||||
|
// Test copy semantics
|
||||||
|
var s2 = s;
|
||||||
|
s2.A = 100;
|
||||||
|
if (s.A != 42) return 4; // Should be unchanged (value type)
|
||||||
|
if (s2.A != 100) return 5;
|
||||||
|
|
||||||
|
// Test static field storage
|
||||||
|
staticSequential = s;
|
||||||
|
if (staticSequential.A != 42) return 6;
|
||||||
|
if (staticSequential.C != long.MaxValue) return 7;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestExplicitUnion()
|
||||||
|
{
|
||||||
|
var u = new ExplicitUnion();
|
||||||
|
|
||||||
|
// Test overlapping int/float
|
||||||
|
u.AsInt = 0x3F800000; // IEEE 754 representation of 1.0f
|
||||||
|
if (Math.Abs(u.AsFloat - 1.0f) > 0.0001f) return 10;
|
||||||
|
|
||||||
|
// Test byte-level access
|
||||||
|
u.AsInt = 0x12345678;
|
||||||
|
bool isLittleEndian = BitConverter.IsLittleEndian;
|
||||||
|
if (isLittleEndian)
|
||||||
|
{
|
||||||
|
if (u.Byte0 != 0x78) return 11;
|
||||||
|
if (u.Byte1 != 0x56) return 12;
|
||||||
|
if (u.Byte2 != 0x34) return 13;
|
||||||
|
if (u.Byte3 != 0x12) return 14;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (u.Byte0 != 0x12) return 11;
|
||||||
|
if (u.Byte1 != 0x34) return 12;
|
||||||
|
if (u.Byte2 != 0x56) return 13;
|
||||||
|
if (u.Byte3 != 0x78) return 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test static field
|
||||||
|
staticUnion = u;
|
||||||
|
if (staticUnion.AsInt != 0x12345678) return 15;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestFixedSizeStruct()
|
||||||
|
{
|
||||||
|
var f = new FixedSizeStruct { First = -1, Second = 42, Third = 1000 };
|
||||||
|
|
||||||
|
if (f.First != -1) return 20;
|
||||||
|
if (f.Second != 42) return 21;
|
||||||
|
if (f.Third != 1000) return 22;
|
||||||
|
|
||||||
|
// Test size is respected
|
||||||
|
int size = Marshal.SizeOf(typeof(FixedSizeStruct));
|
||||||
|
if (size != 16) return 23;
|
||||||
|
|
||||||
|
staticFixed = f;
|
||||||
|
if (staticFixed.Second != 42) return 24;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestPackedStruct()
|
||||||
|
{
|
||||||
|
var p = new PackedStruct { A = 1, B = 0x12345678, C = 2 };
|
||||||
|
|
||||||
|
if (p.A != 1) return 30;
|
||||||
|
if (p.B != 0x12345678) return 31;
|
||||||
|
if (p.C != 2) return 32;
|
||||||
|
|
||||||
|
// Packed struct should be 6 bytes (1 + 4 + 1)
|
||||||
|
int size = Marshal.SizeOf(typeof(PackedStruct));
|
||||||
|
if (size != 6) return 33;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestInstanceFields()
|
||||||
|
{
|
||||||
|
var container = new FieldContainer();
|
||||||
|
|
||||||
|
container.instanceSequential = new SequentialStruct { A = 111, B = 222, C = 333 };
|
||||||
|
if (container.instanceSequential.A != 111) return 40;
|
||||||
|
|
||||||
|
container.instanceUnion = new ExplicitUnion { AsInt = unchecked((int)0xDEADBEEF) };
|
||||||
|
if (container.instanceUnion.AsInt != unchecked((int)0xDEADBEEF)) return 41;
|
||||||
|
|
||||||
|
container.instancePacked = new PackedStruct { A = 10, B = 20, C = 30 };
|
||||||
|
if (container.instancePacked.B != 20) return 42;
|
||||||
|
|
||||||
|
container.instanceNested = new NestedUnion();
|
||||||
|
container.instanceNested.Inner.AsInt = 100;
|
||||||
|
if (container.instanceNested.Inner.AsInt != 100) return 43;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestStructPassing()
|
||||||
|
{
|
||||||
|
var s = new SequentialStruct { A = 500, B = 50, C = 5005 };
|
||||||
|
int result = ProcessSequential(s);
|
||||||
|
if (result != 555) return 50; // 500 + 50 + 5 (C % 1000)
|
||||||
|
|
||||||
|
var u = new ExplicitUnion { AsInt = 1000 };
|
||||||
|
u = TransformUnion(u);
|
||||||
|
if (u.AsInt != 2000) return 51;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ProcessSequential(SequentialStruct s)
|
||||||
|
{
|
||||||
|
return s.A + s.B + (int)(s.C % 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ExplicitUnion TransformUnion(ExplicitUnion u)
|
||||||
|
{
|
||||||
|
u.AsInt *= 2;
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestNestedUnion()
|
||||||
|
{
|
||||||
|
var n = new NestedUnion();
|
||||||
|
n.Inner.AsInt = 0x12345678;
|
||||||
|
|
||||||
|
// Lower 32 bits should match Inner.AsInt
|
||||||
|
if ((n.AsLong & 0xFFFFFFFF) != 0x12345678) return 60;
|
||||||
|
|
||||||
|
// Modify upper int
|
||||||
|
n.UpperInt = unchecked((int)0xABCDEF00);
|
||||||
|
|
||||||
|
// Check both parts
|
||||||
|
if (n.Inner.AsInt != 0x12345678) return 61;
|
||||||
|
if (n.UpperInt != unchecked((int)0xABCDEF00)) return 62;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestLargeUnion()
|
||||||
|
{
|
||||||
|
var l = new LargeUnion();
|
||||||
|
|
||||||
|
// Test double/long overlap
|
||||||
|
l.Double1 = 1.0;
|
||||||
|
l.Double2 = 2.0;
|
||||||
|
|
||||||
|
// IEEE 754: 1.0 = 0x3FF0000000000000
|
||||||
|
if (l.Long1 != 0x3FF0000000000000) return 70;
|
||||||
|
// IEEE 754: 2.0 = 0x4000000000000000
|
||||||
|
if (l.Long2 != 0x4000000000000000) return 71;
|
||||||
|
|
||||||
|
// Test decimal overlap (decimal is 128 bits)
|
||||||
|
l.AsDecimal = 42m;
|
||||||
|
// Just verify it doesn't crash and maintains some structure
|
||||||
|
if (l.AsDecimal != 42m) return 72;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestAutoLayout()
|
||||||
|
{
|
||||||
|
// Auto layout structs can't use FieldOffset, but we can still test basic functionality
|
||||||
|
var a = new AutoLayoutStruct { X = 100, Y = "test", Z = 3.14159 };
|
||||||
|
|
||||||
|
if (a.X != 100) return 80;
|
||||||
|
if (a.Y != "test") return 81;
|
||||||
|
if (Math.Abs(a.Z - 3.14159) > 0.00001) return 82;
|
||||||
|
|
||||||
|
// Test copy
|
||||||
|
var a2 = a;
|
||||||
|
a2.X = 200;
|
||||||
|
if (a.X != 100) return 83; // Original should be unchanged
|
||||||
|
if (a2.X != 200) return 84;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestStructArray()
|
||||||
|
{
|
||||||
|
var arr = new ExplicitUnion[3];
|
||||||
|
arr[0].AsInt = 10;
|
||||||
|
arr[1].AsInt = 20;
|
||||||
|
arr[2].AsInt = 30;
|
||||||
|
|
||||||
|
if (arr[0].AsInt != 10) return 90;
|
||||||
|
if (arr[1].AsInt != 20) return 91;
|
||||||
|
if (arr[2].AsInt != 30) return 92;
|
||||||
|
|
||||||
|
// Modify through float view
|
||||||
|
arr[1].AsFloat = 2.5f;
|
||||||
|
if (Math.Abs(arr[1].AsFloat - 2.5f) > 0.0001f) return 93;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestBoxingUnboxing()
|
||||||
|
{
|
||||||
|
ExplicitUnion u = new ExplicitUnion { AsInt = 999 };
|
||||||
|
object boxed = u; // Box
|
||||||
|
ExplicitUnion unboxed = (ExplicitUnion)boxed; // Unbox
|
||||||
|
|
||||||
|
if (unboxed.AsInt != 999) return 100;
|
||||||
|
|
||||||
|
// Modify original, boxed should remain unchanged
|
||||||
|
u.AsInt = 111;
|
||||||
|
ExplicitUnion fromBoxed = (ExplicitUnion)boxed;
|
||||||
|
if (fromBoxed.AsInt != 999) return 101; // Should still be 999
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TestDefaultValues()
|
||||||
|
{
|
||||||
|
// Test that default struct initialization zeroes memory
|
||||||
|
var s = new SequentialStruct();
|
||||||
|
if (s.A != 0) return 110;
|
||||||
|
if (s.B != 0) return 111;
|
||||||
|
if (s.C != 0) return 112;
|
||||||
|
|
||||||
|
var u = new ExplicitUnion();
|
||||||
|
if (u.AsInt != 0) return 113;
|
||||||
|
if (u.AsFloat != 0.0f) return 114;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Main(string[] argv)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
result = TestExplicitUnion();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestSequentialLayout();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestFixedSizeStruct();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestPackedStruct();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestInstanceFields();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestStructPassing();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestNestedUnion();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestLargeUnion();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestAutoLayout();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestStructArray();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestBoxingUnboxing();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = TestDefaultValues();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
return 0; // All tests passed
|
||||||
|
}
|
||||||
|
}
|
@@ -10,16 +10,16 @@ unsafe public class Program
|
|||||||
|
|
||||||
public struct MediumStruct
|
public struct MediumStruct
|
||||||
{
|
{
|
||||||
public int Value1;
|
public int MediumValue1;
|
||||||
public int Value2;
|
public int MediumValue2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct LargeStruct
|
public struct LargeStruct
|
||||||
{
|
{
|
||||||
public long Value1;
|
public long LongValue1;
|
||||||
public long Value2;
|
public long LongValue2;
|
||||||
public long Value3;
|
public long LongValue3;
|
||||||
public long Value4;
|
public long LongValue4;
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct NestedStruct
|
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;
|
||||||
|
}
|
||||||
|
}
|
184
WoofWare.PawPrint.Test/sourcesPure/StackOperations.cs
Normal file
184
WoofWare.PawPrint.Test/sourcesPure/StackOperations.cs
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
public class TestStackOperations
|
||||||
|
{
|
||||||
|
// Test LdArg0-3: Load method arguments
|
||||||
|
public static int TestLoadArguments(int arg0, int arg1, int arg2, int arg3)
|
||||||
|
{
|
||||||
|
// LdArg0 loads 'this' for instance methods or first arg for static
|
||||||
|
if (arg0 != 10) return 1;
|
||||||
|
|
||||||
|
// LdArg1 loads second argument
|
||||||
|
if (arg1 != 20) return 2;
|
||||||
|
|
||||||
|
// LdArg2 loads third argument
|
||||||
|
if (arg2 != 30) return 3;
|
||||||
|
|
||||||
|
// LdArg3 loads fourth argument
|
||||||
|
if (arg3 != 40) return 4;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Ldloc_0-3 and Stloc_0-3: Load/store local variables
|
||||||
|
public static int TestLocalVariables()
|
||||||
|
{
|
||||||
|
int local0 = 100;
|
||||||
|
int local1 = 200;
|
||||||
|
int local2 = 300;
|
||||||
|
int local3 = 400;
|
||||||
|
|
||||||
|
// Test loading locals
|
||||||
|
if (local0 != 100) return 10;
|
||||||
|
if (local1 != 200) return 11;
|
||||||
|
if (local2 != 300) return 12;
|
||||||
|
if (local3 != 400) return 13;
|
||||||
|
|
||||||
|
// Test storing to locals
|
||||||
|
local0 = local1 + local2; // Stloc_0
|
||||||
|
if (local0 != 500) return 14;
|
||||||
|
|
||||||
|
local1 = local2 * 2; // Stloc_1
|
||||||
|
if (local1 != 600) return 15;
|
||||||
|
|
||||||
|
local2 = local3 - 100; // Stloc_2
|
||||||
|
if (local2 != 300) return 16;
|
||||||
|
|
||||||
|
local3 = local0 / 5; // Stloc_3
|
||||||
|
if (local3 != 100) return 17;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Pop: Remove top stack value
|
||||||
|
public static int TestPop()
|
||||||
|
{
|
||||||
|
int value = 42;
|
||||||
|
|
||||||
|
// Push value on stack then pop it
|
||||||
|
PushAndPop(value);
|
||||||
|
|
||||||
|
// If we get here, pop worked
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PushAndPop(int value)
|
||||||
|
{
|
||||||
|
// The compiler will generate pop instructions
|
||||||
|
// for unused return values
|
||||||
|
GetValue();
|
||||||
|
GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetValue()
|
||||||
|
{
|
||||||
|
return 123;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Dup: Duplicate top stack value
|
||||||
|
public static int TestDup()
|
||||||
|
{
|
||||||
|
int value = 50;
|
||||||
|
|
||||||
|
// Dup is used when same value is needed twice
|
||||||
|
int result1 = value * value; // Compiler may use dup here
|
||||||
|
if (result1 != 2500) return 20;
|
||||||
|
|
||||||
|
// More complex dup scenario
|
||||||
|
int x = 10;
|
||||||
|
int result2 = AddTwice(x);
|
||||||
|
if (result2 != 20) return 21;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int AddTwice(int val)
|
||||||
|
{
|
||||||
|
// Compiler may generate dup to use val twice
|
||||||
|
return val + val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Ret: Return from method
|
||||||
|
public static int TestReturn()
|
||||||
|
{
|
||||||
|
// Test void return
|
||||||
|
VoidReturn();
|
||||||
|
|
||||||
|
// Test value return
|
||||||
|
int result = ValueReturn(5);
|
||||||
|
if (result != 5) return 30;
|
||||||
|
|
||||||
|
// Test early return
|
||||||
|
result = EarlyReturn(true);
|
||||||
|
if (result != 1) return 31;
|
||||||
|
|
||||||
|
result = EarlyReturn(false);
|
||||||
|
if (result != 2) return 32;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void VoidReturn()
|
||||||
|
{
|
||||||
|
// Ret with no value
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ValueReturn(int x)
|
||||||
|
{
|
||||||
|
// Ret with value
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int EarlyReturn(bool condition)
|
||||||
|
{
|
||||||
|
if (condition)
|
||||||
|
return 1; // Early ret
|
||||||
|
|
||||||
|
return 2; // Normal ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test combinations of stack operations
|
||||||
|
public static int TestStackCombinations()
|
||||||
|
{
|
||||||
|
int a = 10, b = 20, c = 30;
|
||||||
|
|
||||||
|
// Complex expression using multiple locals
|
||||||
|
int result = (a + b) * c - (b - a);
|
||||||
|
if (result != 890) return 40;
|
||||||
|
|
||||||
|
// Nested method calls
|
||||||
|
result = Compute(a, Compute(b, c));
|
||||||
|
if (result != 60) return 41;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int Compute(int x, int y)
|
||||||
|
{
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Main(string[] argv)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
result = TestLoadArguments(10, 20, 30, 40);
|
||||||
|
if (result != 0) return 100 + result;
|
||||||
|
|
||||||
|
result = TestLocalVariables();
|
||||||
|
if (result != 0) return 200 + result;
|
||||||
|
|
||||||
|
result = TestPop();
|
||||||
|
if (result != 0) return 300 + result;
|
||||||
|
|
||||||
|
result = TestDup();
|
||||||
|
if (result != 0) return 400 + result;
|
||||||
|
|
||||||
|
result = TestReturn();
|
||||||
|
if (result != 0) return 500 + result;
|
||||||
|
|
||||||
|
result = TestStackCombinations();
|
||||||
|
if (result != 0) return 600 + result;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
276
WoofWare.PawPrint.Test/sourcesPure/UnsafeAs.cs
Normal file
276
WoofWare.PawPrint.Test/sourcesPure/UnsafeAs.cs
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
public class TestUnsafeAs
|
||||||
|
{
|
||||||
|
private struct Int32Wrapper
|
||||||
|
{
|
||||||
|
public int Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct UInt32Wrapper
|
||||||
|
{
|
||||||
|
public uint Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct TwoInt16s
|
||||||
|
{
|
||||||
|
public short First;
|
||||||
|
public short Second;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct FourBytes
|
||||||
|
{
|
||||||
|
public byte B0;
|
||||||
|
public byte B1;
|
||||||
|
public byte B2;
|
||||||
|
public byte B3;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum TestEnum : int
|
||||||
|
{
|
||||||
|
Value1 = 0x12345678,
|
||||||
|
Value2 = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 1: Int32 -> UInt32 reinterpretation
|
||||||
|
public static int Test1()
|
||||||
|
{
|
||||||
|
int original = -1;
|
||||||
|
ref uint reinterpreted = ref Unsafe.As<int, uint>(ref original);
|
||||||
|
|
||||||
|
if (reinterpreted != 0xFFFFFFFF)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
reinterpreted = 0x12345678;
|
||||||
|
if (original != 0x12345678)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
original = int.MinValue;
|
||||||
|
if (reinterpreted != 0x80000000)
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 2: Struct -> Struct reinterpretation
|
||||||
|
public static int Test2()
|
||||||
|
{
|
||||||
|
Int32Wrapper wrapper = new Int32Wrapper { Value = 0x01020304 };
|
||||||
|
ref FourBytes bytes = ref Unsafe.As<Int32Wrapper, FourBytes>(ref wrapper);
|
||||||
|
|
||||||
|
if (BitConverter.IsLittleEndian)
|
||||||
|
{
|
||||||
|
if (bytes.B0 != 0x04) return 10;
|
||||||
|
if (bytes.B1 != 0x03) return 11;
|
||||||
|
if (bytes.B2 != 0x02) return 12;
|
||||||
|
if (bytes.B3 != 0x01) return 13;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (bytes.B0 != 0x01) return 14;
|
||||||
|
if (bytes.B1 != 0x02) return 15;
|
||||||
|
if (bytes.B2 != 0x03) return 16;
|
||||||
|
if (bytes.B3 != 0x04) return 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes.B0 = 0xFF;
|
||||||
|
int expectedValue = BitConverter.IsLittleEndian ? 0x010203FF : unchecked((int)0xFF020304);
|
||||||
|
if (wrapper.Value != expectedValue)
|
||||||
|
return 18;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 3: Int32 -> Two Int16s
|
||||||
|
public static int Test3()
|
||||||
|
{
|
||||||
|
int value = 0x12345678;
|
||||||
|
ref TwoInt16s halves = ref Unsafe.As<int, TwoInt16s>(ref value);
|
||||||
|
|
||||||
|
if (BitConverter.IsLittleEndian)
|
||||||
|
{
|
||||||
|
if (halves.First != unchecked((short)0x5678)) return 20;
|
||||||
|
if (halves.Second != 0x1234) return 21;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (halves.First != 0x1234) return 22;
|
||||||
|
if (halves.Second != unchecked((short)0x5678)) return 23;
|
||||||
|
}
|
||||||
|
|
||||||
|
halves.First = -1;
|
||||||
|
int expectedValue = BitConverter.IsLittleEndian ? 0x1234FFFF : unchecked((int)0xFFFF5678);
|
||||||
|
if (value != expectedValue)
|
||||||
|
return 24;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 4: Array element reinterpretation
|
||||||
|
public static int Test4()
|
||||||
|
{
|
||||||
|
int[] intArray = new int[] { 0x01020304, 0x05060708 };
|
||||||
|
ref uint uintRef = ref Unsafe.As<int, uint>(ref intArray[0]);
|
||||||
|
|
||||||
|
if (uintRef != 0x01020304u)
|
||||||
|
return 30;
|
||||||
|
|
||||||
|
uintRef = 0xAABBCCDD;
|
||||||
|
if (intArray[0] != unchecked((int)0xAABBCCDD))
|
||||||
|
return 31;
|
||||||
|
if (intArray[1] != 0x05060708)
|
||||||
|
return 32;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 5: Bool -> Byte
|
||||||
|
public static int Test5()
|
||||||
|
{
|
||||||
|
bool trueValue = true;
|
||||||
|
bool falseValue = false;
|
||||||
|
|
||||||
|
ref byte trueByte = ref Unsafe.As<bool, byte>(ref trueValue);
|
||||||
|
ref byte falseByte = ref Unsafe.As<bool, byte>(ref falseValue);
|
||||||
|
|
||||||
|
if (trueByte != 1)
|
||||||
|
return 40;
|
||||||
|
if (falseByte != 0)
|
||||||
|
return 41;
|
||||||
|
|
||||||
|
// Modify through byte reference
|
||||||
|
trueByte = 0;
|
||||||
|
if (trueValue != false)
|
||||||
|
return 42;
|
||||||
|
|
||||||
|
falseByte = 1;
|
||||||
|
if (falseValue != true)
|
||||||
|
return 43;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 6: Char -> UInt16
|
||||||
|
public static int Test6()
|
||||||
|
{
|
||||||
|
char ch = 'A';
|
||||||
|
ref ushort asUInt16 = ref Unsafe.As<char, ushort>(ref ch);
|
||||||
|
|
||||||
|
if (asUInt16 != 65)
|
||||||
|
return 50;
|
||||||
|
|
||||||
|
asUInt16 = 0x03B1; // Greek lowercase alpha
|
||||||
|
if (ch != 'α')
|
||||||
|
return 51;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 7: Float -> Int32
|
||||||
|
public static int Test7()
|
||||||
|
{
|
||||||
|
float floatValue = 1.0f;
|
||||||
|
ref int intBits = ref Unsafe.As<float, int>(ref floatValue);
|
||||||
|
|
||||||
|
// IEEE 754: 1.0f = 0x3F800000
|
||||||
|
if (intBits != 0x3F800000)
|
||||||
|
return 60;
|
||||||
|
|
||||||
|
intBits = 0x40000000; // 2.0f in IEEE 754
|
||||||
|
if (floatValue != 2.0f)
|
||||||
|
return 61;
|
||||||
|
|
||||||
|
floatValue = -0.0f;
|
||||||
|
if (intBits != unchecked((int)0x80000000))
|
||||||
|
return 62;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 8: Double -> Int64
|
||||||
|
public static int Test8()
|
||||||
|
{
|
||||||
|
double doubleValue = 1.0;
|
||||||
|
ref long longBits = ref Unsafe.As<double, long>(ref doubleValue);
|
||||||
|
|
||||||
|
// IEEE 754: 1.0 = 0x3FF0000000000000
|
||||||
|
if (longBits != 0x3FF0000000000000L)
|
||||||
|
return 70;
|
||||||
|
|
||||||
|
longBits = 0x4000000000000000L; // 2.0 in IEEE 754
|
||||||
|
if (doubleValue != 2.0)
|
||||||
|
return 71;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 9: Enum -> Underlying type
|
||||||
|
public static int Test9()
|
||||||
|
{
|
||||||
|
TestEnum enumValue = TestEnum.Value1;
|
||||||
|
ref int underlying = ref Unsafe.As<TestEnum, int>(ref enumValue);
|
||||||
|
|
||||||
|
if (underlying != 0x12345678)
|
||||||
|
return 80;
|
||||||
|
|
||||||
|
underlying = -1;
|
||||||
|
if (enumValue != TestEnum.Value2)
|
||||||
|
return 81;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 10: Local variable reinterpretation
|
||||||
|
public static int Test10()
|
||||||
|
{
|
||||||
|
int local = unchecked((int)0xDEADBEEF);
|
||||||
|
ref uint localAsUint = ref Unsafe.As<int, uint>(ref local);
|
||||||
|
|
||||||
|
if (localAsUint != 0xDEADBEEF)
|
||||||
|
return 90;
|
||||||
|
|
||||||
|
localAsUint = 0xCAFEBABE;
|
||||||
|
if (local != unchecked((int)0xCAFEBABE))
|
||||||
|
return 91;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Main(string[] argv)
|
||||||
|
{
|
||||||
|
var result = Test1();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test2();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test3();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test4();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test5();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test6();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test7();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test8();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test9();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
result = Test10();
|
||||||
|
if (result != 0) return result;
|
||||||
|
|
||||||
|
// All tests passed
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -1,6 +1,5 @@
|
|||||||
namespace WoofWare.PawPrint
|
namespace WoofWare.PawPrint
|
||||||
|
|
||||||
open System.Collections.Immutable
|
|
||||||
open Microsoft.Extensions.Logging
|
open Microsoft.Extensions.Logging
|
||||||
open Microsoft.FSharp.Core
|
open Microsoft.FSharp.Core
|
||||||
open WoofWare.PawPrint.ExternImplementations
|
open WoofWare.PawPrint.ExternImplementations
|
||||||
@@ -43,7 +42,7 @@ module AbstractMachine =
|
|||||||
| Some {
|
| Some {
|
||||||
WasConstructingObj = Some _
|
WasConstructingObj = Some _
|
||||||
} ->
|
} ->
|
||||||
IlMachineState.executeDelegateConstructor instruction state
|
IlMachineState.executeDelegateConstructor baseClassTypes instruction state
|
||||||
// can't advance the program counter here - there's no IL instructions executing!
|
// can't advance the program counter here - there's no IL instructions executing!
|
||||||
|> IlMachineState.returnStackFrame loggerFactory baseClassTypes thread
|
|> IlMachineState.returnStackFrame loggerFactory baseClassTypes thread
|
||||||
|> Option.get
|
|> Option.get
|
||||||
@@ -55,18 +54,19 @@ module AbstractMachine =
|
|||||||
// We've been instructed to run a delegate.
|
// We've been instructed to run a delegate.
|
||||||
let delegateToRunAddr =
|
let delegateToRunAddr =
|
||||||
match instruction.Arguments.[0] with
|
match instruction.Arguments.[0] with
|
||||||
|
| CliType.RuntimePointer (CliRuntimePointer.Managed (ManagedPointerSource.Heap addr))
|
||||||
| CliType.ObjectRef (Some addr) -> addr
|
| CliType.ObjectRef (Some addr) -> addr
|
||||||
| _ -> failwith "expected a managed object ref to delegate"
|
| _ -> failwith "expected a managed object ref to delegate"
|
||||||
|
|
||||||
let delegateToRun = state.ManagedHeap.NonArrayObjects.[delegateToRunAddr]
|
let delegateToRun = state.ManagedHeap.NonArrayObjects.[delegateToRunAddr]
|
||||||
|
|
||||||
let target =
|
let target =
|
||||||
match delegateToRun.Fields.["_target"] with
|
match delegateToRun |> AllocatedNonArrayObject.DereferenceField "_target" with
|
||||||
| CliType.ObjectRef addr -> addr
|
| CliType.ObjectRef addr -> addr
|
||||||
| x -> failwith $"TODO: delegate target wasn't an object ref: %O{x}"
|
| x -> failwith $"TODO: delegate target wasn't an object ref: %O{x}"
|
||||||
|
|
||||||
let methodPtr =
|
let methodPtr =
|
||||||
match delegateToRun.Fields.["_methodPtr"] with
|
match delegateToRun |> AllocatedNonArrayObject.DereferenceField "_methodPtr" with
|
||||||
| CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.FunctionPointer mi)) -> mi
|
| CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.FunctionPointer mi)) -> mi
|
||||||
| d -> failwith $"unexpectedly not a method pointer in delegate invocation: {d}"
|
| d -> failwith $"unexpectedly not a method pointer in delegate invocation: {d}"
|
||||||
|
|
||||||
@@ -222,5 +222,5 @@ module AbstractMachine =
|
|||||||
|> ExecutionResult.Stepped
|
|> ExecutionResult.Stepped
|
||||||
| IlOp.Switch immutableArray -> failwith "TODO: Switch unimplemented"
|
| IlOp.Switch immutableArray -> failwith "TODO: Switch unimplemented"
|
||||||
| IlOp.UnaryStringToken (unaryStringTokenIlOp, stringHandle) ->
|
| IlOp.UnaryStringToken (unaryStringTokenIlOp, stringHandle) ->
|
||||||
UnaryStringTokenIlOp.execute baseClassTypes unaryStringTokenIlOp stringHandle state thread
|
UnaryStringTokenIlOp.execute loggerFactory baseClassTypes unaryStringTokenIlOp stringHandle state thread
|
||||||
|> ExecutionResult.Stepped
|
|> ExecutionResult.Stepped
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
namespace WoofWare.PawPrint
|
namespace WoofWare.PawPrint
|
||||||
|
|
||||||
|
open System
|
||||||
open System.Collections.Immutable
|
open System.Collections.Immutable
|
||||||
open System.Reflection
|
open System.Reflection
|
||||||
open System.Reflection.Metadata
|
open Checked
|
||||||
|
|
||||||
/// Source:
|
/// Source:
|
||||||
/// Table I.6: Data Types Directly Supported by the CLI
|
/// Table I.6: Data Types Directly Supported by the CLI
|
||||||
@@ -41,7 +42,9 @@ type ManagedPointerSource =
|
|||||||
| Argument of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
|
| Argument of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
|
||||||
| Heap of ManagedHeapAddress
|
| Heap of ManagedHeapAddress
|
||||||
| ArrayIndex of arr : ManagedHeapAddress * index : int
|
| ArrayIndex of arr : ManagedHeapAddress * index : int
|
||||||
|
| Field of ManagedPointerSource * fieldName : string
|
||||||
| Null
|
| Null
|
||||||
|
| InterpretedAsType of ManagedPointerSource * ConcreteType<ConcreteTypeHandle>
|
||||||
|
|
||||||
override this.ToString () =
|
override this.ToString () =
|
||||||
match this with
|
match this with
|
||||||
@@ -52,6 +55,8 @@ type ManagedPointerSource =
|
|||||||
| ManagedPointerSource.Argument (source, method, var) ->
|
| ManagedPointerSource.Argument (source, method, var) ->
|
||||||
$"<argument %i{var} in method frame %i{method} of thread %O{source}>"
|
$"<argument %i{var} in method frame %i{method} of thread %O{source}>"
|
||||||
| ManagedPointerSource.ArrayIndex (arr, index) -> $"<index %i{index} of array %O{arr}>"
|
| ManagedPointerSource.ArrayIndex (arr, index) -> $"<index %i{index} of array %O{arr}>"
|
||||||
|
| ManagedPointerSource.Field (source, name) -> $"<field %s{name} of %O{source}>"
|
||||||
|
| ManagedPointerSource.InterpretedAsType (src, ty) -> $"<%O{src} as %s{ty.Namespace}.%s{ty.Name}>"
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
type UnsignedNativeIntSource =
|
type UnsignedNativeIntSource =
|
||||||
@@ -64,6 +69,7 @@ type NativeIntSource =
|
|||||||
| ManagedPointer of ManagedPointerSource
|
| ManagedPointer of ManagedPointerSource
|
||||||
| FunctionPointer of MethodInfo<ConcreteTypeHandle, ConcreteTypeHandle, ConcreteTypeHandle>
|
| FunctionPointer of MethodInfo<ConcreteTypeHandle, ConcreteTypeHandle, ConcreteTypeHandle>
|
||||||
| TypeHandlePtr of ConcreteTypeHandle
|
| TypeHandlePtr of ConcreteTypeHandle
|
||||||
|
| FieldHandlePtr of int64
|
||||||
|
|
||||||
override this.ToString () : string =
|
override this.ToString () : string =
|
||||||
match this with
|
match this with
|
||||||
@@ -72,12 +78,14 @@ type NativeIntSource =
|
|||||||
| NativeIntSource.FunctionPointer methodDefinition ->
|
| NativeIntSource.FunctionPointer methodDefinition ->
|
||||||
$"<pointer to {methodDefinition.Name} in {methodDefinition.DeclaringType.Assembly.Name}>"
|
$"<pointer to {methodDefinition.Name} in {methodDefinition.DeclaringType.Assembly.Name}>"
|
||||||
| NativeIntSource.TypeHandlePtr ptr -> $"<type ID %O{ptr}>"
|
| NativeIntSource.TypeHandlePtr ptr -> $"<type ID %O{ptr}>"
|
||||||
|
| NativeIntSource.FieldHandlePtr ptr -> $"<field ID %O{ptr}>"
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module NativeIntSource =
|
module NativeIntSource =
|
||||||
let isZero (n : NativeIntSource) : bool =
|
let isZero (n : NativeIntSource) : bool =
|
||||||
match n with
|
match n with
|
||||||
| NativeIntSource.Verbatim i -> i = 0L
|
| NativeIntSource.Verbatim i -> i = 0L
|
||||||
|
| NativeIntSource.FieldHandlePtr _
|
||||||
| NativeIntSource.TypeHandlePtr _ -> false
|
| NativeIntSource.TypeHandlePtr _ -> false
|
||||||
| NativeIntSource.FunctionPointer _ -> failwith "TODO"
|
| NativeIntSource.FunctionPointer _ -> failwith "TODO"
|
||||||
| NativeIntSource.ManagedPointer src ->
|
| NativeIntSource.ManagedPointer src ->
|
||||||
@@ -89,6 +97,7 @@ module NativeIntSource =
|
|||||||
match n with
|
match n with
|
||||||
| NativeIntSource.Verbatim i -> i >= 0L
|
| NativeIntSource.Verbatim i -> i >= 0L
|
||||||
| NativeIntSource.FunctionPointer _ -> failwith "TODO"
|
| NativeIntSource.FunctionPointer _ -> failwith "TODO"
|
||||||
|
| NativeIntSource.FieldHandlePtr _
|
||||||
| NativeIntSource.TypeHandlePtr _ -> true
|
| NativeIntSource.TypeHandlePtr _ -> true
|
||||||
| NativeIntSource.ManagedPointer _ -> true
|
| NativeIntSource.ManagedPointer _ -> true
|
||||||
|
|
||||||
@@ -113,17 +122,51 @@ type CliNumericType =
|
|||||||
| Float32 of float32
|
| Float32 of float32
|
||||||
| Float64 of float
|
| Float64 of float
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
static member SizeOf (t : CliNumericType) : int =
|
||||||
type CliRuntimePointerSource =
|
match t with
|
||||||
| LocalVariable of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
|
| CliNumericType.Int32 _ -> 4
|
||||||
| Argument of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
|
| CliNumericType.Int64 _ -> 8
|
||||||
| Heap of ManagedHeapAddress
|
| CliNumericType.NativeInt _ -> 8
|
||||||
| ArrayIndex of arr : ManagedHeapAddress * index : int
|
| CliNumericType.NativeFloat _ -> 8
|
||||||
| Null
|
| CliNumericType.Int8 _ -> 1
|
||||||
|
| CliNumericType.Int16 _ -> 2
|
||||||
|
| CliNumericType.UInt8 _ -> 1
|
||||||
|
| CliNumericType.UInt16 _ -> 2
|
||||||
|
| CliNumericType.Float32 _ -> 4
|
||||||
|
| CliNumericType.Float64 _ -> 8
|
||||||
|
|
||||||
|
static member ToBytes (t : CliNumericType) : byte[] =
|
||||||
|
match t with
|
||||||
|
| CliNumericType.Int32 i -> BitConverter.GetBytes i
|
||||||
|
| CliNumericType.Int64 i -> BitConverter.GetBytes i
|
||||||
|
| CliNumericType.NativeInt src ->
|
||||||
|
match src with
|
||||||
|
| NativeIntSource.Verbatim i -> BitConverter.GetBytes i
|
||||||
|
| NativeIntSource.ManagedPointer src ->
|
||||||
|
match src with
|
||||||
|
| ManagedPointerSource.Null -> BitConverter.GetBytes 0L
|
||||||
|
| _ -> failwith "refusing to express pointer as bytes"
|
||||||
|
| NativeIntSource.FieldHandlePtr _ -> failwith "refusing to express FieldHandlePtr as bytes"
|
||||||
|
| NativeIntSource.FunctionPointer _ -> failwith "refusing to express FunctionPointer as bytes"
|
||||||
|
| NativeIntSource.TypeHandlePtr _ -> failwith "refusing to express TypeHandlePtr as bytes"
|
||||||
|
| CliNumericType.NativeFloat f -> BitConverter.GetBytes f
|
||||||
|
| CliNumericType.Int8 i -> BitConverter.GetBytes i
|
||||||
|
| CliNumericType.Int16 i -> BitConverter.GetBytes i
|
||||||
|
| CliNumericType.UInt8 i -> BitConverter.GetBytes i
|
||||||
|
| CliNumericType.UInt16 i -> BitConverter.GetBytes i
|
||||||
|
| CliNumericType.Float32 i -> BitConverter.GetBytes i
|
||||||
|
| CliNumericType.Float64 i -> BitConverter.GetBytes i
|
||||||
|
|
||||||
type CliRuntimePointer =
|
type CliRuntimePointer =
|
||||||
| Unmanaged of int64
|
| Verbatim of int64
|
||||||
| Managed of CliRuntimePointerSource
|
| FieldRegistryHandle 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.
|
/// This is the kind of type that can be stored in arguments, local variables, statics, array elements, fields.
|
||||||
type CliType =
|
type CliType =
|
||||||
@@ -141,11 +184,350 @@ type CliType =
|
|||||||
/// as a concatenated list of its fields.
|
/// as a concatenated list of its fields.
|
||||||
| ValueType of CliValueType
|
| ValueType of CliValueType
|
||||||
|
|
||||||
and CliValueType =
|
static member SizeOf (t : CliType) : SizeofResult =
|
||||||
|
match t with
|
||||||
|
| 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
|
||||||
|
|
||||||
|
static member ToBytes (t : CliType) : byte[] =
|
||||||
|
match t with
|
||||||
|
| CliType.Numeric n -> CliNumericType.ToBytes n
|
||||||
|
| CliType.Bool b -> [| b |]
|
||||||
|
| CliType.Char (high, low) -> [| low ; high |]
|
||||||
|
| CliType.ObjectRef None -> Array.zeroCreate NATIVE_INT_SIZE
|
||||||
|
| CliType.ObjectRef (Some i) -> failwith "todo"
|
||||||
|
| CliType.RuntimePointer cliRuntimePointer -> failwith "todo"
|
||||||
|
| CliType.ValueType cvt -> CliValueType.ToBytes cvt
|
||||||
|
|
||||||
|
static member OfBytesAsType (targetType : ConcreteTypeHandle) (bytes : byte[]) : CliType = failwith "TODO"
|
||||||
|
|
||||||
|
and CliField =
|
||||||
{
|
{
|
||||||
Fields : (string * CliType) list
|
Name : string
|
||||||
|
Contents : CliType
|
||||||
|
/// "None" for "no explicit offset specified"; we expect most offsets to be None.
|
||||||
|
Offset : int option
|
||||||
|
Type : ConcreteTypeHandle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
and CliConcreteField =
|
||||||
|
private
|
||||||
|
{
|
||||||
|
Name : string
|
||||||
|
Contents : CliType
|
||||||
|
Offset : int
|
||||||
|
Size : int
|
||||||
|
Alignment : int
|
||||||
|
ConfiguredOffset : int option
|
||||||
|
EditedAtTime : uint64
|
||||||
|
Type : ConcreteTypeHandle
|
||||||
|
}
|
||||||
|
|
||||||
|
static member ToCliField (this : CliConcreteField) : CliField =
|
||||||
|
{
|
||||||
|
Offset = this.ConfiguredOffset
|
||||||
|
Contents = this.Contents
|
||||||
|
Name = this.Name
|
||||||
|
Type = this.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
and CliValueType =
|
||||||
|
private
|
||||||
|
{
|
||||||
|
_Fields : CliConcreteField list
|
||||||
|
Layout : Layout
|
||||||
|
/// We track dependency orderings between updates to overlapping fields with a monotonically increasing
|
||||||
|
/// timestamp.
|
||||||
|
NextTimestamp : uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
static member private ComputeConcreteFields (layout : Layout) (fields : CliField list) : CliConcreteField list =
|
||||||
|
// Minimum size only matters for `sizeof` computation
|
||||||
|
let _minimumSize, packingSize =
|
||||||
|
match layout with
|
||||||
|
| Layout.Custom (size = size ; packingSize = packing) ->
|
||||||
|
size, if packing = 0 then DEFAULT_STRUCT_ALIGNMENT else packing
|
||||||
|
| Layout.Default -> 0, DEFAULT_STRUCT_ALIGNMENT
|
||||||
|
|
||||||
|
let seqFields, nonSeqFields =
|
||||||
|
fields |> List.partition (fun field -> field.Offset.IsNone)
|
||||||
|
|
||||||
|
match seqFields, nonSeqFields with
|
||||||
|
| [], [] -> []
|
||||||
|
| _ :: _, [] ->
|
||||||
|
// Sequential layout: compute offsets respecting alignment
|
||||||
|
let _, concreteFields =
|
||||||
|
((0, []), seqFields)
|
||||||
|
||> List.fold (fun (currentOffset, acc) field ->
|
||||||
|
let size = CliType.SizeOf field.Contents
|
||||||
|
let alignmentCap = min size.Alignment packingSize
|
||||||
|
let error = currentOffset % alignmentCap
|
||||||
|
|
||||||
|
let alignedOffset =
|
||||||
|
if error > 0 then
|
||||||
|
currentOffset + (alignmentCap - error)
|
||||||
|
else
|
||||||
|
currentOffset
|
||||||
|
|
||||||
|
let concreteField =
|
||||||
|
{
|
||||||
|
Name = field.Name
|
||||||
|
Contents = field.Contents
|
||||||
|
Offset = alignedOffset
|
||||||
|
Size = size.Size
|
||||||
|
Alignment = size.Alignment
|
||||||
|
ConfiguredOffset = field.Offset
|
||||||
|
EditedAtTime = 0UL
|
||||||
|
Type = field.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
alignedOffset + size.Size, concreteField :: acc
|
||||||
|
)
|
||||||
|
|
||||||
|
List.rev concreteFields
|
||||||
|
|
||||||
|
| [], _ :: _ ->
|
||||||
|
// Explicit layout: use provided offsets
|
||||||
|
nonSeqFields
|
||||||
|
|> List.map (fun field ->
|
||||||
|
let size = CliType.SizeOf field.Contents
|
||||||
|
|
||||||
|
{
|
||||||
|
Name = field.Name
|
||||||
|
Contents = field.Contents
|
||||||
|
Offset = field.Offset.Value
|
||||||
|
Size = size.Size
|
||||||
|
Alignment = size.Alignment
|
||||||
|
ConfiguredOffset = field.Offset
|
||||||
|
EditedAtTime = 0UL
|
||||||
|
Type = field.Type
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
| _ :: _, _ :: _ -> failwith "unexpectedly mixed explicit and automatic layout of fields"
|
||||||
|
|
||||||
|
static member ToBytes (cvt : CliValueType) : byte[] =
|
||||||
|
let bytes = Array.zeroCreate<byte> (CliValueType.SizeOf(cvt).Size)
|
||||||
|
|
||||||
|
cvt._Fields
|
||||||
|
|> List.sortBy _.EditedAtTime
|
||||||
|
|> List.iter (fun candidateField ->
|
||||||
|
let fieldBytes : byte[] = CliType.ToBytes candidateField.Contents
|
||||||
|
|
||||||
|
for i = 0 to candidateField.Size - 1 do
|
||||||
|
bytes.[candidateField.Offset + i] <- fieldBytes.[i]
|
||||||
|
)
|
||||||
|
|
||||||
|
bytes
|
||||||
|
|
||||||
|
static member OfFields (layout : Layout) (f : CliField list) : CliValueType =
|
||||||
|
let fields = CliValueType.ComputeConcreteFields layout f
|
||||||
|
|
||||||
|
{
|
||||||
|
_Fields = fields
|
||||||
|
Layout = layout
|
||||||
|
NextTimestamp = 1UL
|
||||||
|
}
|
||||||
|
|
||||||
|
static member AddField (f : CliField) (vt : CliValueType) : CliValueType =
|
||||||
|
// Recompute all fields with the new one added
|
||||||
|
// TODO: the existence of this function at all is rather dubious, but it's there
|
||||||
|
// at the moment to support delegate types.
|
||||||
|
// The whole function is just a bodge and it will hopefully go away soon; I just don't know how.
|
||||||
|
let prevFields = vt._Fields |> List.map (fun f -> f.Name, f) |> Map.ofList
|
||||||
|
|
||||||
|
let allFields =
|
||||||
|
f
|
||||||
|
:: (vt._Fields
|
||||||
|
|> List.map (fun cf ->
|
||||||
|
{
|
||||||
|
Name = cf.Name
|
||||||
|
Contents = cf.Contents
|
||||||
|
Offset =
|
||||||
|
match vt.Layout with
|
||||||
|
| Layout.Default -> None
|
||||||
|
| Layout.Custom _ -> Some cf.Offset
|
||||||
|
Type = cf.Type
|
||||||
|
}
|
||||||
|
))
|
||||||
|
|
||||||
|
let newFields =
|
||||||
|
CliValueType.ComputeConcreteFields vt.Layout allFields
|
||||||
|
|> List.map (fun field ->
|
||||||
|
match Map.tryFind field.Name prevFields with
|
||||||
|
| Some prev ->
|
||||||
|
{ field with
|
||||||
|
EditedAtTime = prev.EditedAtTime
|
||||||
|
}
|
||||||
|
| None ->
|
||||||
|
{ field with
|
||||||
|
EditedAtTime = vt.NextTimestamp
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
{
|
||||||
|
_Fields = newFields
|
||||||
|
Layout = vt.Layout
|
||||||
|
NextTimestamp = vt.NextTimestamp + 1UL
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the offset and size.
|
||||||
|
static member GetFieldLayout (field : string) (cvt : CliValueType) : int * int =
|
||||||
|
let targetField =
|
||||||
|
cvt._Fields
|
||||||
|
|> List.tryFind (fun f -> f.Name = field)
|
||||||
|
|> Option.defaultWith (fun () -> failwithf $"Field '%s{field}' not found")
|
||||||
|
|
||||||
|
targetField.Offset, targetField.Size
|
||||||
|
|
||||||
|
// TODO: use DereferenceFieldAt for the implementation.
|
||||||
|
// We should eventually be able to dereference an arbitrary field of a struct
|
||||||
|
// as though it were any other field of any other type, to accommodate Unsafe.As.
|
||||||
|
static member DereferenceField (field : string) (cvt : CliValueType) : CliType =
|
||||||
|
let targetField =
|
||||||
|
cvt._Fields
|
||||||
|
|> List.tryFind (fun f -> f.Name = field)
|
||||||
|
|> Option.defaultWith (fun () -> failwithf $"Field '%s{field}' not found")
|
||||||
|
|
||||||
|
// Identify all fields that overlap with the target field's memory range
|
||||||
|
let targetStart = targetField.Offset
|
||||||
|
let targetEnd = targetField.Offset + targetField.Size
|
||||||
|
|
||||||
|
let affectedFields =
|
||||||
|
cvt._Fields
|
||||||
|
|> List.filter (fun f ->
|
||||||
|
let fieldStart = f.Offset
|
||||||
|
let fieldEnd = f.Offset + f.Size
|
||||||
|
// Fields overlap if their ranges intersect
|
||||||
|
fieldStart < targetEnd && targetStart < fieldEnd
|
||||||
|
)
|
||||||
|
|
||||||
|
match affectedFields with
|
||||||
|
| [] -> failwith "unexpectedly didn't dereference a field"
|
||||||
|
| [ f ] -> f.Contents
|
||||||
|
| fields ->
|
||||||
|
let bytes = CliValueType.ToBytes cvt
|
||||||
|
|
||||||
|
let fieldBytes =
|
||||||
|
bytes.[targetField.Offset .. targetField.Offset + targetField.Size - 1]
|
||||||
|
|
||||||
|
CliType.OfBytesAsType targetField.Type fieldBytes
|
||||||
|
|
||||||
|
static member FieldsAt (offset : int) (cvt : CliValueType) : CliConcreteField list =
|
||||||
|
cvt._Fields |> List.filter (fun f -> f.Offset = offset)
|
||||||
|
|
||||||
|
static member DereferenceFieldAt (offset : int) (size : int) (cvt : CliValueType) : CliType =
|
||||||
|
let targetField =
|
||||||
|
CliValueType.FieldsAt offset cvt |> List.tryFind (fun f -> f.Size = size)
|
||||||
|
|
||||||
|
match targetField with
|
||||||
|
| None -> failwith "TODO: couldn't find the field"
|
||||||
|
| Some f -> f.Contents
|
||||||
|
|
||||||
|
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
|
||||||
|
// Now we can just use the precomputed offsets and sizes
|
||||||
|
let finalOffset, alignment =
|
||||||
|
vt._Fields
|
||||||
|
|> List.fold
|
||||||
|
(fun (maxEnd, maxAlign) field ->
|
||||||
|
let fieldEnd = field.Offset + field.Size
|
||||||
|
let alignmentCap = min field.Alignment packingSize
|
||||||
|
max maxEnd fieldEnd, max maxAlign alignmentCap
|
||||||
|
)
|
||||||
|
(0, 0)
|
||||||
|
|
||||||
|
let error = finalOffset % alignment
|
||||||
|
|
||||||
|
let size =
|
||||||
|
if error = 0 then
|
||||||
|
finalOffset
|
||||||
|
else
|
||||||
|
finalOffset + (alignment - error)
|
||||||
|
|
||||||
|
{
|
||||||
|
Size = max size minimumSize
|
||||||
|
Alignment = alignment
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the value of the specified field, *without* touching any overlapping fields.
|
||||||
|
/// `DereferenceField` handles resolving conflicts between overlapping fields.
|
||||||
|
static member WithFieldSet (field : string) (value : CliType) (cvt : CliValueType) : CliValueType =
|
||||||
|
{
|
||||||
|
Layout = cvt.Layout
|
||||||
|
_Fields =
|
||||||
|
cvt._Fields
|
||||||
|
|> List.replaceWhere (fun f ->
|
||||||
|
if f.Name = field then
|
||||||
|
{ f with
|
||||||
|
Contents = value
|
||||||
|
EditedAtTime = cvt.NextTimestamp
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
else
|
||||||
|
None
|
||||||
|
)
|
||||||
|
NextTimestamp = cvt.NextTimestamp + 1UL
|
||||||
|
}
|
||||||
|
|
||||||
|
/// To facilitate bodges. This function absolutely should not exist.
|
||||||
|
static member TryExactlyOneField (cvt : CliValueType) : CliField option =
|
||||||
|
match cvt._Fields with
|
||||||
|
| [] -> None
|
||||||
|
| [ x ] ->
|
||||||
|
if x.Offset = 0 then
|
||||||
|
Some (CliConcreteField.ToCliField x)
|
||||||
|
else
|
||||||
|
None
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
|
/// To facilitate bodges. This function absolutely should not exist.
|
||||||
|
static member TrySequentialFields (cvt : CliValueType) : CliField list option =
|
||||||
|
let isNone, isSome =
|
||||||
|
cvt._Fields |> List.partition (fun field -> field.ConfiguredOffset.IsNone)
|
||||||
|
|
||||||
|
match isSome with
|
||||||
|
| [] -> Some (isNone |> List.map CliConcreteField.ToCliField)
|
||||||
|
| [ field ] when field.ConfiguredOffset = Some 0 -> Some [ CliConcreteField.ToCliField field ]
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
type CliTypeResolutionResult =
|
type CliTypeResolutionResult =
|
||||||
| Resolved of CliType
|
| Resolved of CliType
|
||||||
| FirstLoad of WoofWare.PawPrint.AssemblyReference
|
| FirstLoad of WoofWare.PawPrint.AssemblyReference
|
||||||
@@ -160,31 +542,14 @@ module CliType =
|
|||||||
|
|
||||||
let ofManagedObject (ptr : ManagedHeapAddress) : CliType = CliType.ObjectRef (Some ptr)
|
let ofManagedObject (ptr : ManagedHeapAddress) : CliType = CliType.ObjectRef (Some ptr)
|
||||||
|
|
||||||
let rec sizeOf (ty : CliType) : int =
|
let sizeOf (ty : CliType) : int = CliType.SizeOf(ty).Size
|
||||||
match ty with
|
|
||||||
| CliType.Numeric ty ->
|
|
||||||
match ty with
|
|
||||||
| CliNumericType.Int32 _ -> 4
|
|
||||||
| CliNumericType.Int64 _ -> 8
|
|
||||||
| CliNumericType.NativeInt _ -> 8
|
|
||||||
| CliNumericType.NativeFloat _ -> 8
|
|
||||||
| CliNumericType.Int8 _ -> 1
|
|
||||||
| CliNumericType.Int16 _ -> 2
|
|
||||||
| CliNumericType.UInt8 _ -> 1
|
|
||||||
| CliNumericType.UInt16 _ -> 2
|
|
||||||
| CliNumericType.Float32 _ -> 4
|
|
||||||
| CliNumericType.Float64 _ -> 8
|
|
||||||
| CliType.Bool _ -> 1
|
|
||||||
| CliType.Char _ -> 2
|
|
||||||
| CliType.ObjectRef _ -> 8
|
|
||||||
| CliType.RuntimePointer _ -> 8
|
|
||||||
| CliType.ValueType vt ->
|
|
||||||
match vt.Fields with
|
|
||||||
| [] -> failwith "is it even possible to instantiate a value type with no fields"
|
|
||||||
| [ _, f ] -> sizeOf f
|
|
||||||
| _ -> failwith $"TODO: %O{vt.Fields} (need to consider struct layout)"
|
|
||||||
|
|
||||||
let zeroOfPrimitive (primitiveType : PrimitiveType) : CliType =
|
let zeroOfPrimitive
|
||||||
|
(concreteTypes : AllConcreteTypes)
|
||||||
|
(corelib : BaseClassTypes<DumpedAssembly>)
|
||||||
|
(primitiveType : PrimitiveType)
|
||||||
|
: CliType
|
||||||
|
=
|
||||||
match primitiveType with
|
match primitiveType with
|
||||||
| PrimitiveType.Boolean -> CliType.Bool 0uy
|
| PrimitiveType.Boolean -> CliType.Bool 0uy
|
||||||
| PrimitiveType.Char -> CliType.Char (0uy, 0uy)
|
| PrimitiveType.Char -> CliType.Char (0uy, 0uy)
|
||||||
@@ -204,8 +569,40 @@ module CliType =
|
|||||||
| PrimitiveType.Double -> CliType.Numeric (CliNumericType.Float64 0.0)
|
| PrimitiveType.Double -> CliType.Numeric (CliNumericType.Float64 0.0)
|
||||||
| PrimitiveType.String -> CliType.ObjectRef None
|
| PrimitiveType.String -> CliType.ObjectRef None
|
||||||
| PrimitiveType.TypedReference -> failwith "todo"
|
| PrimitiveType.TypedReference -> failwith "todo"
|
||||||
| PrimitiveType.IntPtr -> CliType.RuntimePointer (CliRuntimePointer.Managed CliRuntimePointerSource.Null)
|
| PrimitiveType.IntPtr ->
|
||||||
| PrimitiveType.UIntPtr -> CliType.RuntimePointer (CliRuntimePointer.Managed CliRuntimePointerSource.Null)
|
{
|
||||||
|
Name = "_value"
|
||||||
|
Contents =
|
||||||
|
CliType.Numeric (
|
||||||
|
CliNumericType.NativeInt (NativeIntSource.ManagedPointer ManagedPointerSource.Null)
|
||||||
|
)
|
||||||
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
concreteTypes
|
||||||
|
(corelib.IntPtr.Assembly, corelib.IntPtr.Namespace, corelib.IntPtr.Name, ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
|
}
|
||||||
|
|> List.singleton
|
||||||
|
|> CliValueType.OfFields Layout.Default
|
||||||
|
|> CliType.ValueType
|
||||||
|
| PrimitiveType.UIntPtr ->
|
||||||
|
{
|
||||||
|
Name = "_value"
|
||||||
|
Contents =
|
||||||
|
CliType.Numeric (
|
||||||
|
CliNumericType.NativeInt (NativeIntSource.ManagedPointer ManagedPointerSource.Null)
|
||||||
|
)
|
||||||
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
concreteTypes
|
||||||
|
(corelib.UIntPtr.Assembly, corelib.UIntPtr.Namespace, corelib.UIntPtr.Name, ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
|
}
|
||||||
|
|> List.singleton
|
||||||
|
|> CliValueType.OfFields Layout.Default
|
||||||
|
|> CliType.ValueType
|
||||||
| PrimitiveType.Object -> CliType.ObjectRef None
|
| PrimitiveType.Object -> CliType.ObjectRef None
|
||||||
|
|
||||||
let rec zeroOf
|
let rec zeroOf
|
||||||
@@ -230,11 +627,11 @@ module CliType =
|
|||||||
match handle with
|
match handle with
|
||||||
| ConcreteTypeHandle.Byref _ ->
|
| ConcreteTypeHandle.Byref _ ->
|
||||||
// Byref types are managed references - the zero value is a null reference
|
// Byref types are managed references - the zero value is a null reference
|
||||||
CliType.RuntimePointer (CliRuntimePointer.Managed CliRuntimePointerSource.Null), concreteTypes
|
CliType.RuntimePointer (CliRuntimePointer.Managed ManagedPointerSource.Null), concreteTypes
|
||||||
|
|
||||||
| ConcreteTypeHandle.Pointer _ ->
|
| ConcreteTypeHandle.Pointer _ ->
|
||||||
// Pointer types are unmanaged pointers - the zero value is a null pointer
|
// Pointer types are unmanaged pointers - the zero value is a null pointer
|
||||||
CliType.RuntimePointer (CliRuntimePointer.Unmanaged 0L), concreteTypes
|
CliType.RuntimePointer (CliRuntimePointer.Managed ManagedPointerSource.Null), concreteTypes
|
||||||
|
|
||||||
| ConcreteTypeHandle.Concrete _ ->
|
| ConcreteTypeHandle.Concrete _ ->
|
||||||
// This is a concrete type - look it up in the mapping
|
// This is a concrete type - look it up in the mapping
|
||||||
@@ -248,72 +645,82 @@ module CliType =
|
|||||||
let typeDef = assembly.TypeDefs.[concreteType.Definition.Get]
|
let typeDef = assembly.TypeDefs.[concreteType.Definition.Get]
|
||||||
|
|
||||||
// Check if it's a primitive type by comparing with corelib types FIRST
|
// Check if it's a primitive type by comparing with corelib types FIRST
|
||||||
if concreteType.Assembly = corelib.Corelib.Name && concreteType.Generics.IsEmpty then
|
if
|
||||||
|
concreteType.Assembly.FullName = corelib.Corelib.Name.FullName
|
||||||
|
&& concreteType.Generics.IsEmpty
|
||||||
|
then
|
||||||
// Check against known primitive types
|
// Check against known primitive types
|
||||||
if TypeInfo.NominallyEqual typeDef corelib.Boolean then
|
if TypeInfo.NominallyEqual typeDef corelib.Boolean then
|
||||||
zeroOfPrimitive PrimitiveType.Boolean, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.Boolean, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.Char then
|
elif TypeInfo.NominallyEqual typeDef corelib.Char then
|
||||||
zeroOfPrimitive PrimitiveType.Char, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.Char, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.SByte then
|
elif TypeInfo.NominallyEqual typeDef corelib.SByte then
|
||||||
zeroOfPrimitive PrimitiveType.SByte, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.SByte, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.Byte then
|
elif TypeInfo.NominallyEqual typeDef corelib.Byte then
|
||||||
zeroOfPrimitive PrimitiveType.Byte, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.Byte, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.Int16 then
|
elif TypeInfo.NominallyEqual typeDef corelib.Int16 then
|
||||||
zeroOfPrimitive PrimitiveType.Int16, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.Int16, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.UInt16 then
|
elif TypeInfo.NominallyEqual typeDef corelib.UInt16 then
|
||||||
zeroOfPrimitive PrimitiveType.UInt16, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.UInt16, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.Int32 then
|
elif TypeInfo.NominallyEqual typeDef corelib.Int32 then
|
||||||
zeroOfPrimitive PrimitiveType.Int32, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.Int32, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.UInt32 then
|
elif TypeInfo.NominallyEqual typeDef corelib.UInt32 then
|
||||||
zeroOfPrimitive PrimitiveType.UInt32, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.UInt32, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.Int64 then
|
elif TypeInfo.NominallyEqual typeDef corelib.Int64 then
|
||||||
zeroOfPrimitive PrimitiveType.Int64, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.Int64, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.UInt64 then
|
elif TypeInfo.NominallyEqual typeDef corelib.UInt64 then
|
||||||
zeroOfPrimitive PrimitiveType.UInt64, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.UInt64, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.Single then
|
elif TypeInfo.NominallyEqual typeDef corelib.Single then
|
||||||
zeroOfPrimitive PrimitiveType.Single, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.Single, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.Double then
|
elif TypeInfo.NominallyEqual typeDef corelib.Double then
|
||||||
zeroOfPrimitive PrimitiveType.Double, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.Double, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.String then
|
elif TypeInfo.NominallyEqual typeDef corelib.String then
|
||||||
zeroOfPrimitive PrimitiveType.String, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.String, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.Object then
|
elif TypeInfo.NominallyEqual typeDef corelib.Object then
|
||||||
zeroOfPrimitive PrimitiveType.Object, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.Object, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.IntPtr then
|
elif TypeInfo.NominallyEqual typeDef corelib.IntPtr then
|
||||||
zeroOfPrimitive PrimitiveType.IntPtr, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.IntPtr, concreteTypes
|
||||||
elif TypeInfo.NominallyEqual typeDef corelib.UIntPtr then
|
elif TypeInfo.NominallyEqual typeDef corelib.UIntPtr then
|
||||||
zeroOfPrimitive PrimitiveType.UIntPtr, concreteTypes
|
zeroOfPrimitive concreteTypes corelib PrimitiveType.UIntPtr, concreteTypes
|
||||||
else if
|
elif TypeInfo.NominallyEqual typeDef corelib.Array then
|
||||||
// Check if it's an array type
|
// Arrays are reference types
|
||||||
typeDef = corelib.Array
|
CliType.ObjectRef None, concreteTypes
|
||||||
then
|
|
||||||
CliType.ObjectRef None, concreteTypes // Arrays are reference types
|
|
||||||
else if
|
else if
|
||||||
|
|
||||||
// Not a known primitive, now check for cycles
|
// Not a known primitive, now check for cycles
|
||||||
Set.contains handle visited
|
Set.contains handle visited
|
||||||
then
|
then
|
||||||
// We're in a cycle - return a default zero value for the type
|
// We're in a cycle - return a default zero value for the type
|
||||||
// For value types in cycles, we'll return a null reference as a safe fallback
|
// Value types can't be self-referential unless they are specifically known to the
|
||||||
// This should only happen with self-referential types
|
// runtime - for example, System.Byte is a value type with a single field,
|
||||||
|
// of type System.Byte.
|
||||||
|
// Since we check for (nominal) equality against all such types in the first branch,
|
||||||
|
// this code path is only hit with reference types.
|
||||||
CliType.ObjectRef None, concreteTypes
|
CliType.ObjectRef None, concreteTypes
|
||||||
else
|
else
|
||||||
let visited = Set.add handle visited
|
let visited = Set.add handle visited
|
||||||
// Not a known primitive, check if it's a value type or reference type
|
// Not a known primitive, check if it's a value type or reference type
|
||||||
determineZeroForCustomType concreteTypes assemblies corelib handle concreteType typeDef visited
|
determineZeroForCustomType concreteTypes assemblies corelib handle concreteType typeDef visited
|
||||||
else if
|
else if
|
||||||
|
|
||||||
// Not from corelib or has generics
|
// Not from corelib or has generics
|
||||||
concreteType.Assembly = corelib.Corelib.Name
|
concreteType.Assembly = corelib.Corelib.Name
|
||||||
&& typeDef = corelib.Array
|
&& typeDef = corelib.Array
|
||||||
&& concreteType.Generics.Length = 1
|
&& concreteType.Generics.Length = 1
|
||||||
then
|
then
|
||||||
// This is an array type
|
// This is an array type, so null is appropriate
|
||||||
CliType.ObjectRef None, concreteTypes
|
CliType.ObjectRef None, concreteTypes
|
||||||
else if
|
else if
|
||||||
|
|
||||||
// Custom type - now check for cycles
|
// Custom type - now check for cycles
|
||||||
Set.contains handle visited
|
Set.contains handle visited
|
||||||
then
|
then
|
||||||
// We're in a cycle - return a default zero value for the type
|
// We're in a cycle - return a default zero value for the type.
|
||||||
// For value types in cycles, we'll return a null reference as a safe fallback
|
// Value types can't be self-referential unless they are specifically known to the
|
||||||
// This should only happen with self-referential types
|
// runtime - for example, System.Byte is a value type with a single field,
|
||||||
|
// of type System.Byte.
|
||||||
|
// Since we check for (nominal) equality against all such types in the first branch,
|
||||||
|
// this code path is only hit with reference types.
|
||||||
CliType.ObjectRef None, concreteTypes
|
CliType.ObjectRef None, concreteTypes
|
||||||
else
|
else
|
||||||
let visited = Set.add handle visited
|
let visited = Set.add handle visited
|
||||||
@@ -343,7 +750,7 @@ module CliType =
|
|||||||
// It's a value type - need to create zero values for all non-static fields
|
// It's a value type - need to create zero values for all non-static fields
|
||||||
let mutable currentConcreteTypes = concreteTypes
|
let mutable currentConcreteTypes = concreteTypes
|
||||||
|
|
||||||
let fieldZeros =
|
let vt =
|
||||||
typeDef.Fields
|
typeDef.Fields
|
||||||
|> List.filter (fun field -> not (field.Attributes.HasFlag FieldAttributes.Static))
|
|> List.filter (fun field -> not (field.Attributes.HasFlag FieldAttributes.Static))
|
||||||
|> List.map (fun field ->
|
|> List.map (fun field ->
|
||||||
@@ -359,13 +766,15 @@ module CliType =
|
|||||||
zeroOfWithVisited currentConcreteTypes assemblies corelib fieldHandle visited
|
zeroOfWithVisited currentConcreteTypes assemblies corelib fieldHandle visited
|
||||||
|
|
||||||
currentConcreteTypes <- updatedConcreteTypes2
|
currentConcreteTypes <- updatedConcreteTypes2
|
||||||
(field.Name, fieldZero)
|
|
||||||
)
|
|
||||||
|
|
||||||
let vt =
|
{
|
||||||
{
|
Name = field.Name
|
||||||
Fields = fieldZeros
|
Contents = fieldZero
|
||||||
}
|
Offset = field.Offset
|
||||||
|
Type = fieldHandle
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|> CliValueType.OfFields typeDef.Layout
|
||||||
|
|
||||||
CliType.ValueType vt, currentConcreteTypes
|
CliType.ValueType vt, currentConcreteTypes
|
||||||
else
|
else
|
||||||
@@ -393,20 +802,22 @@ module CliType =
|
|||||||
// The field type might reference generic parameters of the declaring type
|
// The field type might reference generic parameters of the declaring type
|
||||||
let methodGenerics = ImmutableArray.Empty // Fields don't have method generics
|
let methodGenerics = ImmutableArray.Empty // Fields don't have method generics
|
||||||
|
|
||||||
let loadAssembly
|
let loadAssembly =
|
||||||
(assyName : AssemblyName)
|
{ new IAssemblyLoad with
|
||||||
(ref : AssemblyReferenceHandle)
|
member _.LoadAssembly loaded assyName ref =
|
||||||
: ImmutableDictionary<string, DumpedAssembly> * DumpedAssembly
|
match loaded.TryGetValue assyName.FullName with
|
||||||
=
|
| true, currentAssy ->
|
||||||
match assemblies.TryGetValue assyName.FullName with
|
let targetAssyRef = currentAssy.AssemblyReferences.[ref]
|
||||||
| true, currentAssy ->
|
|
||||||
let targetAssyRef = currentAssy.AssemblyReferences.[ref]
|
|
||||||
|
|
||||||
match assemblies.TryGetValue targetAssyRef.Name.FullName with
|
match loaded.TryGetValue targetAssyRef.Name.FullName with
|
||||||
| true, targetAssy -> assemblies, targetAssy
|
| true, targetAssy -> loaded, targetAssy
|
||||||
| false, _ ->
|
| false, _ ->
|
||||||
failwithf "Assembly %s not loaded when trying to resolve reference" targetAssyRef.Name.FullName
|
failwithf
|
||||||
| false, _ -> failwithf "Current assembly %s not loaded when trying to resolve reference" assyName.FullName
|
"Assembly %s not loaded when trying to resolve reference"
|
||||||
|
targetAssyRef.Name.FullName
|
||||||
|
| false, _ ->
|
||||||
|
failwithf "Current assembly %s not loaded when trying to resolve reference" assyName.FullName
|
||||||
|
}
|
||||||
|
|
||||||
let handle, newCtx =
|
let handle, newCtx =
|
||||||
TypeConcretization.concretizeType
|
TypeConcretization.concretizeType
|
||||||
@@ -418,3 +829,41 @@ module CliType =
|
|||||||
fieldType
|
fieldType
|
||||||
|
|
||||||
handle, newCtx.ConcreteTypes
|
handle, newCtx.ConcreteTypes
|
||||||
|
|
||||||
|
let withFieldSet (field : string) (value : CliType) (c : CliType) : CliType =
|
||||||
|
match c with
|
||||||
|
| CliType.Numeric cliNumericType -> failwith "todo"
|
||||||
|
| CliType.Bool b -> failwith "todo"
|
||||||
|
| CliType.Char (high, low) -> failwith "todo"
|
||||||
|
| CliType.ObjectRef managedHeapAddressOption -> failwith "todo"
|
||||||
|
| CliType.RuntimePointer cliRuntimePointer -> failwith "todo"
|
||||||
|
| CliType.ValueType cvt -> CliValueType.WithFieldSet field value cvt |> CliType.ValueType
|
||||||
|
|
||||||
|
let getField (field : string) (value : CliType) : CliType =
|
||||||
|
match value with
|
||||||
|
| CliType.Numeric cliNumericType -> failwith "todo"
|
||||||
|
| CliType.Bool b -> failwith "todo"
|
||||||
|
| CliType.Char (high, low) -> failwith "todo"
|
||||||
|
| CliType.ObjectRef managedHeapAddressOption -> failwith "todo"
|
||||||
|
| CliType.RuntimePointer cliRuntimePointer -> failwith "todo"
|
||||||
|
| CliType.ValueType cvt -> CliValueType.DereferenceField field cvt
|
||||||
|
|
||||||
|
/// Returns the offset and size.
|
||||||
|
let getFieldLayout (field : string) (value : CliType) : int * int =
|
||||||
|
match value with
|
||||||
|
| CliType.Numeric cliNumericType -> failwith "todo"
|
||||||
|
| CliType.Bool b -> failwith "todo"
|
||||||
|
| CliType.Char (high, low) -> failwith "todo"
|
||||||
|
| CliType.ObjectRef managedHeapAddressOption -> failwith "todo"
|
||||||
|
| CliType.RuntimePointer cliRuntimePointer -> failwith "todo"
|
||||||
|
| CliType.ValueType cvt -> CliValueType.GetFieldLayout field cvt
|
||||||
|
|
||||||
|
/// Returns None if there isn't *exactly* one field that starts there. This rules out some valid programs.
|
||||||
|
let getFieldAt (offset : int) (value : CliType) : CliConcreteField option =
|
||||||
|
match value with
|
||||||
|
| CliType.Numeric cliNumericType -> failwith "todo"
|
||||||
|
| CliType.Bool b -> failwith "todo"
|
||||||
|
| CliType.Char (high, low) -> failwith "todo"
|
||||||
|
| CliType.ObjectRef managedHeapAddressOption -> failwith "todo"
|
||||||
|
| CliType.RuntimePointer cliRuntimePointer -> failwith "todo"
|
||||||
|
| CliType.ValueType cvt -> CliValueType.FieldsAt offset cvt |> List.tryExactlyOne
|
||||||
|
@@ -4,25 +4,133 @@ namespace WoofWare.PawPrint
|
|||||||
|
|
||||||
type IArithmeticOperation =
|
type IArithmeticOperation =
|
||||||
abstract Int32Int32 : int32 -> int32 -> int32
|
abstract Int32Int32 : int32 -> int32 -> int32
|
||||||
|
abstract Int32NativeInt : int32 -> nativeint -> nativeint
|
||||||
|
abstract NativeIntInt32 : nativeint -> int32 -> nativeint
|
||||||
abstract Int64Int64 : int64 -> int64 -> int64
|
abstract Int64Int64 : int64 -> int64 -> int64
|
||||||
abstract FloatFloat : float -> float -> float
|
abstract FloatFloat : float -> float -> float
|
||||||
|
abstract NativeIntNativeInt : nativeint -> nativeint -> nativeint
|
||||||
|
abstract Int32ManagedPtr : IlMachineState -> int32 -> ManagedPointerSource -> Choice<ManagedPointerSource, int>
|
||||||
|
abstract ManagedPtrInt32 : IlMachineState -> ManagedPointerSource -> int32 -> Choice<ManagedPointerSource, int>
|
||||||
|
|
||||||
|
abstract ManagedPtrManagedPtr :
|
||||||
|
IlMachineState -> ManagedPointerSource -> ManagedPointerSource -> Choice<ManagedPointerSource, nativeint>
|
||||||
|
|
||||||
abstract Name : string
|
abstract Name : string
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module ArithmeticOperation =
|
module ArithmeticOperation =
|
||||||
|
let private addInt32ManagedPtr state v ptr =
|
||||||
|
match ptr with
|
||||||
|
| LocalVariable (sourceThread, methodFrame, whichVar) -> failwith "refusing to add to a local variable address"
|
||||||
|
| Argument (sourceThread, methodFrame, whichVar) -> failwith "refusing to add to an argument address"
|
||||||
|
| Heap managedHeapAddress -> failwith "refusing to add to a heap address"
|
||||||
|
| ArrayIndex (arr, index) -> failwith "TODO: arrays"
|
||||||
|
| Field (src, fieldName) ->
|
||||||
|
let obj = IlMachineState.dereferencePointer state src
|
||||||
|
let offset, _ = CliType.getFieldLayout fieldName obj
|
||||||
|
|
||||||
|
match CliType.getFieldAt (offset + v) obj with
|
||||||
|
| None -> failwith "TODO: couldn't identify field at offset"
|
||||||
|
| Some field ->
|
||||||
|
ManagedPointerSource.Field (src, CliConcreteField.ToCliField(field).Name)
|
||||||
|
|> Choice1Of2
|
||||||
|
| Null -> Choice2Of2 v
|
||||||
|
| InterpretedAsType (managedPointerSource, concreteType) -> failwith "todo"
|
||||||
|
|
||||||
|
let private mulInt32ManagedPtr (state : IlMachineState) v ptr =
|
||||||
|
if v = 0 then
|
||||||
|
Choice2Of2 0
|
||||||
|
elif v = 1 then
|
||||||
|
Choice1Of2 ptr
|
||||||
|
else
|
||||||
|
|
||||||
|
match ptr with
|
||||||
|
| ManagedPointerSource.Null -> Choice2Of2 0
|
||||||
|
| _ -> failwith "refusing to multiply pointers"
|
||||||
|
|
||||||
let add =
|
let add =
|
||||||
{ new IArithmeticOperation with
|
{ new IArithmeticOperation with
|
||||||
member _.Int32Int32 a b = (# "add" a b : int32 #)
|
member _.Int32Int32 a b = (# "add" a b : int32 #)
|
||||||
member _.Int64Int64 a b = (# "add" a b : int64 #)
|
member _.Int64Int64 a b = (# "add" a b : int64 #)
|
||||||
member _.FloatFloat a b = (# "add" a b : float #)
|
member _.FloatFloat a b = (# "add" a b : float #)
|
||||||
|
member _.NativeIntNativeInt a b = (# "add" a b : nativeint #)
|
||||||
|
member _.Int32NativeInt a b = (# "add" a b : nativeint #)
|
||||||
|
member _.NativeIntInt32 a b = (# "add" a b : nativeint #)
|
||||||
|
|
||||||
|
member _.ManagedPtrManagedPtr _ ptr1 ptr2 =
|
||||||
|
match ptr1, ptr2 with
|
||||||
|
| ManagedPointerSource.Null, _ -> Choice1Of2 ptr2
|
||||||
|
| _, ManagedPointerSource.Null -> Choice1Of2 ptr1
|
||||||
|
| _, _ -> failwith "refusing to add two managed pointers"
|
||||||
|
|
||||||
|
member _.Int32ManagedPtr state val1 ptr2 = addInt32ManagedPtr state val1 ptr2
|
||||||
|
member _.ManagedPtrInt32 state ptr1 val2 = addInt32ManagedPtr state val2 ptr1
|
||||||
|
|
||||||
member _.Name = "add"
|
member _.Name = "add"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let addOvf =
|
||||||
|
{ new IArithmeticOperation with
|
||||||
|
member _.Int32Int32 a b = (# "add.ovf" a b : int32 #)
|
||||||
|
member _.Int64Int64 a b = (# "add.ovf" a b : int64 #)
|
||||||
|
member _.FloatFloat a b = (# "add.ovf" a b : float #)
|
||||||
|
member _.NativeIntNativeInt a b = (# "add.ovf" a b : nativeint #)
|
||||||
|
member _.Int32NativeInt a b = (# "add.ovf" a b : nativeint #)
|
||||||
|
member _.NativeIntInt32 a b = (# "add.ovf" a b : nativeint #)
|
||||||
|
|
||||||
|
member _.ManagedPtrManagedPtr _ ptr1 ptr2 =
|
||||||
|
match ptr1, ptr2 with
|
||||||
|
| ManagedPointerSource.Null, _ -> Choice1Of2 ptr2
|
||||||
|
| _, ManagedPointerSource.Null -> Choice1Of2 ptr1
|
||||||
|
| _, _ -> failwith "refusing to add two managed pointers"
|
||||||
|
|
||||||
|
member _.Int32ManagedPtr state val1 ptr2 = addInt32ManagedPtr state val1 ptr2
|
||||||
|
member _.ManagedPtrInt32 state ptr1 val2 = addInt32ManagedPtr state val2 ptr1
|
||||||
|
|
||||||
|
member _.Name = "add.ovf"
|
||||||
|
}
|
||||||
|
|
||||||
let sub =
|
let sub =
|
||||||
{ new IArithmeticOperation with
|
{ new IArithmeticOperation with
|
||||||
member _.Int32Int32 a b = (# "sub" a b : int32 #)
|
member _.Int32Int32 a b = (# "sub" a b : int32 #)
|
||||||
member _.Int64Int64 a b = (# "sub" a b : int64 #)
|
member _.Int64Int64 a b = (# "sub" a b : int64 #)
|
||||||
member _.FloatFloat a b = (# "sub" a b : float #)
|
member _.FloatFloat a b = (# "sub" a b : float #)
|
||||||
|
member _.NativeIntNativeInt a b = (# "sub" a b : nativeint #)
|
||||||
|
member _.Int32NativeInt a b = (# "sub" a b : nativeint #)
|
||||||
|
member _.NativeIntInt32 a b = (# "sub" a b : nativeint #)
|
||||||
|
|
||||||
|
member _.ManagedPtrManagedPtr state ptr1 ptr2 =
|
||||||
|
match ptr1, ptr2 with
|
||||||
|
| ptr1, ManagedPointerSource.Null -> Choice1Of2 ptr1
|
||||||
|
| ManagedPointerSource.Null, _ -> failwith "refusing to create negative pointer"
|
||||||
|
| ManagedPointerSource.ArrayIndex (arr1, index1), ManagedPointerSource.ArrayIndex (arr2, index2) ->
|
||||||
|
if arr1 <> arr2 then
|
||||||
|
failwith "refusing to operate on pointers to different arrays"
|
||||||
|
|
||||||
|
(index1 - index2) |> nativeint |> Choice2Of2
|
||||||
|
| ManagedPointerSource.ArrayIndex _, _ -> failwith $"refusing to operate on array index ptr vs %O{ptr2}"
|
||||||
|
| ManagedPointerSource.Argument _, _
|
||||||
|
| _, ManagedPointerSource.Argument _ ->
|
||||||
|
failwith $"refusing to operate on pointers to arguments: %O{ptr1} and %O{ptr2}"
|
||||||
|
| ManagedPointerSource.Field (obj1, fieldName1), ManagedPointerSource.Field (obj2, fieldName2) ->
|
||||||
|
if obj1 <> obj2 then
|
||||||
|
failwith "refusing to operate on field pointers in different objects"
|
||||||
|
|
||||||
|
let obj = IlMachineState.dereferencePointer state obj1
|
||||||
|
|
||||||
|
let offset1, _ = CliType.getFieldLayout fieldName1 obj
|
||||||
|
let offset2, _ = CliType.getFieldLayout fieldName2 obj
|
||||||
|
|
||||||
|
(offset1 - offset2) |> nativeint |> Choice2Of2
|
||||||
|
| _, _ -> failwith "TODO"
|
||||||
|
|
||||||
|
member _.Int32ManagedPtr state val1 ptr2 =
|
||||||
|
match ptr2 with
|
||||||
|
| ManagedPointerSource.Null -> Choice2Of2 val1
|
||||||
|
| _ -> failwith "refusing to subtract a pointer"
|
||||||
|
|
||||||
|
member _.ManagedPtrInt32 state ptr1 val2 = failwith "TODO: subtract from pointer"
|
||||||
|
|
||||||
member _.Name = "sub"
|
member _.Name = "sub"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,21 +139,143 @@ module ArithmeticOperation =
|
|||||||
member _.Int32Int32 a b = (# "mul" a b : int32 #)
|
member _.Int32Int32 a b = (# "mul" a b : int32 #)
|
||||||
member _.Int64Int64 a b = (# "mul" a b : int64 #)
|
member _.Int64Int64 a b = (# "mul" a b : int64 #)
|
||||||
member _.FloatFloat a b = (# "mul" a b : float #)
|
member _.FloatFloat a b = (# "mul" a b : float #)
|
||||||
|
member _.NativeIntNativeInt a b = (# "mul" a b : nativeint #)
|
||||||
|
member _.Int32NativeInt a b = (# "mul" a b : nativeint #)
|
||||||
|
member _.NativeIntInt32 a b = (# "mul" a b : nativeint #)
|
||||||
|
|
||||||
|
member _.ManagedPtrManagedPtr _ ptr1 ptr2 =
|
||||||
|
match ptr1, ptr2 with
|
||||||
|
| ManagedPointerSource.Null, _ -> Choice2Of2 (nativeint 0)
|
||||||
|
| _, ManagedPointerSource.Null -> Choice2Of2 (nativeint 0)
|
||||||
|
| _, _ -> failwith "refusing to multiply two managed pointers"
|
||||||
|
|
||||||
|
member _.Int32ManagedPtr state a ptr = mulInt32ManagedPtr state a ptr
|
||||||
|
member _.ManagedPtrInt32 state ptr a = mulInt32ManagedPtr state a ptr
|
||||||
|
|
||||||
member _.Name = "mul"
|
member _.Name = "mul"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let rem =
|
||||||
|
{ new IArithmeticOperation with
|
||||||
|
member _.Int32Int32 a b = (# "rem" a b : int32 #)
|
||||||
|
member _.Int64Int64 a b = (# "rem" a b : int64 #)
|
||||||
|
member _.FloatFloat a b = (# "rem" a b : float #)
|
||||||
|
member _.NativeIntNativeInt a b = (# "rem" a b : nativeint #)
|
||||||
|
member _.Int32NativeInt a b = (# "rem" a b : nativeint #)
|
||||||
|
member _.NativeIntInt32 a b = (# "rem" a b : nativeint #)
|
||||||
|
|
||||||
|
member _.ManagedPtrManagedPtr _ ptr1 ptr2 = failwith "refusing to rem pointers"
|
||||||
|
|
||||||
|
member _.Int32ManagedPtr _ a ptr = failwith "refusing to rem pointer"
|
||||||
|
|
||||||
|
member _.ManagedPtrInt32 _ ptr a = failwith "refusing to rem pointer"
|
||||||
|
|
||||||
|
member _.Name = "mul"
|
||||||
|
}
|
||||||
|
|
||||||
|
let mulOvf =
|
||||||
|
{ new IArithmeticOperation with
|
||||||
|
member _.Int32Int32 a b = (# "mul.ovf" a b : int32 #)
|
||||||
|
member _.Int64Int64 a b = (# "mul.ovf" a b : int64 #)
|
||||||
|
member _.FloatFloat a b = (# "mul.ovf" a b : float #)
|
||||||
|
member _.NativeIntNativeInt a b = (# "mul.ovf" a b : nativeint #)
|
||||||
|
member _.Int32NativeInt a b = (# "mul.ovf" a b : nativeint #)
|
||||||
|
member _.NativeIntInt32 a b = (# "mul.ovf" a b : nativeint #)
|
||||||
|
|
||||||
|
member _.ManagedPtrManagedPtr _ ptr1 ptr2 =
|
||||||
|
match ptr1, ptr2 with
|
||||||
|
| ManagedPointerSource.Null, _ -> Choice2Of2 (nativeint 0)
|
||||||
|
| _, ManagedPointerSource.Null -> Choice2Of2 (nativeint 0)
|
||||||
|
| _, _ -> failwith "refusing to multiply two managed pointers"
|
||||||
|
|
||||||
|
member _.Int32ManagedPtr state a ptr = mulInt32ManagedPtr state a ptr
|
||||||
|
member _.ManagedPtrInt32 state a ptr = mulInt32ManagedPtr state ptr a
|
||||||
|
|
||||||
|
member _.Name = "mul_ovf"
|
||||||
|
}
|
||||||
|
|
||||||
|
let div =
|
||||||
|
{ new IArithmeticOperation with
|
||||||
|
member _.Int32Int32 a b = (# "div" a b : int32 #)
|
||||||
|
member _.Int64Int64 a b = (# "div" a b : int64 #)
|
||||||
|
member _.FloatFloat a b = (# "div" a b : float #)
|
||||||
|
member _.NativeIntNativeInt a b = (# "div" a b : nativeint #)
|
||||||
|
member _.Int32NativeInt a b = (# "div" a b : nativeint #)
|
||||||
|
member _.NativeIntInt32 a b = (# "div" a b : nativeint #)
|
||||||
|
|
||||||
|
member _.ManagedPtrManagedPtr _ ptr1 ptr2 =
|
||||||
|
match ptr1, ptr2 with
|
||||||
|
| ManagedPointerSource.Null, _ -> Choice2Of2 (nativeint 0)
|
||||||
|
| _, _ -> failwith "refusing to divide two managed pointers"
|
||||||
|
|
||||||
|
member _.Int32ManagedPtr _ a ptr =
|
||||||
|
if a = 0 then
|
||||||
|
Choice2Of2 0
|
||||||
|
else
|
||||||
|
failwith "refusing to divide pointers"
|
||||||
|
|
||||||
|
member _.ManagedPtrInt32 _ ptr a =
|
||||||
|
if a = 1 then
|
||||||
|
Choice1Of2 ptr
|
||||||
|
else
|
||||||
|
failwith "refusing to divide a pointer"
|
||||||
|
|
||||||
|
member _.Name = "div"
|
||||||
|
}
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module BinaryArithmetic =
|
module BinaryArithmetic =
|
||||||
let execute (op : IArithmeticOperation) (val1 : EvalStackValue) (val2 : EvalStackValue) : EvalStackValue =
|
let execute
|
||||||
|
(op : IArithmeticOperation)
|
||||||
|
(state : IlMachineState)
|
||||||
|
(val1 : EvalStackValue)
|
||||||
|
(val2 : EvalStackValue)
|
||||||
|
: EvalStackValue
|
||||||
|
=
|
||||||
// see table at https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.add?view=net-9.0
|
// see table at https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.add?view=net-9.0
|
||||||
match val1, val2 with
|
match val1, val2 with
|
||||||
| EvalStackValue.Int32 val1, EvalStackValue.Int32 val2 -> op.Int32Int32 val1 val2 |> EvalStackValue.Int32
|
| EvalStackValue.Int32 val1, EvalStackValue.Int32 val2 -> op.Int32Int32 val1 val2 |> EvalStackValue.Int32
|
||||||
| EvalStackValue.Int32 val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.NativeInt
|
| EvalStackValue.Int32 val1, EvalStackValue.NativeInt val2 ->
|
||||||
| EvalStackValue.Int32 val1, EvalStackValue.ManagedPointer val2 -> failwith "" |> EvalStackValue.ManagedPointer
|
let val2 =
|
||||||
|
match val2 with
|
||||||
|
| NativeIntSource.Verbatim n -> nativeint<int64> n
|
||||||
|
| v -> failwith $"refusing to operate on non-verbatim native int %O{v}"
|
||||||
|
|
||||||
|
op.Int32NativeInt val1 val2
|
||||||
|
|> int64<nativeint>
|
||||||
|
|> NativeIntSource.Verbatim
|
||||||
|
|> EvalStackValue.NativeInt
|
||||||
|
| EvalStackValue.Int32 val1, EvalStackValue.ManagedPointer val2 ->
|
||||||
|
match op.Int32ManagedPtr state val1 val2 with
|
||||||
|
| Choice1Of2 v -> EvalStackValue.ManagedPointer v
|
||||||
|
| Choice2Of2 i -> EvalStackValue.Int32 i
|
||||||
| EvalStackValue.Int32 val1, EvalStackValue.ObjectRef val2 -> failwith "" |> EvalStackValue.ObjectRef
|
| EvalStackValue.Int32 val1, EvalStackValue.ObjectRef val2 -> failwith "" |> EvalStackValue.ObjectRef
|
||||||
| EvalStackValue.Int64 val1, EvalStackValue.Int64 val2 -> op.Int64Int64 val1 val2 |> EvalStackValue.Int64
|
| EvalStackValue.Int64 val1, EvalStackValue.Int64 val2 -> op.Int64Int64 val1 val2 |> EvalStackValue.Int64
|
||||||
| EvalStackValue.NativeInt val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.NativeInt
|
| EvalStackValue.NativeInt val1, EvalStackValue.Int32 val2 ->
|
||||||
| EvalStackValue.NativeInt val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.NativeInt
|
let val1 =
|
||||||
|
match val1 with
|
||||||
|
| NativeIntSource.Verbatim n -> nativeint<int64> n
|
||||||
|
| v -> failwith $"refusing to operate on non-verbatim native int %O{v}"
|
||||||
|
|
||||||
|
op.NativeIntInt32 val1 val2
|
||||||
|
|> int64<nativeint>
|
||||||
|
|> NativeIntSource.Verbatim
|
||||||
|
|> EvalStackValue.NativeInt
|
||||||
|
| EvalStackValue.NativeInt val1, EvalStackValue.NativeInt val2 ->
|
||||||
|
let val1 =
|
||||||
|
match val1 with
|
||||||
|
| NativeIntSource.Verbatim n -> nativeint<int64> n
|
||||||
|
| v -> failwith $"refusing to operate on non-verbatim native int %O{v}"
|
||||||
|
|
||||||
|
let val2 =
|
||||||
|
match val2 with
|
||||||
|
| NativeIntSource.Verbatim n -> nativeint<int64> n
|
||||||
|
| v -> failwith $"refusing to operate on non-verbatim native int %O{v}"
|
||||||
|
|
||||||
|
op.NativeIntNativeInt val1 val2
|
||||||
|
|> int64<nativeint>
|
||||||
|
|> NativeIntSource.Verbatim
|
||||||
|
|> EvalStackValue.NativeInt
|
||||||
| EvalStackValue.NativeInt val1, EvalStackValue.ManagedPointer val2 ->
|
| EvalStackValue.NativeInt val1, EvalStackValue.ManagedPointer val2 ->
|
||||||
failwith "" |> EvalStackValue.ManagedPointer
|
failwith "" |> EvalStackValue.ManagedPointer
|
||||||
| EvalStackValue.NativeInt val1, EvalStackValue.ObjectRef val2 -> failwith "" |> EvalStackValue.ObjectRef
|
| EvalStackValue.NativeInt val1, EvalStackValue.ObjectRef val2 -> failwith "" |> EvalStackValue.ObjectRef
|
||||||
@@ -53,6 +283,13 @@ module BinaryArithmetic =
|
|||||||
| EvalStackValue.ManagedPointer val1, EvalStackValue.NativeInt val2 ->
|
| EvalStackValue.ManagedPointer val1, EvalStackValue.NativeInt val2 ->
|
||||||
failwith "" |> EvalStackValue.ManagedPointer
|
failwith "" |> EvalStackValue.ManagedPointer
|
||||||
| EvalStackValue.ObjectRef val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.ObjectRef
|
| EvalStackValue.ObjectRef val1, EvalStackValue.NativeInt val2 -> failwith "" |> EvalStackValue.ObjectRef
|
||||||
| EvalStackValue.ManagedPointer val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.ManagedPointer
|
| EvalStackValue.ManagedPointer val1, EvalStackValue.Int32 val2 ->
|
||||||
|
match op.ManagedPtrInt32 state val1 val2 with
|
||||||
|
| Choice1Of2 result -> EvalStackValue.ManagedPointer result
|
||||||
|
| Choice2Of2 result -> EvalStackValue.NativeInt (NativeIntSource.Verbatim (int64<int32> result))
|
||||||
| EvalStackValue.ObjectRef val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.ObjectRef
|
| EvalStackValue.ObjectRef val1, EvalStackValue.Int32 val2 -> failwith "" |> EvalStackValue.ObjectRef
|
||||||
|
| EvalStackValue.ManagedPointer val1, EvalStackValue.ManagedPointer val2 ->
|
||||||
|
match op.ManagedPtrManagedPtr state val1 val2 with
|
||||||
|
| Choice1Of2 result -> EvalStackValue.ManagedPointer result
|
||||||
|
| Choice2Of2 result -> EvalStackValue.NativeInt (NativeIntSource.Verbatim (int64<nativeint> result))
|
||||||
| val1, val2 -> failwith $"invalid %s{op.Name} operation: {val1} and {val2}"
|
| val1, val2 -> failwith $"invalid %s{op.Name} operation: {val1} and {val2}"
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
namespace WoofWare.PawPrint
|
namespace WoofWare.PawPrint
|
||||||
|
|
||||||
|
open System.Collections.Immutable
|
||||||
|
open System.Reflection.Metadata
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module Corelib =
|
module Corelib =
|
||||||
|
|
||||||
@@ -149,6 +152,66 @@ module Corelib =
|
|||||||
)
|
)
|
||||||
|> Seq.exactlyOne
|
|> Seq.exactlyOne
|
||||||
|
|
||||||
|
let exceptionType =
|
||||||
|
corelib.TypeDefs
|
||||||
|
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "Exception" then Some v else None)
|
||||||
|
|> Seq.exactlyOne
|
||||||
|
|
||||||
|
let arithmeticException =
|
||||||
|
corelib.TypeDefs
|
||||||
|
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "ArithmeticException" then Some v else None)
|
||||||
|
|> Seq.exactlyOne
|
||||||
|
|
||||||
|
let divideByZeroException =
|
||||||
|
corelib.TypeDefs
|
||||||
|
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "DivideByZeroException" then Some v else None)
|
||||||
|
|> Seq.exactlyOne
|
||||||
|
|
||||||
|
let overflowException =
|
||||||
|
corelib.TypeDefs
|
||||||
|
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "OverflowException" then Some v else None)
|
||||||
|
|> Seq.exactlyOne
|
||||||
|
|
||||||
|
let stackOverflowException =
|
||||||
|
corelib.TypeDefs
|
||||||
|
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "StackOverflowException" then Some v else None)
|
||||||
|
|> Seq.exactlyOne
|
||||||
|
|
||||||
|
let typeLoadException =
|
||||||
|
corelib.TypeDefs
|
||||||
|
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "TypeLoadException" then Some v else None)
|
||||||
|
|> Seq.exactlyOne
|
||||||
|
|
||||||
|
let indexOutOfRangeException =
|
||||||
|
corelib.TypeDefs
|
||||||
|
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "IndexOutOfRangeException" then Some v else None)
|
||||||
|
|> Seq.exactlyOne
|
||||||
|
|
||||||
|
let invalidCastException =
|
||||||
|
corelib.TypeDefs
|
||||||
|
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "InvalidCastException" then Some v else None)
|
||||||
|
|> Seq.exactlyOne
|
||||||
|
|
||||||
|
let missingFieldException =
|
||||||
|
corelib.TypeDefs
|
||||||
|
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "MissingFieldException" then Some v else None)
|
||||||
|
|> Seq.exactlyOne
|
||||||
|
|
||||||
|
let missingMethodException =
|
||||||
|
corelib.TypeDefs
|
||||||
|
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "MissingMethodException" then Some v else None)
|
||||||
|
|> Seq.exactlyOne
|
||||||
|
|
||||||
|
let nullReferenceException =
|
||||||
|
corelib.TypeDefs
|
||||||
|
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "NullReferenceException" then Some v else None)
|
||||||
|
|> Seq.exactlyOne
|
||||||
|
|
||||||
|
let outOfMemoryException =
|
||||||
|
corelib.TypeDefs
|
||||||
|
|> Seq.choose (fun (KeyValue (_, v)) -> if v.Name = "OutOfMemoryException" then Some v else None)
|
||||||
|
|> Seq.exactlyOne
|
||||||
|
|
||||||
{
|
{
|
||||||
Corelib = corelib
|
Corelib = corelib
|
||||||
String = stringType
|
String = stringType
|
||||||
@@ -179,4 +242,94 @@ module Corelib =
|
|||||||
TypedReference = typedReferenceType
|
TypedReference = typedReferenceType
|
||||||
IntPtr = intPtrType
|
IntPtr = intPtrType
|
||||||
UIntPtr = uintPtrType
|
UIntPtr = uintPtrType
|
||||||
|
Exception = exceptionType
|
||||||
|
ArithmeticException = arithmeticException
|
||||||
|
DivideByZeroException = divideByZeroException
|
||||||
|
OverflowException = overflowException
|
||||||
|
StackOverflowException = stackOverflowException
|
||||||
|
TypeLoadException = typeLoadException
|
||||||
|
IndexOutOfRangeException = indexOutOfRangeException
|
||||||
|
InvalidCastException = invalidCastException
|
||||||
|
MissingFieldException = missingFieldException
|
||||||
|
MissingMethodException = missingMethodException
|
||||||
|
NullReferenceException = nullReferenceException
|
||||||
|
OutOfMemoryException = outOfMemoryException
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let concretizeAll
|
||||||
|
(loaded : ImmutableDictionary<string, DumpedAssembly>)
|
||||||
|
(bct : BaseClassTypes<DumpedAssembly>)
|
||||||
|
(t : AllConcreteTypes)
|
||||||
|
: AllConcreteTypes
|
||||||
|
=
|
||||||
|
let ctx =
|
||||||
|
{
|
||||||
|
TypeConcretization.ConcretizationContext.InProgress = ImmutableDictionary.Empty
|
||||||
|
TypeConcretization.ConcretizationContext.ConcreteTypes = t
|
||||||
|
TypeConcretization.ConcretizationContext.LoadedAssemblies = loaded
|
||||||
|
TypeConcretization.ConcretizationContext.BaseTypes = bct
|
||||||
|
}
|
||||||
|
|
||||||
|
let loader =
|
||||||
|
{ new IAssemblyLoad with
|
||||||
|
member _.LoadAssembly _ _ _ =
|
||||||
|
failwith "should have already loaded this assembly"
|
||||||
|
}
|
||||||
|
|
||||||
|
let tys =
|
||||||
|
[
|
||||||
|
bct.String
|
||||||
|
bct.Boolean
|
||||||
|
bct.Char
|
||||||
|
bct.SByte
|
||||||
|
bct.Byte
|
||||||
|
bct.Int16
|
||||||
|
bct.UInt16
|
||||||
|
bct.Int32
|
||||||
|
bct.UInt32
|
||||||
|
bct.Int64
|
||||||
|
bct.UInt64
|
||||||
|
bct.Single
|
||||||
|
bct.Double
|
||||||
|
bct.Array
|
||||||
|
bct.Enum
|
||||||
|
bct.ValueType
|
||||||
|
bct.DelegateType
|
||||||
|
bct.Object
|
||||||
|
bct.RuntimeTypeHandle
|
||||||
|
bct.RuntimeMethodHandle
|
||||||
|
bct.RuntimeFieldHandle
|
||||||
|
bct.RuntimeFieldInfoStub
|
||||||
|
bct.RuntimeFieldHandleInternal
|
||||||
|
bct.RuntimeType
|
||||||
|
bct.Void
|
||||||
|
bct.TypedReference
|
||||||
|
bct.IntPtr
|
||||||
|
bct.UIntPtr
|
||||||
|
]
|
||||||
|
|
||||||
|
(ctx, tys)
|
||||||
|
||> List.fold (fun ctx ty ->
|
||||||
|
let stk =
|
||||||
|
match DumpedAssembly.resolveBaseType ctx.BaseTypes ctx.LoadedAssemblies ty.Assembly ty.BaseType with
|
||||||
|
| ResolvedBaseType.Enum
|
||||||
|
| ResolvedBaseType.ValueType -> SignatureTypeKind.ValueType
|
||||||
|
| ResolvedBaseType.Object
|
||||||
|
| ResolvedBaseType.Delegate -> SignatureTypeKind.Class
|
||||||
|
|
||||||
|
let _handle, ctx =
|
||||||
|
TypeConcretization.concretizeType
|
||||||
|
ctx
|
||||||
|
loader
|
||||||
|
ty.Assembly
|
||||||
|
ImmutableArray.Empty
|
||||||
|
ImmutableArray.Empty
|
||||||
|
(TypeDefn.FromDefinition (
|
||||||
|
ComparableTypeDefinitionHandle.Make ty.TypeDefHandle,
|
||||||
|
ty.Assembly.FullName,
|
||||||
|
stk
|
||||||
|
))
|
||||||
|
|
||||||
|
ctx
|
||||||
|
)
|
||||||
|
|> _.ConcreteTypes
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
namespace WoofWare.PawPrint
|
namespace WoofWare.PawPrint
|
||||||
|
|
||||||
|
#nowarn "42"
|
||||||
|
|
||||||
/// See I.12.3.2.1 for definition
|
/// See I.12.3.2.1 for definition
|
||||||
type EvalStackValue =
|
type EvalStackValue =
|
||||||
| Int32 of int32
|
| Int32 of int32
|
||||||
@@ -8,10 +10,8 @@ type EvalStackValue =
|
|||||||
| Float of float
|
| Float of float
|
||||||
| ManagedPointer of ManagedPointerSource
|
| ManagedPointer of ManagedPointerSource
|
||||||
| ObjectRef of ManagedHeapAddress
|
| ObjectRef of ManagedHeapAddress
|
||||||
// Fraser thinks this isn't really a thing in CoreCLR
|
/// This doesn't match what the CLR does in reality, but we can work out whatever we need from it.
|
||||||
// | TransientPointer of TransientPointerSource
|
| UserDefinedValueType of CliValueType
|
||||||
/// Mapping of field name to value
|
|
||||||
| UserDefinedValueType of (string * EvalStackValue) list
|
|
||||||
|
|
||||||
override this.ToString () =
|
override this.ToString () =
|
||||||
match this with
|
match this with
|
||||||
@@ -21,13 +21,7 @@ type EvalStackValue =
|
|||||||
| EvalStackValue.Float f -> $"Float(%f{f})"
|
| EvalStackValue.Float f -> $"Float(%f{f})"
|
||||||
| EvalStackValue.ManagedPointer managedPointerSource -> $"Pointer(%O{managedPointerSource})"
|
| EvalStackValue.ManagedPointer managedPointerSource -> $"Pointer(%O{managedPointerSource})"
|
||||||
| EvalStackValue.ObjectRef managedHeapAddress -> $"ObjectRef(%O{managedHeapAddress})"
|
| EvalStackValue.ObjectRef managedHeapAddress -> $"ObjectRef(%O{managedHeapAddress})"
|
||||||
| EvalStackValue.UserDefinedValueType evalStackValues ->
|
| EvalStackValue.UserDefinedValueType evalStackValues -> $"Struct(%O{evalStackValues})"
|
||||||
let desc =
|
|
||||||
evalStackValues
|
|
||||||
|> List.map (snd >> string<EvalStackValue>)
|
|
||||||
|> String.concat " | "
|
|
||||||
|
|
||||||
$"Struct(%s{desc})"
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module EvalStackValue =
|
module EvalStackValue =
|
||||||
@@ -55,6 +49,7 @@ module EvalStackValue =
|
|||||||
failwith "todo"
|
failwith "todo"
|
||||||
| NativeIntSource.ManagedPointer _ -> failwith "TODO"
|
| NativeIntSource.ManagedPointer _ -> failwith "TODO"
|
||||||
| NativeIntSource.FunctionPointer _ -> failwith "TODO"
|
| NativeIntSource.FunctionPointer _ -> failwith "TODO"
|
||||||
|
| NativeIntSource.FieldHandlePtr _ -> failwith "TODO"
|
||||||
| NativeIntSource.TypeHandlePtr _ -> failwith "TODO"
|
| NativeIntSource.TypeHandlePtr _ -> failwith "TODO"
|
||||||
| EvalStackValue.Float f -> failwith "todo"
|
| EvalStackValue.Float f -> failwith "todo"
|
||||||
| EvalStackValue.ManagedPointer managedPointerSource ->
|
| EvalStackValue.ManagedPointer managedPointerSource ->
|
||||||
@@ -83,7 +78,14 @@ module EvalStackValue =
|
|||||||
match value with
|
match value with
|
||||||
| EvalStackValue.Int32 i -> Some (int64<int> i)
|
| EvalStackValue.Int32 i -> Some (int64<int> i)
|
||||||
| EvalStackValue.Int64 i -> Some i
|
| EvalStackValue.Int64 i -> Some i
|
||||||
| EvalStackValue.NativeInt nativeIntSource -> failwith "todo"
|
| EvalStackValue.NativeInt src ->
|
||||||
|
match src with
|
||||||
|
| NativeIntSource.Verbatim int64 -> Some int64
|
||||||
|
| NativeIntSource.ManagedPointer ManagedPointerSource.Null -> Some 0L
|
||||||
|
| NativeIntSource.ManagedPointer _
|
||||||
|
| NativeIntSource.FunctionPointer _
|
||||||
|
| NativeIntSource.TypeHandlePtr _
|
||||||
|
| NativeIntSource.FieldHandlePtr _ -> failwith "refusing to convert pointer to int64"
|
||||||
| EvalStackValue.Float f -> failwith "todo"
|
| EvalStackValue.Float f -> failwith "todo"
|
||||||
| EvalStackValue.ManagedPointer managedPointerSource -> failwith "todo"
|
| EvalStackValue.ManagedPointer managedPointerSource -> failwith "todo"
|
||||||
| EvalStackValue.ObjectRef managedHeapAddress -> failwith "todo"
|
| EvalStackValue.ObjectRef managedHeapAddress -> failwith "todo"
|
||||||
@@ -100,6 +102,52 @@ module EvalStackValue =
|
|||||||
| EvalStackValue.ObjectRef managedHeapAddress -> failwith "todo"
|
| EvalStackValue.ObjectRef managedHeapAddress -> failwith "todo"
|
||||||
| EvalStackValue.UserDefinedValueType evalStackValues -> failwith "todo"
|
| EvalStackValue.UserDefinedValueType evalStackValues -> failwith "todo"
|
||||||
|
|
||||||
|
/// Then truncates to int32.
|
||||||
|
let convToUInt8 (value : EvalStackValue) : int32 option =
|
||||||
|
match value with
|
||||||
|
| EvalStackValue.Int32 (i : int32) ->
|
||||||
|
let v = (# "conv.u1" i : uint8 #)
|
||||||
|
Some (int32<uint8> v)
|
||||||
|
| EvalStackValue.Int64 int64 ->
|
||||||
|
let v = (# "conv.u1" int64 : uint8 #)
|
||||||
|
Some (int32<uint8> v)
|
||||||
|
| EvalStackValue.NativeInt nativeIntSource -> failwith "todo"
|
||||||
|
| EvalStackValue.Float f -> failwith "todo"
|
||||||
|
| EvalStackValue.ManagedPointer managedPointerSource -> failwith "todo"
|
||||||
|
| EvalStackValue.ObjectRef managedHeapAddress -> failwith "todo"
|
||||||
|
| EvalStackValue.UserDefinedValueType evalStackValues -> failwith "todo"
|
||||||
|
|
||||||
|
let rec ofCliType (v : CliType) : EvalStackValue =
|
||||||
|
match v with
|
||||||
|
| CliType.Numeric numeric ->
|
||||||
|
match numeric with
|
||||||
|
| CliNumericType.Int32 i -> EvalStackValue.Int32 i
|
||||||
|
| CliNumericType.Int64 i -> EvalStackValue.Int64 i
|
||||||
|
| CliNumericType.NativeInt i -> EvalStackValue.NativeInt i
|
||||||
|
// Sign-extend types int8 and int16
|
||||||
|
// Zero-extend unsigned int8/unsigned int16
|
||||||
|
| CliNumericType.Int8 b -> int32<int8> b |> EvalStackValue.Int32
|
||||||
|
| CliNumericType.UInt8 b -> int32<uint8> b |> EvalStackValue.Int32
|
||||||
|
| CliNumericType.Int16 s -> int32<int16> s |> EvalStackValue.Int32
|
||||||
|
| CliNumericType.UInt16 s -> int32<uint16> s |> EvalStackValue.Int32
|
||||||
|
| CliNumericType.Float32 f -> EvalStackValue.Float (float<float32> f)
|
||||||
|
| CliNumericType.Float64 f -> EvalStackValue.Float f
|
||||||
|
| CliNumericType.NativeFloat f -> EvalStackValue.Float f
|
||||||
|
| CliType.ObjectRef i ->
|
||||||
|
match i with
|
||||||
|
| None -> EvalStackValue.ManagedPointer ManagedPointerSource.Null
|
||||||
|
| Some i -> EvalStackValue.ManagedPointer (ManagedPointerSource.Heap i)
|
||||||
|
// Zero-extend bool/char
|
||||||
|
| CliType.Bool b -> int32 b |> EvalStackValue.Int32
|
||||||
|
| CliType.Char (high, low) -> int32 high * 256 + int32 low |> EvalStackValue.Int32
|
||||||
|
| CliType.RuntimePointer ptr ->
|
||||||
|
match ptr with
|
||||||
|
| CliRuntimePointer.Verbatim ptrInt -> NativeIntSource.Verbatim ptrInt |> EvalStackValue.NativeInt
|
||||||
|
| CliRuntimePointer.FieldRegistryHandle ptrInt ->
|
||||||
|
NativeIntSource.FieldHandlePtr ptrInt |> EvalStackValue.NativeInt
|
||||||
|
| CliRuntimePointer.Managed ptr -> ptr |> EvalStackValue.ManagedPointer
|
||||||
|
| CliType.ValueType fields -> EvalStackValue.UserDefinedValueType fields
|
||||||
|
|
||||||
let rec toCliTypeCoerced (target : CliType) (popped : EvalStackValue) : CliType =
|
let rec toCliTypeCoerced (target : CliType) (popped : EvalStackValue) : CliType =
|
||||||
match target with
|
match target with
|
||||||
| CliType.Numeric numeric ->
|
| CliType.Numeric numeric ->
|
||||||
@@ -107,7 +155,13 @@ module EvalStackValue =
|
|||||||
| CliNumericType.Int32 _ ->
|
| CliNumericType.Int32 _ ->
|
||||||
match popped with
|
match popped with
|
||||||
| EvalStackValue.Int32 i -> CliType.Numeric (CliNumericType.Int32 i)
|
| EvalStackValue.Int32 i -> CliType.Numeric (CliNumericType.Int32 i)
|
||||||
| EvalStackValue.UserDefinedValueType [ popped ] -> toCliTypeCoerced target (snd popped)
|
| EvalStackValue.UserDefinedValueType popped ->
|
||||||
|
let popped = CliValueType.DereferenceFieldAt 0 4 popped
|
||||||
|
// TODO: when we have a general mechanism to coerce CliTypes to each other,
|
||||||
|
// do that
|
||||||
|
match popped with
|
||||||
|
| CliType.Numeric (CliNumericType.Int32 i) -> CliType.Numeric (CliNumericType.Int32 i)
|
||||||
|
| _ -> failwith "TODO"
|
||||||
| i -> failwith $"TODO: %O{i}"
|
| i -> failwith $"TODO: %O{i}"
|
||||||
| CliNumericType.Int64 _ ->
|
| CliNumericType.Int64 _ ->
|
||||||
match popped with
|
match popped with
|
||||||
@@ -117,17 +171,34 @@ module EvalStackValue =
|
|||||||
| NativeIntSource.Verbatim i -> CliType.Numeric (CliNumericType.Int64 i)
|
| NativeIntSource.Verbatim i -> CliType.Numeric (CliNumericType.Int64 i)
|
||||||
| NativeIntSource.ManagedPointer ptr -> failwith "TODO"
|
| NativeIntSource.ManagedPointer ptr -> failwith "TODO"
|
||||||
| NativeIntSource.FunctionPointer f -> failwith $"TODO: {f}"
|
| NativeIntSource.FunctionPointer f -> failwith $"TODO: {f}"
|
||||||
// CliType.Numeric (CliNumericType.ProvenanceTrackedNativeInt64 f)
|
| NativeIntSource.FieldHandlePtr f -> failwith $"TODO: {f}"
|
||||||
| NativeIntSource.TypeHandlePtr f -> failwith $"TODO: {f}"
|
| NativeIntSource.TypeHandlePtr f -> failwith $"TODO: {f}"
|
||||||
// CliType.Numeric (CliNumericType.TypeHandlePtr f)
|
// CliType.Numeric (CliNumericType.TypeHandlePtr f)
|
||||||
| i -> failwith $"TODO: %O{i}"
|
| i -> failwith $"TODO: %O{i}"
|
||||||
| CliNumericType.NativeInt _ ->
|
| CliNumericType.NativeInt _ ->
|
||||||
match popped with
|
match popped with
|
||||||
| EvalStackValue.NativeInt s -> CliNumericType.NativeInt s
|
| EvalStackValue.NativeInt s -> CliNumericType.NativeInt s |> CliType.Numeric
|
||||||
| EvalStackValue.ManagedPointer ptrSrc ->
|
| EvalStackValue.ManagedPointer ptrSrc ->
|
||||||
CliNumericType.NativeInt (NativeIntSource.ManagedPointer ptrSrc)
|
CliNumericType.NativeInt (NativeIntSource.ManagedPointer ptrSrc)
|
||||||
|
|> CliType.Numeric
|
||||||
|
| EvalStackValue.UserDefinedValueType vt ->
|
||||||
|
let popped = CliValueType.DereferenceFieldAt 0 NATIVE_INT_SIZE vt
|
||||||
|
// TODO: when we have a general mechanism to coerce CliTypes to each other,
|
||||||
|
// do that
|
||||||
|
match popped with
|
||||||
|
| CliType.Numeric (CliNumericType.NativeInt i) -> CliType.Numeric (CliNumericType.NativeInt i)
|
||||||
|
| CliType.Numeric (CliNumericType.Int64 i) ->
|
||||||
|
CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.Verbatim i))
|
||||||
|
| CliType.RuntimePointer ptr ->
|
||||||
|
match ptr with
|
||||||
|
| CliRuntimePointer.Verbatim i ->
|
||||||
|
CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.Verbatim i))
|
||||||
|
| CliRuntimePointer.FieldRegistryHandle ptr ->
|
||||||
|
CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.FieldHandlePtr ptr))
|
||||||
|
| CliRuntimePointer.Managed src ->
|
||||||
|
CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.ManagedPointer src))
|
||||||
|
| _ -> failwith $"TODO: {popped}"
|
||||||
| _ -> failwith $"TODO: {popped}"
|
| _ -> failwith $"TODO: {popped}"
|
||||||
|> CliType.Numeric
|
|
||||||
| CliNumericType.NativeFloat f -> failwith "todo"
|
| CliNumericType.NativeFloat f -> failwith "todo"
|
||||||
| CliNumericType.Int8 _ ->
|
| CliNumericType.Int8 _ ->
|
||||||
match popped with
|
match popped with
|
||||||
@@ -156,34 +227,29 @@ module EvalStackValue =
|
|||||||
| CliType.ObjectRef _ ->
|
| CliType.ObjectRef _ ->
|
||||||
match popped with
|
match popped with
|
||||||
| EvalStackValue.ManagedPointer ptrSource ->
|
| EvalStackValue.ManagedPointer ptrSource ->
|
||||||
match ptrSource with
|
ptrSource |> CliRuntimePointer.Managed |> CliType.RuntimePointer
|
||||||
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
|
| EvalStackValue.ObjectRef ptr ->
|
||||||
CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, whichVar)
|
ManagedPointerSource.Heap ptr
|
||||||
|> CliRuntimePointer.Managed
|
|> CliRuntimePointer.Managed
|
||||||
|> CliType.RuntimePointer
|
|> CliType.RuntimePointer
|
||||||
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
|
|
||||||
CliRuntimePointerSource.Argument (sourceThread, methodFrame, whichVar)
|
|
||||||
|> CliRuntimePointer.Managed
|
|
||||||
|> CliType.RuntimePointer
|
|
||||||
| ManagedPointerSource.Heap managedHeapAddress -> CliType.ObjectRef (Some managedHeapAddress)
|
|
||||||
| ManagedPointerSource.Null -> CliType.ObjectRef None
|
|
||||||
| ManagedPointerSource.ArrayIndex (arr, ind) ->
|
|
||||||
CliType.RuntimePointer (CliRuntimePointer.Managed (CliRuntimePointerSource.ArrayIndex (arr, ind)))
|
|
||||||
| EvalStackValue.NativeInt nativeIntSource ->
|
| EvalStackValue.NativeInt nativeIntSource ->
|
||||||
match nativeIntSource with
|
match nativeIntSource with
|
||||||
| NativeIntSource.Verbatim 0L -> CliType.ObjectRef None
|
| NativeIntSource.Verbatim 0L -> CliType.ObjectRef None
|
||||||
| NativeIntSource.Verbatim i -> failwith $"refusing to interpret verbatim native int {i} as a pointer"
|
| NativeIntSource.Verbatim i -> failwith $"refusing to interpret verbatim native int {i} as a pointer"
|
||||||
| NativeIntSource.FunctionPointer _ -> failwith "TODO"
|
| NativeIntSource.FunctionPointer _ -> failwith "TODO"
|
||||||
| NativeIntSource.TypeHandlePtr _ -> failwith "refusing to interpret type handle ID as an object ref"
|
| NativeIntSource.TypeHandlePtr _ -> failwith "refusing to interpret type handle ID as an object ref"
|
||||||
|
| NativeIntSource.FieldHandlePtr _ -> failwith "refusing to interpret field handle ID as an object ref"
|
||||||
| NativeIntSource.ManagedPointer ptr ->
|
| NativeIntSource.ManagedPointer ptr ->
|
||||||
match ptr with
|
match ptr with
|
||||||
| ManagedPointerSource.Null -> CliType.ObjectRef None
|
| ManagedPointerSource.Null -> CliType.ObjectRef None
|
||||||
| ManagedPointerSource.Heap s -> CliType.ObjectRef (Some s)
|
| ManagedPointerSource.Heap s -> CliType.ObjectRef (Some s)
|
||||||
| _ -> failwith "TODO"
|
| _ -> failwith "TODO"
|
||||||
| EvalStackValue.UserDefinedValueType fields ->
|
| EvalStackValue.UserDefinedValueType obj ->
|
||||||
match fields with
|
let popped = CliValueType.DereferenceFieldAt 0 NATIVE_INT_SIZE obj
|
||||||
| [ esv ] -> toCliTypeCoerced target (snd esv)
|
|
||||||
| fields -> failwith $"TODO: don't know how to coerce struct of {fields} to a pointer"
|
match popped with
|
||||||
|
| CliType.ObjectRef r -> CliType.ObjectRef r
|
||||||
|
| _ -> failwith "TODO"
|
||||||
| _ -> failwith $"TODO: {popped}"
|
| _ -> failwith $"TODO: {popped}"
|
||||||
| CliType.Bool _ ->
|
| CliType.Bool _ ->
|
||||||
match popped with
|
match popped with
|
||||||
@@ -195,41 +261,19 @@ module EvalStackValue =
|
|||||||
| i -> failwith $"TODO: %O{i}"
|
| i -> failwith $"TODO: %O{i}"
|
||||||
| CliType.RuntimePointer _ ->
|
| CliType.RuntimePointer _ ->
|
||||||
match popped with
|
match popped with
|
||||||
| EvalStackValue.ManagedPointer src ->
|
| EvalStackValue.ManagedPointer src -> src |> CliRuntimePointer.Managed |> CliType.RuntimePointer
|
||||||
match src with
|
|
||||||
| ManagedPointerSource.Heap addr -> CliType.ofManagedObject addr
|
|
||||||
| ManagedPointerSource.Null -> CliType.ObjectRef None
|
|
||||||
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, var) ->
|
|
||||||
CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, var)
|
|
||||||
|> CliRuntimePointer.Managed
|
|
||||||
|> CliType.RuntimePointer
|
|
||||||
| ManagedPointerSource.Argument (sourceThread, methodFrame, var) ->
|
|
||||||
CliRuntimePointerSource.Argument (sourceThread, methodFrame, var)
|
|
||||||
|> CliRuntimePointer.Managed
|
|
||||||
|> CliType.RuntimePointer
|
|
||||||
| ManagedPointerSource.ArrayIndex (arr, index) ->
|
|
||||||
CliRuntimePointerSource.ArrayIndex (arr, index)
|
|
||||||
|> CliRuntimePointer.Managed
|
|
||||||
|> CliType.RuntimePointer
|
|
||||||
| EvalStackValue.NativeInt intSrc ->
|
| EvalStackValue.NativeInt intSrc ->
|
||||||
match intSrc with
|
match intSrc with
|
||||||
| NativeIntSource.Verbatim i -> CliType.RuntimePointer (CliRuntimePointer.Unmanaged i)
|
| NativeIntSource.Verbatim i -> CliType.RuntimePointer (CliRuntimePointer.Verbatim i)
|
||||||
| NativeIntSource.ManagedPointer src ->
|
| NativeIntSource.ManagedPointer src -> src |> CliRuntimePointer.Managed |> CliType.RuntimePointer
|
||||||
match src with
|
|
||||||
| ManagedPointerSource.Heap src ->
|
|
||||||
CliType.RuntimePointer (CliRuntimePointer.Managed (CliRuntimePointerSource.Heap src))
|
|
||||||
| ManagedPointerSource.Null ->
|
|
||||||
CliType.RuntimePointer (CliRuntimePointer.Managed CliRuntimePointerSource.Null)
|
|
||||||
| ManagedPointerSource.LocalVariable (a, b, c) ->
|
|
||||||
CliType.RuntimePointer (
|
|
||||||
CliRuntimePointer.Managed (CliRuntimePointerSource.LocalVariable (a, b, c))
|
|
||||||
)
|
|
||||||
| ManagedPointerSource.Argument (a, b, c) ->
|
|
||||||
CliType.RuntimePointer (CliRuntimePointer.Managed (CliRuntimePointerSource.Argument (a, b, c)))
|
|
||||||
| ManagedPointerSource.ArrayIndex _ -> failwith "TODO"
|
|
||||||
| NativeIntSource.FunctionPointer methodInfo ->
|
| NativeIntSource.FunctionPointer methodInfo ->
|
||||||
CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.FunctionPointer methodInfo))
|
CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.FunctionPointer methodInfo))
|
||||||
| NativeIntSource.TypeHandlePtr int64 -> failwith "todo"
|
| NativeIntSource.TypeHandlePtr int64 -> failwith "todo"
|
||||||
|
| NativeIntSource.FieldHandlePtr int64 -> failwith "todo"
|
||||||
|
| EvalStackValue.ObjectRef addr ->
|
||||||
|
ManagedPointerSource.Heap addr
|
||||||
|
|> CliRuntimePointer.Managed
|
||||||
|
|> CliType.RuntimePointer
|
||||||
| _ -> failwith $"TODO: %O{popped}"
|
| _ -> failwith $"TODO: %O{popped}"
|
||||||
| CliType.Char _ ->
|
| CliType.Char _ ->
|
||||||
match popped with
|
match popped with
|
||||||
@@ -240,74 +284,38 @@ module EvalStackValue =
|
|||||||
| popped -> failwith $"Unexpectedly wanted a char from {popped}"
|
| popped -> failwith $"Unexpectedly wanted a char from {popped}"
|
||||||
| CliType.ValueType vt ->
|
| CliType.ValueType vt ->
|
||||||
match popped with
|
match popped with
|
||||||
| EvalStackValue.UserDefinedValueType popped ->
|
| EvalStackValue.UserDefinedValueType popped' ->
|
||||||
if vt.Fields.Length <> popped.Length then
|
match CliValueType.TrySequentialFields vt, CliValueType.TrySequentialFields popped' with
|
||||||
failwith
|
| Some vt, Some popped ->
|
||||||
$"mismatch: popped value type {popped} (length %i{popped.Length}) into {vt} (length %i{vt.Fields.Length})"
|
if vt.Length <> popped.Length then
|
||||||
|
failwith
|
||||||
|
$"mismatch: popped value type {popped} (length %i{popped.Length}) into {vt} (length %i{vt.Length})"
|
||||||
|
|
||||||
let fields =
|
(vt, popped)
|
||||||
List.map2
|
||> List.map2 (fun field1 popped ->
|
||||||
(fun (name1, v1) (name2, v2) ->
|
if field1.Name <> popped.Name then
|
||||||
if name1 <> name2 then
|
failwith $"TODO: name mismatch, {field1.Name} vs {popped.Name}"
|
||||||
failwith $"TODO: name mismatch, {name1} vs {name2}"
|
|
||||||
|
|
||||||
name1, toCliTypeCoerced v1 v2
|
if field1.Offset <> popped.Offset then
|
||||||
)
|
failwith $"TODO: offset mismatch for {field1.Name}, {field1.Offset} vs {popped.Offset}"
|
||||||
vt.Fields
|
|
||||||
popped
|
|
||||||
|
|
||||||
{
|
let contents = toCliTypeCoerced field1.Contents (ofCliType popped.Contents)
|
||||||
Fields = fields
|
|
||||||
}
|
{
|
||||||
|> CliType.ValueType
|
CliField.Name = field1.Name
|
||||||
|
Contents = contents
|
||||||
|
Offset = field1.Offset
|
||||||
|
Type = field1.Type
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|> CliValueType.OfFields popped'.Layout
|
||||||
|
|> CliType.ValueType
|
||||||
|
| _, _ -> failwith "TODO: overlapping fields going onto eval stack"
|
||||||
| popped ->
|
| popped ->
|
||||||
match vt.Fields with
|
match CliValueType.TryExactlyOneField vt with
|
||||||
| [ _, target ] -> toCliTypeCoerced target popped
|
| Some field -> toCliTypeCoerced field.Contents popped
|
||||||
| _ -> failwith $"TODO: {popped} into value type {target}"
|
| _ -> failwith $"TODO: {popped} into value type {target}"
|
||||||
|
|
||||||
let rec ofCliType (v : CliType) : EvalStackValue =
|
|
||||||
match v with
|
|
||||||
| CliType.Numeric numeric ->
|
|
||||||
match numeric with
|
|
||||||
| CliNumericType.Int32 i -> EvalStackValue.Int32 i
|
|
||||||
| CliNumericType.Int64 i -> EvalStackValue.Int64 i
|
|
||||||
| CliNumericType.NativeInt i -> EvalStackValue.NativeInt i
|
|
||||||
// Sign-extend types int8 and int16
|
|
||||||
// Zero-extend unsigned int8/unsigned int16
|
|
||||||
| CliNumericType.Int8 b -> int32<int8> b |> EvalStackValue.Int32
|
|
||||||
| CliNumericType.UInt8 b -> int32<uint8> b |> EvalStackValue.Int32
|
|
||||||
| CliNumericType.Int16 s -> int32<int16> s |> EvalStackValue.Int32
|
|
||||||
| CliNumericType.UInt16 s -> int32<uint16> s |> EvalStackValue.Int32
|
|
||||||
| CliNumericType.Float32 f -> EvalStackValue.Float (float<float32> f)
|
|
||||||
| CliNumericType.Float64 f -> EvalStackValue.Float f
|
|
||||||
| CliNumericType.NativeFloat f -> EvalStackValue.Float f
|
|
||||||
| CliType.ObjectRef i ->
|
|
||||||
match i with
|
|
||||||
| None -> EvalStackValue.ManagedPointer ManagedPointerSource.Null
|
|
||||||
| Some i -> EvalStackValue.ManagedPointer (ManagedPointerSource.Heap i)
|
|
||||||
// Zero-extend bool/char
|
|
||||||
| CliType.Bool b -> int32 b |> EvalStackValue.Int32
|
|
||||||
| CliType.Char (high, low) -> int32 high * 256 + int32 low |> EvalStackValue.Int32
|
|
||||||
| CliType.RuntimePointer ptr ->
|
|
||||||
match ptr with
|
|
||||||
| CliRuntimePointer.Unmanaged ptrInt -> NativeIntSource.Verbatim ptrInt |> EvalStackValue.NativeInt
|
|
||||||
| CliRuntimePointer.Managed ptr ->
|
|
||||||
match ptr with
|
|
||||||
| CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, var) ->
|
|
||||||
ManagedPointerSource.LocalVariable (sourceThread, methodFrame, var)
|
|
||||||
|> EvalStackValue.ManagedPointer
|
|
||||||
| CliRuntimePointerSource.ArrayIndex (arr, ind) ->
|
|
||||||
ManagedPointerSource.ArrayIndex (arr, ind) |> EvalStackValue.ManagedPointer
|
|
||||||
| CliRuntimePointerSource.Argument (sourceThread, methodFrame, var) ->
|
|
||||||
ManagedPointerSource.Argument (sourceThread, methodFrame, var)
|
|
||||||
|> EvalStackValue.ManagedPointer
|
|
||||||
| CliRuntimePointerSource.Heap addr -> EvalStackValue.ObjectRef addr
|
|
||||||
| CliRuntimePointerSource.Null -> EvalStackValue.ManagedPointer ManagedPointerSource.Null
|
|
||||||
| CliType.ValueType fields ->
|
|
||||||
fields.Fields
|
|
||||||
|> List.map (fun (name, f) -> name, ofCliType f)
|
|
||||||
|> EvalStackValue.UserDefinedValueType
|
|
||||||
|
|
||||||
type EvalStack =
|
type EvalStack =
|
||||||
{
|
{
|
||||||
Values : EvalStackValue list
|
Values : EvalStackValue list
|
||||||
|
@@ -123,11 +123,14 @@ module EvalStackValueComparisons =
|
|||||||
let rec ceq (var1 : EvalStackValue) (var2 : EvalStackValue) : bool =
|
let rec ceq (var1 : EvalStackValue) (var2 : EvalStackValue) : bool =
|
||||||
// Table III.4
|
// Table III.4
|
||||||
match var1, var2 with
|
match var1, var2 with
|
||||||
| EvalStackValue.UserDefinedValueType [ _, u ], v -> ceq u v
|
| EvalStackValue.UserDefinedValueType var1, v ->
|
||||||
| u, EvalStackValue.UserDefinedValueType [ _, v ] -> ceq u v
|
match CliValueType.TryExactlyOneField var1 with
|
||||||
| EvalStackValue.UserDefinedValueType [], EvalStackValue.UserDefinedValueType [] -> true
|
| None -> failwith "TODO"
|
||||||
| EvalStackValue.UserDefinedValueType _, _
|
| Some var1 -> ceq (EvalStackValue.ofCliType var1.Contents) v
|
||||||
| _, EvalStackValue.UserDefinedValueType _ -> failwith $"bad ceq: {var1} vs {var2}"
|
| u, EvalStackValue.UserDefinedValueType var2 ->
|
||||||
|
match CliValueType.TryExactlyOneField var2 with
|
||||||
|
| None -> failwith "TODO"
|
||||||
|
| Some var2 -> ceq u (EvalStackValue.ofCliType var2.Contents)
|
||||||
| EvalStackValue.Int32 var1, EvalStackValue.Int32 var2 -> var1 = var2
|
| EvalStackValue.Int32 var1, EvalStackValue.Int32 var2 -> var1 = var2
|
||||||
| EvalStackValue.Int32 var1, EvalStackValue.NativeInt var2 -> failwith "TODO: int32 CEQ nativeint"
|
| EvalStackValue.Int32 var1, EvalStackValue.NativeInt var2 -> failwith "TODO: int32 CEQ nativeint"
|
||||||
| EvalStackValue.Int32 _, _ -> failwith $"bad ceq: Int32 vs {var2}"
|
| EvalStackValue.Int32 _, _ -> failwith $"bad ceq: Int32 vs {var2}"
|
||||||
@@ -151,6 +154,16 @@ module EvalStackValueComparisons =
|
|||||||
failwith $"TODO (CEQ): nativeint vs managed pointer"
|
failwith $"TODO (CEQ): nativeint vs managed pointer"
|
||||||
| EvalStackValue.NativeInt _, _ -> failwith $"bad ceq: NativeInt vs {var2}"
|
| EvalStackValue.NativeInt _, _ -> failwith $"bad ceq: NativeInt vs {var2}"
|
||||||
| EvalStackValue.ObjectRef var1, EvalStackValue.ObjectRef var2 -> var1 = var2
|
| EvalStackValue.ObjectRef var1, EvalStackValue.ObjectRef var2 -> var1 = var2
|
||||||
|
| EvalStackValue.ManagedPointer src, EvalStackValue.ObjectRef var1
|
||||||
|
| EvalStackValue.ObjectRef var1, EvalStackValue.ManagedPointer src ->
|
||||||
|
match src with
|
||||||
|
| ManagedPointerSource.Heap src -> src = var1
|
||||||
|
| ManagedPointerSource.Null -> false
|
||||||
|
| ManagedPointerSource.Field _
|
||||||
|
| ManagedPointerSource.LocalVariable _
|
||||||
|
| ManagedPointerSource.Argument _ -> false
|
||||||
|
| ManagedPointerSource.ArrayIndex (arr, index) -> failwith "todo"
|
||||||
|
| ManagedPointerSource.InterpretedAsType (src, ty) -> failwith "todo"
|
||||||
| EvalStackValue.ObjectRef _, _ -> failwith $"bad ceq: ObjectRef vs {var2}"
|
| EvalStackValue.ObjectRef _, _ -> failwith $"bad ceq: ObjectRef vs {var2}"
|
||||||
| EvalStackValue.ManagedPointer var1, EvalStackValue.ManagedPointer var2 -> var1 = var2
|
| EvalStackValue.ManagedPointer var1, EvalStackValue.ManagedPointer var2 -> var1 = var2
|
||||||
| EvalStackValue.ManagedPointer var1, EvalStackValue.NativeInt var2 ->
|
| EvalStackValue.ManagedPointer var1, EvalStackValue.NativeInt var2 ->
|
||||||
|
@@ -62,6 +62,7 @@ module System_Threading_Monitor =
|
|||||||
match lockObj with
|
match lockObj with
|
||||||
| EvalStackValue.ManagedPointer ManagedPointerSource.Null ->
|
| EvalStackValue.ManagedPointer ManagedPointerSource.Null ->
|
||||||
failwith "TODO: throw ArgumentNullException"
|
failwith "TODO: throw ArgumentNullException"
|
||||||
|
| EvalStackValue.ObjectRef addr
|
||||||
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) ->
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) ->
|
||||||
match IlMachineState.getSyncBlock addr state with
|
match IlMachineState.getSyncBlock addr state with
|
||||||
| SyncBlock.Free ->
|
| SyncBlock.Free ->
|
||||||
@@ -87,6 +88,8 @@ module System_Threading_Monitor =
|
|||||||
failwith "not really expecting to *edit* an argument..."
|
failwith "not really expecting to *edit* an argument..."
|
||||||
| ManagedPointerSource.Heap addr -> failwith "todo: managed heap"
|
| ManagedPointerSource.Heap addr -> failwith "todo: managed heap"
|
||||||
| ManagedPointerSource.ArrayIndex _ -> failwith "todo: array index"
|
| ManagedPointerSource.ArrayIndex _ -> failwith "todo: array index"
|
||||||
|
| ManagedPointerSource.Field (managedPointerSource, fieldName) -> failwith "todo"
|
||||||
|
| ManagedPointerSource.InterpretedAsType _ -> failwith "TODO"
|
||||||
|
|
||||||
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
|
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
|
||||||
|
|
||||||
@@ -100,6 +103,7 @@ module System_Threading_Monitor =
|
|||||||
match lockObj with
|
match lockObj with
|
||||||
| EvalStackValue.ManagedPointer ManagedPointerSource.Null ->
|
| EvalStackValue.ManagedPointer ManagedPointerSource.Null ->
|
||||||
failwith "TODO: throw ArgumentNullException"
|
failwith "TODO: throw ArgumentNullException"
|
||||||
|
| EvalStackValue.ObjectRef addr
|
||||||
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) ->
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) ->
|
||||||
match IlMachineState.getSyncBlock addr state with
|
match IlMachineState.getSyncBlock addr state with
|
||||||
| SyncBlock.Free -> failwith "TODO: throw SynchronizationLockException"
|
| SyncBlock.Free -> failwith "TODO: throw SynchronizationLockException"
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
namespace WoofWare.PawPrint
|
namespace WoofWare.PawPrint
|
||||||
|
|
||||||
|
open System.Collections.Immutable
|
||||||
open System.Reflection
|
open System.Reflection
|
||||||
open System.Reflection.Metadata
|
open System.Reflection.Metadata
|
||||||
|
|
||||||
@@ -33,8 +34,9 @@ module FieldHandleRegistry =
|
|||||||
/// Returns a (struct) System.RuntimeFieldHandle, with its contents (reference type) freshly allocated if necessary.
|
/// Returns a (struct) System.RuntimeFieldHandle, with its contents (reference type) freshly allocated if necessary.
|
||||||
let getOrAllocate
|
let getOrAllocate
|
||||||
(baseClassTypes : BaseClassTypes<'corelib>)
|
(baseClassTypes : BaseClassTypes<'corelib>)
|
||||||
|
(allConcreteTypes : AllConcreteTypes)
|
||||||
(allocState : 'allocState)
|
(allocState : 'allocState)
|
||||||
(allocate : (string * CliType) list -> 'allocState -> ManagedHeapAddress * 'allocState)
|
(allocate : CliValueType -> 'allocState -> ManagedHeapAddress * 'allocState)
|
||||||
(declaringAssy : AssemblyName)
|
(declaringAssy : AssemblyName)
|
||||||
(declaringType : ConcreteTypeHandle)
|
(declaringType : ConcreteTypeHandle)
|
||||||
(handle : FieldDefinitionHandle)
|
(handle : FieldDefinitionHandle)
|
||||||
@@ -54,8 +56,20 @@ module FieldHandleRegistry =
|
|||||||
failwith $"unexpected field name %s{field.Name} for BCL type RuntimeFieldHandle"
|
failwith $"unexpected field name %s{field.Name} for BCL type RuntimeFieldHandle"
|
||||||
|
|
||||||
{
|
{
|
||||||
Fields = [ "m_ptr", CliType.ofManagedObject runtimeFieldInfoStub ]
|
Name = "m_ptr"
|
||||||
|
Contents = CliType.ofManagedObject runtimeFieldInfoStub
|
||||||
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
allConcreteTypes
|
||||||
|
(baseClassTypes.RuntimeFieldInfoStub.Assembly,
|
||||||
|
baseClassTypes.RuntimeFieldInfoStub.Namespace,
|
||||||
|
baseClassTypes.RuntimeFieldInfoStub.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
}
|
}
|
||||||
|
|> List.singleton
|
||||||
|
|> CliValueType.OfFields Layout.Default
|
||||||
|> CliType.ValueType
|
|> CliType.ValueType
|
||||||
|
|
||||||
let handle =
|
let handle =
|
||||||
@@ -81,22 +95,93 @@ module FieldHandleRegistry =
|
|||||||
| TypeDefn.PrimitiveType PrimitiveType.IntPtr -> ()
|
| TypeDefn.PrimitiveType PrimitiveType.IntPtr -> ()
|
||||||
| s -> failwith $"bad sig: {s}"
|
| s -> failwith $"bad sig: {s}"
|
||||||
|
|
||||||
|
// https://github.com/dotnet/runtime/blob/2b21c73fa2c32fa0195e4a411a435dda185efd08/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs#L1380
|
||||||
{
|
{
|
||||||
Fields = [ "m_handle", CliType.RuntimePointer (CliRuntimePointer.Unmanaged newHandle) ]
|
Name = "m_handle"
|
||||||
|
Contents = CliType.RuntimePointer (CliRuntimePointer.FieldRegistryHandle newHandle)
|
||||||
|
Offset = None // no struct layout was specified
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
allConcreteTypes
|
||||||
|
(baseClassTypes.IntPtr.Assembly,
|
||||||
|
baseClassTypes.IntPtr.Namespace,
|
||||||
|
baseClassTypes.IntPtr.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
}
|
}
|
||||||
|
|> List.singleton
|
||||||
|
|> CliValueType.OfFields Layout.Default
|
||||||
|> CliType.ValueType
|
|> CliType.ValueType
|
||||||
|
|
||||||
|
// https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs#L1074
|
||||||
let runtimeFieldInfoStub =
|
let runtimeFieldInfoStub =
|
||||||
|
let objType =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
allConcreteTypes
|
||||||
|
(baseClassTypes.Object.Assembly,
|
||||||
|
baseClassTypes.Object.Namespace,
|
||||||
|
baseClassTypes.Object.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
|
|
||||||
|
let intType =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
allConcreteTypes
|
||||||
|
(baseClassTypes.Int32.Assembly,
|
||||||
|
baseClassTypes.Int32.Namespace,
|
||||||
|
baseClassTypes.Int32.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
|
|
||||||
|
// LayoutKind.Sequential
|
||||||
[
|
[
|
||||||
// If we ever implement a GC, something should change here
|
// If we ever implement a GC, something should change here
|
||||||
"m_keepalive", CliType.ObjectRef None
|
{
|
||||||
"m_c", CliType.ObjectRef None
|
Name = "m_keepalive"
|
||||||
"m_d", CliType.ObjectRef None
|
Contents = CliType.ObjectRef None
|
||||||
"m_b", CliType.Numeric (CliNumericType.Int32 0)
|
Offset = None
|
||||||
"m_e", CliType.ObjectRef None
|
Type = objType
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Name = "m_c"
|
||||||
|
Contents = CliType.ObjectRef None
|
||||||
|
Offset = None
|
||||||
|
Type = objType
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Name = "m_d"
|
||||||
|
Contents = CliType.ObjectRef None
|
||||||
|
Offset = None
|
||||||
|
Type = objType
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Name = "m_b"
|
||||||
|
Contents = CliType.Numeric (CliNumericType.Int32 0)
|
||||||
|
Offset = None
|
||||||
|
Type = intType
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Name = "m_e"
|
||||||
|
Contents = CliType.ObjectRef None
|
||||||
|
Offset = None
|
||||||
|
Type = objType
|
||||||
|
}
|
||||||
// RuntimeFieldHandleInternal: https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs#L1048
|
// RuntimeFieldHandleInternal: https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs#L1048
|
||||||
"m_fieldHandle", runtimeFieldHandleInternal
|
{
|
||||||
|
Name = "m_fieldHandle"
|
||||||
|
Contents = runtimeFieldHandleInternal
|
||||||
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
allConcreteTypes
|
||||||
|
(baseClassTypes.RuntimeFieldHandleInternal.Assembly,
|
||||||
|
baseClassTypes.RuntimeFieldHandleInternal.Namespace,
|
||||||
|
baseClassTypes.RuntimeFieldHandleInternal.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|> CliValueType.OfFields Layout.Default // explicitly sequential but no custom packing size
|
||||||
|
|
||||||
let alloc, state = allocate runtimeFieldInfoStub allocState
|
let alloc, state = allocate runtimeFieldInfoStub allocState
|
||||||
|
|
||||||
|
@@ -149,7 +149,73 @@ type StateLoadResult =
|
|||||||
module IlMachineState =
|
module IlMachineState =
|
||||||
type private Dummy = class end
|
type private Dummy = class end
|
||||||
|
|
||||||
|
let private loadAssembly'
|
||||||
|
(loggerFactory : ILoggerFactory)
|
||||||
|
(dotnetRuntimeDirs : string seq)
|
||||||
|
(referencedInAssembly : DumpedAssembly)
|
||||||
|
(r : AssemblyReferenceHandle)
|
||||||
|
(assemblies : ImmutableDictionary<string, DumpedAssembly>)
|
||||||
|
=
|
||||||
|
let assemblyRef = referencedInAssembly.AssemblyReferences.[r]
|
||||||
|
let assemblyName = assemblyRef.Name
|
||||||
|
|
||||||
|
match assemblies.TryGetValue assemblyName.FullName with
|
||||||
|
| true, v -> v, assemblyName
|
||||||
|
| false, _ ->
|
||||||
|
let logger = loggerFactory.CreateLogger typeof<Dummy>.DeclaringType
|
||||||
|
|
||||||
|
let assy =
|
||||||
|
dotnetRuntimeDirs
|
||||||
|
|> Seq.choose (fun dir ->
|
||||||
|
let file = Path.Combine (dir, assemblyName.Name + ".dll")
|
||||||
|
|
||||||
|
try
|
||||||
|
use f = File.OpenRead file
|
||||||
|
logger.LogInformation ("Loading assembly from file {AssemblyFileLoadPath}", file)
|
||||||
|
Assembly.read loggerFactory (Some file) f |> Some
|
||||||
|
with :? FileNotFoundException ->
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
match assy |> List.tryHead with
|
||||||
|
| None -> failwith $"Could not find a readable DLL in any runtime dir with name %s{assemblyName.Name}.dll"
|
||||||
|
| Some assy -> assy, assemblyName
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new IlMachineState which has loaded the given assembly.
|
||||||
|
/// This involves reading assemblies from the disk and doing a complete parse of them, so it might be quite slow!
|
||||||
|
///
|
||||||
|
/// This function doesn't do anything if the referenced assembly has already been loaded.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="loggerFactory">LoggerFactory into which to emit logs.</param>
|
||||||
|
/// <param name="referencedInAssembly">The assembly which contains an AssemblyReference which causes us to want to load a new assembly.</param>
|
||||||
|
/// <param name="r">The AssemblyReferenceHandle pointing at an assembly we want to load. *Important*: this is an AssemblyReferenceHandle from <c>referencedInAssembly</c>; in general, AssemblyReferenceHandles are only well-defined if you know what assembly they were defined in.</param>
|
||||||
|
/// <param name="state">The immutable state to augment with the new assembly.</param>
|
||||||
|
let loadAssembly
|
||||||
|
(loggerFactory : ILoggerFactory)
|
||||||
|
(referencedInAssembly : DumpedAssembly)
|
||||||
|
(r : AssemblyReferenceHandle)
|
||||||
|
(state : IlMachineState)
|
||||||
|
: IlMachineState * DumpedAssembly * AssemblyName
|
||||||
|
=
|
||||||
|
let dumped, assy =
|
||||||
|
loadAssembly' loggerFactory state.DotnetRuntimeDirs referencedInAssembly r state._LoadedAssemblies
|
||||||
|
|
||||||
|
state.WithLoadedAssembly assy dumped, dumped, assy
|
||||||
|
|
||||||
|
let private loader (loggerFactory : ILoggerFactory) (state : IlMachineState) : IAssemblyLoad =
|
||||||
|
{ new IAssemblyLoad with
|
||||||
|
member _.LoadAssembly loaded assyName ref =
|
||||||
|
let targetAssy, name =
|
||||||
|
loadAssembly' loggerFactory state.DotnetRuntimeDirs loaded.[assyName.FullName] ref loaded
|
||||||
|
|
||||||
|
let newAssys = loaded.SetItem (name.FullName, targetAssy)
|
||||||
|
newAssys, targetAssy
|
||||||
|
}
|
||||||
|
|
||||||
let concretizeType
|
let concretizeType
|
||||||
|
(loggerFactory : ILoggerFactory)
|
||||||
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
||||||
(state : IlMachineState)
|
(state : IlMachineState)
|
||||||
(declaringAssembly : AssemblyName)
|
(declaringAssembly : AssemblyName)
|
||||||
@@ -169,14 +235,7 @@ module IlMachineState =
|
|||||||
let handle, ctx =
|
let handle, ctx =
|
||||||
TypeConcretization.concretizeType
|
TypeConcretization.concretizeType
|
||||||
ctx
|
ctx
|
||||||
(fun assyName ref ->
|
(loader loggerFactory state)
|
||||||
let currentAssy = state.LoadedAssembly assyName |> Option.get
|
|
||||||
|
|
||||||
let targetAssy =
|
|
||||||
currentAssy.AssemblyReferences.[ref].Name |> state.LoadedAssembly |> Option.get
|
|
||||||
|
|
||||||
state._LoadedAssemblies, targetAssy
|
|
||||||
)
|
|
||||||
declaringAssembly
|
declaringAssembly
|
||||||
typeGenerics
|
typeGenerics
|
||||||
methodGenerics
|
methodGenerics
|
||||||
@@ -190,51 +249,6 @@ module IlMachineState =
|
|||||||
|
|
||||||
state, handle
|
state, handle
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new IlMachineState which has loaded the given assembly.
|
|
||||||
/// This involves reading assemblies from the disk and doing a complete parse of them, so it might be quite slow!
|
|
||||||
///
|
|
||||||
/// This function doesn't do anything if the referenced assembly has already been loaded.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="loggerFactory">LoggerFactory into which to emit logs.</param>
|
|
||||||
/// <param name="referencedInAssembly">The assembly which contains an AssemblyReference which causes us to want to load a new assembly.</param>
|
|
||||||
/// <param name="r">The AssemblyReferenceHandle pointing at an assembly we want to load. *Important*: this is an AssemblyReferenceHandle from <c>referencedInAssembly</c>; in general, AssemblyReferenceHandles are only well-defined if you know what assembly they were defined in.</param>
|
|
||||||
/// <param name="state">The immutable state to augment with the new assembly.</param>
|
|
||||||
let loadAssembly
|
|
||||||
(loggerFactory : ILoggerFactory)
|
|
||||||
(referencedInAssembly : DumpedAssembly)
|
|
||||||
(r : AssemblyReferenceHandle)
|
|
||||||
(state : IlMachineState)
|
|
||||||
: IlMachineState * DumpedAssembly * AssemblyName
|
|
||||||
=
|
|
||||||
let assemblyRef = referencedInAssembly.AssemblyReferences.[r]
|
|
||||||
let assemblyName = assemblyRef.Name
|
|
||||||
|
|
||||||
match state.LoadedAssembly assemblyName with
|
|
||||||
| Some v -> state, v, assemblyName
|
|
||||||
| None ->
|
|
||||||
let logger = loggerFactory.CreateLogger typeof<Dummy>.DeclaringType
|
|
||||||
|
|
||||||
let assy =
|
|
||||||
state.DotnetRuntimeDirs
|
|
||||||
|> Seq.choose (fun dir ->
|
|
||||||
let file = Path.Combine (dir, assemblyName.Name + ".dll")
|
|
||||||
|
|
||||||
try
|
|
||||||
use f = File.OpenRead file
|
|
||||||
logger.LogInformation ("Loading assembly from file {AssemblyFileLoadPath}", file)
|
|
||||||
Assembly.read loggerFactory (Some file) f |> Some
|
|
||||||
with :? FileNotFoundException ->
|
|
||||||
None
|
|
||||||
)
|
|
||||||
|> Seq.toList
|
|
||||||
|
|
||||||
match assy |> List.tryHead with
|
|
||||||
| None -> failwith $"Could not find a readable DLL in any runtime dir with name %s{assemblyName.Name}.dll"
|
|
||||||
| Some assy ->
|
|
||||||
|
|
||||||
state.WithLoadedAssembly assemblyName assy, assy, assemblyName
|
|
||||||
|
|
||||||
let rec internal resolveTypeFromName
|
let rec internal resolveTypeFromName
|
||||||
(loggerFactory : ILoggerFactory)
|
(loggerFactory : ILoggerFactory)
|
||||||
(ns : string option)
|
(ns : string option)
|
||||||
@@ -284,7 +298,7 @@ module IlMachineState =
|
|||||||
(state : IlMachineState)
|
(state : IlMachineState)
|
||||||
: IlMachineState * DumpedAssembly * WoofWare.PawPrint.TypeInfo<TypeDefn, TypeDefn>
|
: IlMachineState * DumpedAssembly * WoofWare.PawPrint.TypeInfo<TypeDefn, TypeDefn>
|
||||||
=
|
=
|
||||||
match Assembly.resolveTypeRef state._LoadedAssemblies referencedInAssembly target typeGenericArgs with
|
match Assembly.resolveTypeRef state._LoadedAssemblies referencedInAssembly typeGenericArgs target with
|
||||||
| TypeResolutionResult.Resolved (assy, typeDef) -> state, assy, typeDef
|
| TypeResolutionResult.Resolved (assy, typeDef) -> state, assy, typeDef
|
||||||
| TypeResolutionResult.FirstLoadAssy loadFirst ->
|
| TypeResolutionResult.FirstLoadAssy loadFirst ->
|
||||||
let state, _, _ =
|
let state, _, _ =
|
||||||
@@ -335,36 +349,8 @@ module IlMachineState =
|
|||||||
assy
|
assy
|
||||||
state
|
state
|
||||||
|
|
||||||
// If the resolved argument has generics, create a GenericInstantiation
|
|
||||||
// Otherwise, create a FromDefinition
|
|
||||||
let preservedArg =
|
let preservedArg =
|
||||||
let baseType =
|
DumpedAssembly.typeInfoToTypeDefn baseClassTypes state._LoadedAssemblies resolvedArg
|
||||||
resolvedArg.BaseType
|
|
||||||
|> DumpedAssembly.resolveBaseType baseClassTypes state._LoadedAssemblies assy.Name
|
|
||||||
|
|
||||||
let signatureTypeKind =
|
|
||||||
match baseType with
|
|
||||||
| ResolvedBaseType.Enum
|
|
||||||
| ResolvedBaseType.ValueType -> SignatureTypeKind.ValueType
|
|
||||||
| ResolvedBaseType.Object -> SignatureTypeKind.Class
|
|
||||||
| ResolvedBaseType.Delegate -> SignatureTypeKind.Class
|
|
||||||
|
|
||||||
if resolvedArg.Generics.IsEmpty then
|
|
||||||
TypeDefn.FromDefinition (
|
|
||||||
ComparableTypeDefinitionHandle.Make resolvedArg.TypeDefHandle,
|
|
||||||
assy.Name.FullName,
|
|
||||||
signatureTypeKind
|
|
||||||
)
|
|
||||||
else
|
|
||||||
// Preserve the generic instantiation
|
|
||||||
let genericDef =
|
|
||||||
TypeDefn.FromDefinition (
|
|
||||||
ComparableTypeDefinitionHandle.Make resolvedArg.TypeDefHandle,
|
|
||||||
assy.Name.FullName,
|
|
||||||
signatureTypeKind
|
|
||||||
)
|
|
||||||
|
|
||||||
TypeDefn.GenericInstantiation (genericDef, resolvedArg.Generics)
|
|
||||||
|
|
||||||
args'.Add preservedArg
|
args'.Add preservedArg
|
||||||
|
|
||||||
@@ -446,6 +432,42 @@ module IlMachineState =
|
|||||||
let sign = assy.TypeSpecs.[ty].Signature
|
let sign = assy.TypeSpecs.[ty].Signature
|
||||||
|
|
||||||
// Convert ConcreteTypeHandle to TypeDefn
|
// Convert ConcreteTypeHandle to TypeDefn
|
||||||
|
let typeGenericArgsAsDefn =
|
||||||
|
typeGenericArgs
|
||||||
|
|> ImmutableArray.map (fun handle ->
|
||||||
|
Concretization.concreteHandleToTypeDefn
|
||||||
|
baseClassTypes
|
||||||
|
handle
|
||||||
|
state.ConcreteTypes
|
||||||
|
state._LoadedAssemblies
|
||||||
|
)
|
||||||
|
|
||||||
|
let methodGenericArgsAsDefn =
|
||||||
|
methodGenericArgs
|
||||||
|
|> ImmutableArray.map (fun handle ->
|
||||||
|
Concretization.concreteHandleToTypeDefn
|
||||||
|
baseClassTypes
|
||||||
|
handle
|
||||||
|
state.ConcreteTypes
|
||||||
|
state._LoadedAssemblies
|
||||||
|
)
|
||||||
|
|
||||||
|
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 =
|
let typeGenericArgsAsDefn =
|
||||||
typeGenericArgs
|
typeGenericArgs
|
||||||
|> Seq.map (fun handle ->
|
|> Seq.map (fun handle ->
|
||||||
@@ -468,7 +490,17 @@ module IlMachineState =
|
|||||||
)
|
)
|
||||||
|> ImmutableArray.CreateRange
|
|> ImmutableArray.CreateRange
|
||||||
|
|
||||||
resolveTypeFromDefn loggerFactory baseClassTypes sign typeGenericArgsAsDefn methodGenericArgsAsDefn assy state
|
// 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
|
/// Get zero value for a type that's already been concretized
|
||||||
let cliTypeZeroOfHandle
|
let cliTypeZeroOfHandle
|
||||||
@@ -498,13 +530,13 @@ module IlMachineState =
|
|||||||
/// Concretize a ConcreteType<TypeDefn> to get a ConcreteTypeHandle for static field access
|
/// Concretize a ConcreteType<TypeDefn> to get a ConcreteTypeHandle for static field access
|
||||||
let concretizeFieldDeclaringType
|
let concretizeFieldDeclaringType
|
||||||
(loggerFactory : ILoggerFactory)
|
(loggerFactory : ILoggerFactory)
|
||||||
(baseClassTypes : BaseClassTypes<'corelib>)
|
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
||||||
(declaringType : ConcreteType<TypeDefn>)
|
(declaringType : ConcreteType<TypeDefn>)
|
||||||
(state : IlMachineState)
|
(state : IlMachineState)
|
||||||
: ConcreteTypeHandle * IlMachineState
|
: ConcreteTypeHandle * IlMachineState
|
||||||
=
|
=
|
||||||
// Create a concretization context from the current state
|
// Create a concretization context from the current state
|
||||||
let ctx : TypeConcretization.ConcretizationContext<'corelib> =
|
let ctx : TypeConcretization.ConcretizationContext<_> =
|
||||||
{
|
{
|
||||||
InProgress = ImmutableDictionary.Empty
|
InProgress = ImmutableDictionary.Empty
|
||||||
ConcreteTypes = state.ConcreteTypes
|
ConcreteTypes = state.ConcreteTypes
|
||||||
@@ -512,26 +544,6 @@ module IlMachineState =
|
|||||||
BaseTypes = baseClassTypes
|
BaseTypes = baseClassTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to get assembly from reference
|
|
||||||
let loadAssembly
|
|
||||||
(currentAssembly : AssemblyName)
|
|
||||||
(assyRef : AssemblyReferenceHandle)
|
|
||||||
: ImmutableDictionary<string, DumpedAssembly> * DumpedAssembly
|
|
||||||
=
|
|
||||||
let assyToLoad =
|
|
||||||
match state.LoadedAssembly currentAssembly with
|
|
||||||
| Some assy -> assy
|
|
||||||
| None -> failwithf "Assembly %s not loaded" currentAssembly.FullName
|
|
||||||
|
|
||||||
let referencedAssy = assyToLoad.AssemblyReferences.[assyRef]
|
|
||||||
|
|
||||||
match state.LoadedAssembly referencedAssy.Name with
|
|
||||||
| Some assy -> state._LoadedAssemblies, assy
|
|
||||||
| None ->
|
|
||||||
// Need to load the assembly
|
|
||||||
let newState, loadedAssy, _ = loadAssembly loggerFactory assyToLoad assyRef state
|
|
||||||
newState._LoadedAssemblies, loadedAssy
|
|
||||||
|
|
||||||
// Concretize each generic argument first
|
// Concretize each generic argument first
|
||||||
let mutable currentCtx = ctx
|
let mutable currentCtx = ctx
|
||||||
let genericHandles = ImmutableArray.CreateBuilder declaringType.Generics.Length
|
let genericHandles = ImmutableArray.CreateBuilder declaringType.Generics.Length
|
||||||
@@ -540,7 +552,7 @@ module IlMachineState =
|
|||||||
let handle, newCtx =
|
let handle, newCtx =
|
||||||
TypeConcretization.concretizeType
|
TypeConcretization.concretizeType
|
||||||
currentCtx
|
currentCtx
|
||||||
loadAssembly
|
(loader loggerFactory state)
|
||||||
declaringType.Assembly
|
declaringType.Assembly
|
||||||
ImmutableArray.Empty // No type generics in this context
|
ImmutableArray.Empty // No type generics in this context
|
||||||
ImmutableArray.Empty // No method generics in this context
|
ImmutableArray.Empty // No method generics in this context
|
||||||
@@ -552,12 +564,13 @@ module IlMachineState =
|
|||||||
// Now we need to concretize the type definition itself
|
// Now we need to concretize the type definition itself
|
||||||
// If it's a non-generic type, we can use concretizeTypeDefinition directly
|
// If it's a non-generic type, we can use concretizeTypeDefinition directly
|
||||||
if declaringType.Generics.IsEmpty then
|
if declaringType.Generics.IsEmpty then
|
||||||
let handle, newCtx =
|
let handle, currentCtx =
|
||||||
TypeConcretization.concretizeTypeDefinition currentCtx declaringType.Assembly declaringType.Definition
|
TypeConcretization.concretizeTypeDefinition currentCtx declaringType.Assembly declaringType.Definition
|
||||||
|
|
||||||
let newState =
|
let newState =
|
||||||
{ state with
|
{ state with
|
||||||
ConcreteTypes = newCtx.ConcreteTypes
|
ConcreteTypes = currentCtx.ConcreteTypes
|
||||||
|
_LoadedAssemblies = currentCtx.LoadedAssemblies
|
||||||
}
|
}
|
||||||
|
|
||||||
handle, newState
|
handle, newState
|
||||||
@@ -601,7 +614,7 @@ module IlMachineState =
|
|||||||
(typeGenerics : ConcreteTypeHandle ImmutableArray)
|
(typeGenerics : ConcreteTypeHandle ImmutableArray)
|
||||||
(methodGenerics : ConcreteTypeHandle ImmutableArray)
|
(methodGenerics : ConcreteTypeHandle ImmutableArray)
|
||||||
(state : IlMachineState)
|
(state : IlMachineState)
|
||||||
: IlMachineState * CliType
|
: IlMachineState * CliType * ConcreteTypeHandle
|
||||||
=
|
=
|
||||||
|
|
||||||
// First concretize the type
|
// First concretize the type
|
||||||
@@ -613,11 +626,11 @@ module IlMachineState =
|
|||||||
state.WithLoadedAssembly assy.Name assy
|
state.WithLoadedAssembly assy.Name assy
|
||||||
|
|
||||||
let state, handle =
|
let state, handle =
|
||||||
concretizeType baseClassTypes state assy.Name typeGenerics methodGenerics ty
|
concretizeType loggerFactory baseClassTypes state assy.Name typeGenerics methodGenerics ty
|
||||||
|
|
||||||
// Now get the zero value
|
// Now get the zero value
|
||||||
let zero, state = cliTypeZeroOfHandle state baseClassTypes handle
|
let zero, state = cliTypeZeroOfHandle state baseClassTypes handle
|
||||||
state, zero
|
state, zero, handle
|
||||||
|
|
||||||
let pushToEvalStack' (o : EvalStackValue) (thread : ThreadId) (state : IlMachineState) =
|
let pushToEvalStack' (o : EvalStackValue) (thread : ThreadId) (state : IlMachineState) =
|
||||||
let activeThreadState = state.ThreadState.[thread]
|
let activeThreadState = state.ThreadState.[thread]
|
||||||
@@ -674,14 +687,14 @@ module IlMachineState =
|
|||||||
(state : IlMachineState)
|
(state : IlMachineState)
|
||||||
: IlMachineState
|
: IlMachineState
|
||||||
=
|
=
|
||||||
let heap = ManagedHeap.SetArrayValue arrayAllocation index v state.ManagedHeap
|
let heap = ManagedHeap.setArrayValue arrayAllocation index v state.ManagedHeap
|
||||||
|
|
||||||
{ state with
|
{ state with
|
||||||
ManagedHeap = heap
|
ManagedHeap = heap
|
||||||
}
|
}
|
||||||
|
|
||||||
let getArrayValue (arrayAllocation : ManagedHeapAddress) (index : int) (state : IlMachineState) : CliType =
|
let getArrayValue (arrayAllocation : ManagedHeapAddress) (index : int) (state : IlMachineState) : CliType =
|
||||||
ManagedHeap.GetArrayValue arrayAllocation index state.ManagedHeap
|
ManagedHeap.getArrayValue arrayAllocation index state.ManagedHeap
|
||||||
|
|
||||||
/// There might be no stack frame to return to, so you might get None.
|
/// There might be no stack frame to return to, so you might get None.
|
||||||
let returnStackFrame
|
let returnStackFrame
|
||||||
@@ -740,14 +753,9 @@ module IlMachineState =
|
|||||||
| ResolvedBaseType.Delegate
|
| ResolvedBaseType.Delegate
|
||||||
| ResolvedBaseType.Object -> state |> pushToEvalStack (CliType.ofManagedObject constructing) currentThread
|
| ResolvedBaseType.Object -> state |> pushToEvalStack (CliType.ofManagedObject constructing) currentThread
|
||||||
| ResolvedBaseType.ValueType ->
|
| ResolvedBaseType.ValueType ->
|
||||||
let vt =
|
|
||||||
{
|
|
||||||
Fields = Map.toList constructed.Fields
|
|
||||||
}
|
|
||||||
|
|
||||||
state
|
state
|
||||||
// TODO: ordering of fields probably important
|
// TODO: ordering of fields probably important
|
||||||
|> pushToEvalStack (CliType.ValueType vt) currentThread
|
|> pushToEvalStack (CliType.ValueType constructed.Contents) currentThread
|
||||||
| ResolvedBaseType.Enum -> failwith "TODO"
|
| ResolvedBaseType.Enum -> failwith "TODO"
|
||||||
| None ->
|
| None ->
|
||||||
match threadStateAtEndOfMethod.MethodState.EvaluationStack.Values with
|
match threadStateAtEndOfMethod.MethodState.EvaluationStack.Values with
|
||||||
@@ -789,22 +797,7 @@ module IlMachineState =
|
|||||||
let concretizedMethod, newConcreteTypes, newAssemblies =
|
let concretizedMethod, newConcreteTypes, newAssemblies =
|
||||||
Concretization.concretizeMethod
|
Concretization.concretizeMethod
|
||||||
state.ConcreteTypes
|
state.ConcreteTypes
|
||||||
(fun assyName ref ->
|
(loader loggerFactory state)
|
||||||
match state.LoadedAssembly assyName with
|
|
||||||
| Some currentAssy ->
|
|
||||||
let targetAssyRef = currentAssy.AssemblyReferences.[ref]
|
|
||||||
|
|
||||||
match state.LoadedAssembly targetAssyRef.Name with
|
|
||||||
| Some _ ->
|
|
||||||
// Assembly already loaded, return existing state
|
|
||||||
state._LoadedAssemblies, state._LoadedAssemblies.[targetAssyRef.Name.FullName]
|
|
||||||
| None ->
|
|
||||||
// Need to load the assembly
|
|
||||||
let newState, loadedAssy, _ = loadAssembly loggerFactory currentAssy ref state
|
|
||||||
newState._LoadedAssemblies, loadedAssy
|
|
||||||
| None ->
|
|
||||||
failwithf "Current assembly %s not loaded when trying to resolve reference" assyName.FullName
|
|
||||||
)
|
|
||||||
state._LoadedAssemblies
|
state._LoadedAssemblies
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
methodToCall
|
methodToCall
|
||||||
@@ -850,6 +843,7 @@ module IlMachineState =
|
|||||||
for i = 0 to generics.Length - 1 do
|
for i = 0 to generics.Length - 1 do
|
||||||
let state2, handle =
|
let state2, handle =
|
||||||
concretizeType
|
concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
callingAssembly
|
callingAssembly
|
||||||
@@ -905,14 +899,7 @@ module IlMachineState =
|
|||||||
let handle, newCtx =
|
let handle, newCtx =
|
||||||
TypeConcretization.concretizeType
|
TypeConcretization.concretizeType
|
||||||
ctx
|
ctx
|
||||||
(fun assyName ref ->
|
(loader loggerFactory state)
|
||||||
let currentAssy = state.LoadedAssembly assyName |> Option.get
|
|
||||||
|
|
||||||
let targetAssy =
|
|
||||||
currentAssy.AssemblyReferences.[ref].Name |> state.LoadedAssembly |> Option.get
|
|
||||||
|
|
||||||
state._LoadedAssemblies, targetAssy
|
|
||||||
)
|
|
||||||
(state.ActiveAssembly thread).Name
|
(state.ActiveAssembly thread).Name
|
||||||
ImmutableArray.Empty // No type generics for the concretization context
|
ImmutableArray.Empty // No type generics for the concretization context
|
||||||
ImmutableArray.Empty // No method generics for the concretization context
|
ImmutableArray.Empty // No method generics for the concretization context
|
||||||
@@ -923,6 +910,7 @@ module IlMachineState =
|
|||||||
state <-
|
state <-
|
||||||
{ state with
|
{ state with
|
||||||
ConcreteTypes = newCtx.ConcreteTypes
|
ConcreteTypes = newCtx.ConcreteTypes
|
||||||
|
_LoadedAssemblies = newCtx.LoadedAssemblies
|
||||||
}
|
}
|
||||||
|
|
||||||
handles.ToImmutable (), state
|
handles.ToImmutable (), state
|
||||||
@@ -1027,14 +1015,7 @@ module IlMachineState =
|
|||||||
let declaringHandle, newCtx =
|
let declaringHandle, newCtx =
|
||||||
TypeConcretization.concretizeType
|
TypeConcretization.concretizeType
|
||||||
ctx
|
ctx
|
||||||
(fun assyName ref ->
|
(loader loggerFactory state)
|
||||||
let currentAssy = state.LoadedAssembly assyName |> Option.get
|
|
||||||
|
|
||||||
let targetAssy =
|
|
||||||
currentAssy.AssemblyReferences.[ref].Name |> state.LoadedAssembly |> Option.get
|
|
||||||
|
|
||||||
state._LoadedAssemblies, targetAssy
|
|
||||||
)
|
|
||||||
field.DeclaringType.Assembly
|
field.DeclaringType.Assembly
|
||||||
contextTypeGenerics
|
contextTypeGenerics
|
||||||
contextMethodGenerics
|
contextMethodGenerics
|
||||||
@@ -1069,7 +1050,7 @@ module IlMachineState =
|
|||||||
Logger = logger
|
Logger = logger
|
||||||
NextThreadId = 0
|
NextThreadId = 0
|
||||||
// CallStack = []
|
// CallStack = []
|
||||||
ManagedHeap = ManagedHeap.Empty
|
ManagedHeap = ManagedHeap.empty
|
||||||
ThreadState = Map.empty
|
ThreadState = Map.empty
|
||||||
InternedStrings = ImmutableDictionary.Empty
|
InternedStrings = ImmutableDictionary.Empty
|
||||||
_LoadedAssemblies = ImmutableDictionary.Empty
|
_LoadedAssemblies = ImmutableDictionary.Empty
|
||||||
@@ -1115,7 +1096,7 @@ module IlMachineState =
|
|||||||
Elements = initialisation
|
Elements = initialisation
|
||||||
}
|
}
|
||||||
|
|
||||||
let alloc, heap = state.ManagedHeap |> ManagedHeap.AllocateArray o
|
let alloc, heap = state.ManagedHeap |> ManagedHeap.allocateArray o
|
||||||
|
|
||||||
let state =
|
let state =
|
||||||
{ state with
|
{ state with
|
||||||
@@ -1125,7 +1106,7 @@ module IlMachineState =
|
|||||||
alloc, state
|
alloc, state
|
||||||
|
|
||||||
let allocateStringData (len : int) (state : IlMachineState) : int * IlMachineState =
|
let allocateStringData (len : int) (state : IlMachineState) : int * IlMachineState =
|
||||||
let addr, heap = state.ManagedHeap |> ManagedHeap.AllocateString len
|
let addr, heap = state.ManagedHeap |> ManagedHeap.allocateString len
|
||||||
|
|
||||||
let state =
|
let state =
|
||||||
{ state with
|
{ state with
|
||||||
@@ -1135,7 +1116,7 @@ module IlMachineState =
|
|||||||
addr, state
|
addr, state
|
||||||
|
|
||||||
let setStringData (addr : int) (contents : string) (state : IlMachineState) : IlMachineState =
|
let setStringData (addr : int) (contents : string) (state : IlMachineState) : IlMachineState =
|
||||||
let heap = ManagedHeap.SetStringData addr contents state.ManagedHeap
|
let heap = ManagedHeap.setStringData addr contents state.ManagedHeap
|
||||||
|
|
||||||
{ state with
|
{ state with
|
||||||
ManagedHeap = heap
|
ManagedHeap = heap
|
||||||
@@ -1143,18 +1124,18 @@ module IlMachineState =
|
|||||||
|
|
||||||
let allocateManagedObject
|
let allocateManagedObject
|
||||||
(ty : ConcreteTypeHandle)
|
(ty : ConcreteTypeHandle)
|
||||||
(fields : (string * CliType) list)
|
(fields : CliValueType)
|
||||||
(state : IlMachineState)
|
(state : IlMachineState)
|
||||||
: ManagedHeapAddress * IlMachineState
|
: ManagedHeapAddress * IlMachineState
|
||||||
=
|
=
|
||||||
let o =
|
let o =
|
||||||
{
|
{
|
||||||
Fields = Map.ofList fields
|
Contents = fields
|
||||||
ConcreteType = ty
|
ConcreteType = ty
|
||||||
SyncBlock = SyncBlock.Free
|
SyncBlock = SyncBlock.Free
|
||||||
}
|
}
|
||||||
|
|
||||||
let alloc, heap = state.ManagedHeap |> ManagedHeap.AllocateNonArray o
|
let alloc, heap = state.ManagedHeap |> ManagedHeap.allocateNonArray o
|
||||||
|
|
||||||
let state =
|
let state =
|
||||||
{ state with
|
{ state with
|
||||||
@@ -1220,6 +1201,7 @@ module IlMachineState =
|
|||||||
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
||||||
(currentThread : ThreadId)
|
(currentThread : ThreadId)
|
||||||
(assy : DumpedAssembly)
|
(assy : DumpedAssembly)
|
||||||
|
(genericMethodTypeArgs : ImmutableArray<ConcreteTypeHandle>)
|
||||||
(m : MemberReferenceHandle)
|
(m : MemberReferenceHandle)
|
||||||
(state : IlMachineState)
|
(state : IlMachineState)
|
||||||
: IlMachineState *
|
: IlMachineState *
|
||||||
@@ -1283,6 +1265,7 @@ module IlMachineState =
|
|||||||
// TODO: generics?
|
// TODO: generics?
|
||||||
let state, t =
|
let state, t =
|
||||||
concretizeType
|
concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
targetType.Assembly
|
targetType.Assembly
|
||||||
@@ -1300,6 +1283,7 @@ module IlMachineState =
|
|||||||
// Concretize the field signature from the member reference
|
// Concretize the field signature from the member reference
|
||||||
let state, concreteFieldSig =
|
let state, concreteFieldSig =
|
||||||
concretizeType
|
concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
(state.ActiveAssembly(currentThread).Name)
|
(state.ActiveAssembly(currentThread).Name)
|
||||||
@@ -1317,6 +1301,7 @@ module IlMachineState =
|
|||||||
// Concretize the field's signature for comparison
|
// Concretize the field's signature for comparison
|
||||||
let state, fieldSigConcrete =
|
let state, fieldSigConcrete =
|
||||||
concretizeType
|
concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
assy.Name
|
assy.Name
|
||||||
@@ -1354,11 +1339,12 @@ module IlMachineState =
|
|||||||
state
|
state
|
||||||
(fun state ty ->
|
(fun state ty ->
|
||||||
concretizeType
|
concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
(state.ActiveAssembly(currentThread).Name)
|
(state.ActiveAssembly(currentThread).Name)
|
||||||
concreteExtractedTypeArgs
|
concreteExtractedTypeArgs
|
||||||
ImmutableArray.Empty
|
genericMethodTypeArgs
|
||||||
ty
|
ty
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1371,11 +1357,12 @@ module IlMachineState =
|
|||||||
state
|
state
|
||||||
(fun state ty ->
|
(fun state ty ->
|
||||||
concretizeType
|
concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
assy.Name
|
assy.Name
|
||||||
concreteExtractedTypeArgs
|
concreteExtractedTypeArgs
|
||||||
ImmutableArray.Empty
|
genericMethodTypeArgs
|
||||||
ty
|
ty
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1429,13 +1416,62 @@ module IlMachineState =
|
|||||||
: IlMachineState
|
: IlMachineState
|
||||||
=
|
=
|
||||||
{ state with
|
{ state with
|
||||||
ManagedHeap = state.ManagedHeap |> ManagedHeap.SetSyncBlock addr syncBlockValue
|
ManagedHeap = state.ManagedHeap |> ManagedHeap.setSyncBlock addr syncBlockValue
|
||||||
}
|
}
|
||||||
|
|
||||||
let getSyncBlock (addr : ManagedHeapAddress) (state : IlMachineState) : SyncBlock =
|
let getSyncBlock (addr : ManagedHeapAddress) (state : IlMachineState) : SyncBlock =
|
||||||
state.ManagedHeap |> ManagedHeap.GetSyncBlock addr
|
state.ManagedHeap |> ManagedHeap.getSyncBlock addr
|
||||||
|
|
||||||
let executeDelegateConstructor (instruction : MethodState) (state : IlMachineState) : IlMachineState =
|
let getFieldValue (obj : ManagedPointerSource) (fieldName : string) (state : IlMachineState) : CliType =
|
||||||
|
match obj with
|
||||||
|
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
|
||||||
|
getLocalVariable sourceThread methodFrame whichVar state
|
||||||
|
|> CliType.getField fieldName
|
||||||
|
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) -> failwith "todo"
|
||||||
|
| ManagedPointerSource.Heap addr ->
|
||||||
|
ManagedHeap.get addr state.ManagedHeap
|
||||||
|
|> AllocatedNonArrayObject.DereferenceField fieldName
|
||||||
|
| ManagedPointerSource.ArrayIndex (arr, index) -> getArrayValue arr index state |> CliType.getField fieldName
|
||||||
|
| ManagedPointerSource.Field (src, fieldName) -> failwith "todo"
|
||||||
|
| ManagedPointerSource.Null -> failwith "TODO: throw NRE"
|
||||||
|
| ManagedPointerSource.InterpretedAsType (src, ty) -> failwith "TODO"
|
||||||
|
|
||||||
|
let setFieldValue
|
||||||
|
(obj : ManagedPointerSource)
|
||||||
|
(v : CliType)
|
||||||
|
(fieldName : string)
|
||||||
|
(state : IlMachineState)
|
||||||
|
: IlMachineState
|
||||||
|
=
|
||||||
|
match obj with
|
||||||
|
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
|
||||||
|
let v =
|
||||||
|
getLocalVariable sourceThread methodFrame whichVar state
|
||||||
|
|> CliType.withFieldSet fieldName v
|
||||||
|
|
||||||
|
state |> setLocalVariable sourceThread methodFrame whichVar v
|
||||||
|
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) -> failwith "todo"
|
||||||
|
| ManagedPointerSource.Heap addr ->
|
||||||
|
let newValue =
|
||||||
|
ManagedHeap.get addr state.ManagedHeap
|
||||||
|
|> AllocatedNonArrayObject.SetField fieldName v
|
||||||
|
|
||||||
|
{ state with
|
||||||
|
ManagedHeap = ManagedHeap.set addr newValue state.ManagedHeap
|
||||||
|
}
|
||||||
|
| ManagedPointerSource.ArrayIndex (arr, index) ->
|
||||||
|
let v = getArrayValue arr index state |> CliType.withFieldSet fieldName v
|
||||||
|
state |> setArrayValue arr v index
|
||||||
|
| ManagedPointerSource.Field (managedPointerSource, fieldName) -> failwith "todo"
|
||||||
|
| ManagedPointerSource.Null -> failwith "TODO: throw NRE"
|
||||||
|
| ManagedPointerSource.InterpretedAsType (src, ty) -> failwith "TODO"
|
||||||
|
|
||||||
|
let executeDelegateConstructor
|
||||||
|
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
||||||
|
(instruction : MethodState)
|
||||||
|
(state : IlMachineState)
|
||||||
|
: IlMachineState
|
||||||
|
=
|
||||||
// We've been called with arguments already popped from the stack into local arguments.
|
// We've been called with arguments already popped from the stack into local arguments.
|
||||||
let constructing = instruction.Arguments.[0]
|
let constructing = instruction.Arguments.[0]
|
||||||
let targetObj = instruction.Arguments.[1]
|
let targetObj = instruction.Arguments.[1]
|
||||||
@@ -1443,12 +1479,17 @@ module IlMachineState =
|
|||||||
|
|
||||||
let targetObj =
|
let targetObj =
|
||||||
match targetObj with
|
match targetObj with
|
||||||
| CliType.ObjectRef target -> target
|
| CliType.RuntimePointer (CliRuntimePointer.Managed (ManagedPointerSource.Heap target))
|
||||||
|
| CliType.ObjectRef (Some target) -> Some target
|
||||||
|
| CliType.ObjectRef None
|
||||||
|
| CliType.RuntimePointer (CliRuntimePointer.Managed ManagedPointerSource.Null) -> None
|
||||||
| _ -> failwith $"Unexpected target type for delegate: {targetObj}"
|
| _ -> failwith $"Unexpected target type for delegate: {targetObj}"
|
||||||
|
|
||||||
let constructing =
|
let constructing =
|
||||||
match constructing with
|
match constructing with
|
||||||
|
| CliType.RuntimePointer (CliRuntimePointer.Managed ManagedPointerSource.Null)
|
||||||
| CliType.ObjectRef None -> failwith "unexpectedly constructing the null delegate"
|
| CliType.ObjectRef None -> failwith "unexpectedly constructing the null delegate"
|
||||||
|
| CliType.RuntimePointer (CliRuntimePointer.Managed (ManagedPointerSource.Heap target))
|
||||||
| CliType.ObjectRef (Some target) -> target
|
| CliType.ObjectRef (Some target) -> target
|
||||||
| _ -> failwith $"Unexpectedly not constructing a managed object: {constructing}"
|
| _ -> failwith $"Unexpectedly not constructing a managed object: {constructing}"
|
||||||
|
|
||||||
@@ -1459,14 +1500,42 @@ module IlMachineState =
|
|||||||
|
|
||||||
// Standard delegate fields in .NET are _target and _methodPtr
|
// Standard delegate fields in .NET are _target and _methodPtr
|
||||||
// Update the fields with the target object and method pointer
|
// Update the fields with the target object and method pointer
|
||||||
let updatedFields =
|
let allConcreteTypes = state.ConcreteTypes
|
||||||
heapObj.Fields
|
|
||||||
|> Map.add "_target" (CliType.ObjectRef targetObj)
|
|
||||||
|> Map.add "_methodPtr" methodPtr
|
|
||||||
|
|
||||||
let updatedObj =
|
let updatedObj =
|
||||||
|
let newContents =
|
||||||
|
heapObj.Contents
|
||||||
|
|> CliValueType.AddField
|
||||||
|
{
|
||||||
|
Name = "_target"
|
||||||
|
Contents = CliType.ObjectRef targetObj
|
||||||
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
allConcreteTypes
|
||||||
|
(baseClassTypes.Object.Assembly,
|
||||||
|
baseClassTypes.Object.Namespace,
|
||||||
|
baseClassTypes.Object.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
|
}
|
||||||
|
|> CliValueType.AddField
|
||||||
|
{
|
||||||
|
Name = "_methodPtr"
|
||||||
|
Contents = methodPtr
|
||||||
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
allConcreteTypes
|
||||||
|
(baseClassTypes.Object.Assembly,
|
||||||
|
baseClassTypes.Object.Namespace,
|
||||||
|
baseClassTypes.Object.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
|
}
|
||||||
|
|
||||||
{ heapObj with
|
{ heapObj with
|
||||||
Fields = updatedFields
|
Contents = newContents
|
||||||
}
|
}
|
||||||
|
|
||||||
let updatedHeap =
|
let updatedHeap =
|
||||||
@@ -1480,6 +1549,7 @@ module IlMachineState =
|
|||||||
|
|
||||||
/// Returns the type handle and an allocated System.RuntimeType.
|
/// Returns the type handle and an allocated System.RuntimeType.
|
||||||
let getOrAllocateType
|
let getOrAllocateType
|
||||||
|
(loggerFactory : ILoggerFactory)
|
||||||
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
||||||
(defn : ConcreteTypeHandle)
|
(defn : ConcreteTypeHandle)
|
||||||
(state : IlMachineState)
|
(state : IlMachineState)
|
||||||
@@ -1491,10 +1561,18 @@ module IlMachineState =
|
|||||||
baseClassTypes.Corelib.Name.FullName,
|
baseClassTypes.Corelib.Name.FullName,
|
||||||
SignatureTypeKind.Class
|
SignatureTypeKind.Class
|
||||||
)
|
)
|
||||||
|> concretizeType baseClassTypes state baseClassTypes.Corelib.Name ImmutableArray.Empty ImmutableArray.Empty
|
|> concretizeType
|
||||||
|
loggerFactory
|
||||||
|
baseClassTypes
|
||||||
|
state
|
||||||
|
baseClassTypes.Corelib.Name
|
||||||
|
ImmutableArray.Empty
|
||||||
|
ImmutableArray.Empty
|
||||||
|
|
||||||
let result, reg, state =
|
let result, reg, state =
|
||||||
TypeHandleRegistry.getOrAllocate
|
TypeHandleRegistry.getOrAllocate
|
||||||
|
state.ConcreteTypes
|
||||||
|
baseClassTypes
|
||||||
state
|
state
|
||||||
(fun fields state -> allocateManagedObject runtimeType fields state)
|
(fun fields state -> allocateManagedObject runtimeType fields state)
|
||||||
defn
|
defn
|
||||||
@@ -1535,11 +1613,18 @@ module IlMachineState =
|
|||||||
baseClassTypes.Corelib.Name.FullName,
|
baseClassTypes.Corelib.Name.FullName,
|
||||||
SignatureTypeKind.Class
|
SignatureTypeKind.Class
|
||||||
)
|
)
|
||||||
|> concretizeType baseClassTypes state baseClassTypes.Corelib.Name ImmutableArray.Empty ImmutableArray.Empty
|
|> concretizeType
|
||||||
|
loggerFactory
|
||||||
|
baseClassTypes
|
||||||
|
state
|
||||||
|
baseClassTypes.Corelib.Name
|
||||||
|
ImmutableArray.Empty
|
||||||
|
ImmutableArray.Empty
|
||||||
|
|
||||||
let result, reg, state =
|
let result, reg, state =
|
||||||
FieldHandleRegistry.getOrAllocate
|
FieldHandleRegistry.getOrAllocate
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
|
state.ConcreteTypes
|
||||||
state
|
state
|
||||||
(fun fields state -> allocateManagedObject runtimeType fields state)
|
(fun fields state -> allocateManagedObject runtimeType fields state)
|
||||||
declaringAssy
|
declaringAssy
|
||||||
@@ -1578,6 +1663,38 @@ module IlMachineState =
|
|||||||
| false, _ -> None
|
| false, _ -> None
|
||||||
| true, v -> Some v
|
| true, v -> Some v
|
||||||
|
|
||||||
|
let rec dereferencePointer (state : IlMachineState) (src : ManagedPointerSource) : CliType =
|
||||||
|
match src with
|
||||||
|
| ManagedPointerSource.Null -> failwith "TODO: throw NRE"
|
||||||
|
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
|
||||||
|
state.ThreadState.[sourceThread].MethodStates.[methodFrame].LocalVariables.[int<uint16> whichVar]
|
||||||
|
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
|
||||||
|
state.ThreadState.[sourceThread].MethodStates.[methodFrame].Arguments.[int<uint16> whichVar]
|
||||||
|
| ManagedPointerSource.Heap addr ->
|
||||||
|
let result = ManagedHeap.get addr state.ManagedHeap
|
||||||
|
// TODO: this is awfully dubious, this ain't no value type
|
||||||
|
CliType.ValueType result.Contents
|
||||||
|
| ManagedPointerSource.ArrayIndex (arr, index) -> getArrayValue arr index state
|
||||||
|
| ManagedPointerSource.Field (addr, name) ->
|
||||||
|
let obj = dereferencePointer state addr
|
||||||
|
|
||||||
|
match obj with
|
||||||
|
| CliType.ValueType vt -> vt |> CliValueType.DereferenceField name
|
||||||
|
| v -> failwith $"could not find field {name} on object {v}"
|
||||||
|
| 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"
|
||||||
|
|
||||||
|
failwith $"TODO: interpret as type %s{ty.Assembly.Name}.%s{ty.Namespace}.%s{ty.Name}, object %O{src}"
|
||||||
|
|
||||||
let lookupTypeDefn
|
let lookupTypeDefn
|
||||||
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
||||||
(state : IlMachineState)
|
(state : IlMachineState)
|
||||||
@@ -1586,42 +1703,7 @@ module IlMachineState =
|
|||||||
: IlMachineState * TypeDefn
|
: IlMachineState * TypeDefn
|
||||||
=
|
=
|
||||||
let defn = activeAssy.TypeDefs.[typeDef]
|
let defn = activeAssy.TypeDefs.[typeDef]
|
||||||
|
state, DumpedAssembly.typeInfoToTypeDefn' baseClassTypes state._LoadedAssemblies defn
|
||||||
let baseType =
|
|
||||||
defn.BaseType
|
|
||||||
|> DumpedAssembly.resolveBaseType baseClassTypes state._LoadedAssemblies defn.Assembly
|
|
||||||
|
|
||||||
let signatureTypeKind =
|
|
||||||
match baseType with
|
|
||||||
| ResolvedBaseType.Enum
|
|
||||||
| ResolvedBaseType.ValueType -> SignatureTypeKind.ValueType
|
|
||||||
| ResolvedBaseType.Object -> SignatureTypeKind.Class
|
|
||||||
| ResolvedBaseType.Delegate -> SignatureTypeKind.Class
|
|
||||||
|
|
||||||
let result =
|
|
||||||
if defn.Generics.IsEmpty then
|
|
||||||
TypeDefn.FromDefinition (
|
|
||||||
ComparableTypeDefinitionHandle.Make defn.TypeDefHandle,
|
|
||||||
defn.Assembly.FullName,
|
|
||||||
signatureTypeKind
|
|
||||||
)
|
|
||||||
else
|
|
||||||
// Preserve the generic instantiation by converting GenericParameters to TypeDefn.GenericTypeParameter
|
|
||||||
let genericDef =
|
|
||||||
TypeDefn.FromDefinition (
|
|
||||||
ComparableTypeDefinitionHandle.Make defn.TypeDefHandle,
|
|
||||||
defn.Assembly.FullName,
|
|
||||||
signatureTypeKind
|
|
||||||
)
|
|
||||||
|
|
||||||
let genericArgs =
|
|
||||||
defn.Generics
|
|
||||||
|> Seq.mapi (fun i _ -> TypeDefn.GenericTypeParameter i)
|
|
||||||
|> ImmutableArray.CreateRange
|
|
||||||
|
|
||||||
TypeDefn.GenericInstantiation (genericDef, genericArgs)
|
|
||||||
|
|
||||||
state, result
|
|
||||||
|
|
||||||
let lookupTypeRef
|
let lookupTypeRef
|
||||||
(loggerFactory : ILoggerFactory)
|
(loggerFactory : ILoggerFactory)
|
||||||
@@ -1649,25 +1731,4 @@ module IlMachineState =
|
|||||||
let state, assy, resolved =
|
let state, assy, resolved =
|
||||||
resolveTypeFromRef loggerFactory activeAssy ref typeGenerics state
|
resolveTypeFromRef loggerFactory activeAssy ref typeGenerics state
|
||||||
|
|
||||||
let baseType =
|
state, DumpedAssembly.typeInfoToTypeDefn baseClassTypes state._LoadedAssemblies resolved, assy
|
||||||
resolved.BaseType
|
|
||||||
|> DumpedAssembly.resolveBaseType baseClassTypes state._LoadedAssemblies assy.Name
|
|
||||||
|
|
||||||
let signatureTypeKind =
|
|
||||||
match baseType with
|
|
||||||
| ResolvedBaseType.Enum
|
|
||||||
| ResolvedBaseType.ValueType -> SignatureTypeKind.ValueType
|
|
||||||
| ResolvedBaseType.Object -> SignatureTypeKind.Class
|
|
||||||
| ResolvedBaseType.Delegate -> SignatureTypeKind.Class
|
|
||||||
|
|
||||||
let result =
|
|
||||||
TypeDefn.FromDefinition (
|
|
||||||
ComparableTypeDefinitionHandle.Make resolved.TypeDefHandle,
|
|
||||||
assy.Name.FullName,
|
|
||||||
signatureTypeKind
|
|
||||||
)
|
|
||||||
|
|
||||||
if resolved.Generics.IsEmpty then
|
|
||||||
state, result, assy
|
|
||||||
else
|
|
||||||
failwith "TODO: add generics"
|
|
||||||
|
@@ -10,6 +10,7 @@ open Microsoft.Extensions.Logging
|
|||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module IlMachineStateExecution =
|
module IlMachineStateExecution =
|
||||||
let getTypeOfObj
|
let getTypeOfObj
|
||||||
|
(loggerFactory : ILoggerFactory)
|
||||||
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
||||||
(state : IlMachineState)
|
(state : IlMachineState)
|
||||||
(esv : EvalStackValue)
|
(esv : EvalStackValue)
|
||||||
@@ -17,24 +18,18 @@ module IlMachineStateExecution =
|
|||||||
=
|
=
|
||||||
match esv with
|
match esv with
|
||||||
| EvalStackValue.Int32 _ ->
|
| EvalStackValue.Int32 _ ->
|
||||||
TypeDefn.FromDefinition (
|
DumpedAssembly.typeInfoToTypeDefn' baseClassTypes state._LoadedAssemblies baseClassTypes.Int32
|
||||||
ComparableTypeDefinitionHandle.Make baseClassTypes.Int32.TypeDefHandle,
|
|
||||||
baseClassTypes.Corelib.Name.FullName,
|
|
||||||
SignatureTypeKind.ValueType
|
|
||||||
)
|
|
||||||
|> IlMachineState.concretizeType
|
|> IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
baseClassTypes.Corelib.Name
|
baseClassTypes.Corelib.Name
|
||||||
ImmutableArray.Empty
|
ImmutableArray.Empty
|
||||||
ImmutableArray.Empty
|
ImmutableArray.Empty
|
||||||
| EvalStackValue.Int64 _ ->
|
| EvalStackValue.Int64 _ ->
|
||||||
TypeDefn.FromDefinition (
|
DumpedAssembly.typeInfoToTypeDefn' baseClassTypes state._LoadedAssemblies baseClassTypes.Int64
|
||||||
ComparableTypeDefinitionHandle.Make baseClassTypes.Int64.TypeDefHandle,
|
|
||||||
baseClassTypes.Corelib.Name.FullName,
|
|
||||||
SignatureTypeKind.ValueType
|
|
||||||
)
|
|
||||||
|> IlMachineState.concretizeType
|
|> IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
baseClassTypes.Corelib.Name
|
baseClassTypes.Corelib.Name
|
||||||
@@ -42,12 +37,9 @@ module IlMachineStateExecution =
|
|||||||
ImmutableArray.Empty
|
ImmutableArray.Empty
|
||||||
| EvalStackValue.NativeInt nativeIntSource -> failwith "todo"
|
| EvalStackValue.NativeInt nativeIntSource -> failwith "todo"
|
||||||
| EvalStackValue.Float _ ->
|
| EvalStackValue.Float _ ->
|
||||||
TypeDefn.FromDefinition (
|
DumpedAssembly.typeInfoToTypeDefn' baseClassTypes state._LoadedAssemblies baseClassTypes.Double
|
||||||
ComparableTypeDefinitionHandle.Make baseClassTypes.Double.TypeDefHandle,
|
|
||||||
baseClassTypes.Corelib.Name.FullName,
|
|
||||||
SignatureTypeKind.ValueType
|
|
||||||
)
|
|
||||||
|> IlMachineState.concretizeType
|
|> IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
baseClassTypes.Corelib.Name
|
baseClassTypes.Corelib.Name
|
||||||
@@ -58,12 +50,14 @@ module IlMachineStateExecution =
|
|||||||
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) -> failwith "todo"
|
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) -> failwith "todo"
|
||||||
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) -> failwith "todo"
|
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) -> failwith "todo"
|
||||||
| ManagedPointerSource.Heap addr ->
|
| ManagedPointerSource.Heap addr ->
|
||||||
let o = ManagedHeap.Get addr state.ManagedHeap
|
let o = ManagedHeap.get addr state.ManagedHeap
|
||||||
state, o.ConcreteType
|
state, o.ConcreteType
|
||||||
| ManagedPointerSource.ArrayIndex (arr, index) -> failwith "todo"
|
| ManagedPointerSource.ArrayIndex (arr, index) -> failwith "todo"
|
||||||
| ManagedPointerSource.Null -> failwith "todo"
|
| ManagedPointerSource.Null -> failwith "todo"
|
||||||
|
| ManagedPointerSource.Field (managedPointerSource, fieldName) -> failwith "todo"
|
||||||
|
| ManagedPointerSource.InterpretedAsType (src, ty) -> failwith "todo"
|
||||||
| EvalStackValue.ObjectRef addr ->
|
| EvalStackValue.ObjectRef addr ->
|
||||||
let o = ManagedHeap.Get addr state.ManagedHeap
|
let o = ManagedHeap.get addr state.ManagedHeap
|
||||||
state, o.ConcreteType
|
state, o.ConcreteType
|
||||||
| EvalStackValue.UserDefinedValueType tuples -> failwith "todo"
|
| EvalStackValue.UserDefinedValueType tuples -> failwith "todo"
|
||||||
|
|
||||||
@@ -124,16 +118,13 @@ module IlMachineStateExecution =
|
|||||||
|
|
||||||
match
|
match
|
||||||
if isIntrinsic then
|
if isIntrinsic then
|
||||||
Intrinsics.call baseClassTypes methodToCall thread state
|
Intrinsics.call loggerFactory baseClassTypes methodToCall thread state
|
||||||
else
|
else
|
||||||
None
|
None
|
||||||
with
|
with
|
||||||
| Some result -> result
|
| Some result -> result
|
||||||
| None ->
|
| None ->
|
||||||
|
|
||||||
if methodToCall.Name = "GetValue" then
|
|
||||||
printfn ""
|
|
||||||
|
|
||||||
// Get zero values for all parameters
|
// Get zero values for all parameters
|
||||||
let state, argZeroObjects =
|
let state, argZeroObjects =
|
||||||
((state, []), methodToCall.Signature.ParameterTypes)
|
((state, []), methodToCall.Signature.ParameterTypes)
|
||||||
@@ -167,7 +158,8 @@ module IlMachineStateExecution =
|
|||||||
| None -> failwith "unexpectedly no `this` on the eval stack of instance method"
|
| None -> failwith "unexpectedly no `this` on the eval stack of instance method"
|
||||||
| Some this -> this
|
| Some this -> this
|
||||||
|
|
||||||
let state, callingObjTyHandle = getTypeOfObj baseClassTypes state callingObj
|
let state, callingObjTyHandle =
|
||||||
|
getTypeOfObj loggerFactory baseClassTypes state callingObj
|
||||||
|
|
||||||
let callingObjTy =
|
let callingObjTy =
|
||||||
let ty =
|
let ty =
|
||||||
@@ -211,6 +203,7 @@ module IlMachineStateExecution =
|
|||||||
let state, retType =
|
let state, retType =
|
||||||
meth.Signature.ReturnType
|
meth.Signature.ReturnType
|
||||||
|> IlMachineState.concretizeType
|
|> IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
meth.DeclaringType.Assembly
|
meth.DeclaringType.Assembly
|
||||||
@@ -222,6 +215,7 @@ module IlMachineStateExecution =
|
|||||||
||> Seq.mapFold (fun state ty ->
|
||> Seq.mapFold (fun state ty ->
|
||||||
ty
|
ty
|
||||||
|> IlMachineState.concretizeType
|
|> IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
meth.DeclaringType.Assembly
|
meth.DeclaringType.Assembly
|
||||||
@@ -418,7 +412,7 @@ module IlMachineStateExecution =
|
|||||||
// where Newobj puts the object pointer on top
|
// where Newobj puts the object pointer on top
|
||||||
let thisArg, newState =
|
let thisArg, newState =
|
||||||
popAndCoerceArg
|
popAndCoerceArg
|
||||||
(CliType.RuntimePointer (CliRuntimePointer.Managed CliRuntimePointerSource.Null))
|
(CliType.RuntimePointer (CliRuntimePointer.Managed ManagedPointerSource.Null))
|
||||||
currentState
|
currentState
|
||||||
|
|
||||||
currentState <- newState
|
currentState <- newState
|
||||||
@@ -441,7 +435,7 @@ module IlMachineStateExecution =
|
|||||||
|
|
||||||
let thisArg, newState =
|
let thisArg, newState =
|
||||||
popAndCoerceArg
|
popAndCoerceArg
|
||||||
(CliType.RuntimePointer (CliRuntimePointer.Managed CliRuntimePointerSource.Null))
|
(CliType.RuntimePointer (CliRuntimePointer.Managed ManagedPointerSource.Null))
|
||||||
currentState
|
currentState
|
||||||
|
|
||||||
args.Add thisArg
|
args.Add thisArg
|
||||||
@@ -578,34 +572,13 @@ module IlMachineStateExecution =
|
|||||||
typeDef.Name
|
typeDef.Name
|
||||||
)
|
)
|
||||||
|
|
||||||
// TypeDef won't have any generics; it would be a TypeSpec if it did
|
|
||||||
// Create a TypeDefn from the TypeDef handle
|
|
||||||
let baseTypeDefn =
|
let baseTypeDefn =
|
||||||
let baseTypeDef = sourceAssembly.TypeDefs.[typeDefinitionHandle]
|
DumpedAssembly.typeInfoToTypeDefn' baseClassTypes state._LoadedAssemblies typeDef
|
||||||
|
|
||||||
let baseType =
|
|
||||||
baseTypeDef.BaseType
|
|
||||||
|> DumpedAssembly.resolveBaseType
|
|
||||||
baseClassTypes
|
|
||||||
state._LoadedAssemblies
|
|
||||||
sourceAssembly.Name
|
|
||||||
|
|
||||||
let signatureTypeKind =
|
|
||||||
match baseType with
|
|
||||||
| ResolvedBaseType.Enum
|
|
||||||
| ResolvedBaseType.ValueType -> SignatureTypeKind.ValueType
|
|
||||||
| ResolvedBaseType.Object
|
|
||||||
| ResolvedBaseType.Delegate -> SignatureTypeKind.Class
|
|
||||||
|
|
||||||
TypeDefn.FromDefinition (
|
|
||||||
ComparableTypeDefinitionHandle.Make typeDefinitionHandle,
|
|
||||||
sourceAssembly.Name.FullName,
|
|
||||||
signatureTypeKind
|
|
||||||
)
|
|
||||||
|
|
||||||
// Concretize the base type
|
// Concretize the base type
|
||||||
let state, baseTypeHandle =
|
let state, baseTypeHandle =
|
||||||
IlMachineState.concretizeType
|
IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
sourceAssembly.Name
|
sourceAssembly.Name
|
||||||
@@ -639,26 +612,13 @@ module IlMachineStateExecution =
|
|||||||
|
|
||||||
// Create a TypeDefn from the resolved TypeRef
|
// Create a TypeDefn from the resolved TypeRef
|
||||||
let baseTypeDefn =
|
let baseTypeDefn =
|
||||||
let baseType =
|
targetType
|
||||||
targetType.BaseType
|
|> DumpedAssembly.typeInfoToTypeDefn baseClassTypes state._LoadedAssemblies
|
||||||
|> DumpedAssembly.resolveBaseType baseClassTypes state._LoadedAssemblies assy.Name
|
|
||||||
|
|
||||||
let signatureTypeKind =
|
|
||||||
match baseType with
|
|
||||||
| ResolvedBaseType.Enum
|
|
||||||
| ResolvedBaseType.ValueType -> SignatureTypeKind.ValueType
|
|
||||||
| ResolvedBaseType.Object
|
|
||||||
| ResolvedBaseType.Delegate -> SignatureTypeKind.Class
|
|
||||||
|
|
||||||
TypeDefn.FromDefinition (
|
|
||||||
ComparableTypeDefinitionHandle.Make targetType.TypeDefHandle,
|
|
||||||
assy.Name.FullName,
|
|
||||||
signatureTypeKind
|
|
||||||
)
|
|
||||||
|
|
||||||
// Concretize the base type
|
// Concretize the base type
|
||||||
let state, baseTypeHandle =
|
let state, baseTypeHandle =
|
||||||
IlMachineState.concretizeType
|
IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
sourceAssembly.Name
|
sourceAssembly.Name
|
||||||
@@ -710,6 +670,7 @@ module IlMachineStateExecution =
|
|||||||
state
|
state
|
||||||
(fun state typeDefn ->
|
(fun state typeDefn ->
|
||||||
IlMachineState.concretizeType
|
IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
concreteType.Assembly
|
concreteType.Assembly
|
||||||
@@ -734,6 +695,7 @@ module IlMachineStateExecution =
|
|||||||
||> Seq.fold (fun (state, acc) typeDefn ->
|
||> Seq.fold (fun (state, acc) typeDefn ->
|
||||||
let state, handle =
|
let state, handle =
|
||||||
IlMachineState.concretizeType
|
IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
concreteType.Assembly
|
concreteType.Assembly
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
namespace WoofWare.PawPrint
|
namespace WoofWare.PawPrint
|
||||||
|
|
||||||
open System
|
open System
|
||||||
|
open System.Collections.Immutable
|
||||||
|
open Microsoft.Extensions.Logging
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module Intrinsics =
|
module Intrinsics =
|
||||||
@@ -14,10 +16,99 @@ module Intrinsics =
|
|||||||
"System.Private.CoreLib", "ArgumentNullException", "ThrowIfNull"
|
"System.Private.CoreLib", "ArgumentNullException", "ThrowIfNull"
|
||||||
// https://github.com/dotnet/runtime/blob/ec11903827fc28847d775ba17e0cd1ff56cfbc2e/src/coreclr/System.Private.CoreLib/src/System/Type.CoreCLR.cs#L82
|
// https://github.com/dotnet/runtime/blob/ec11903827fc28847d775ba17e0cd1ff56cfbc2e/src/coreclr/System.Private.CoreLib/src/System/Type.CoreCLR.cs#L82
|
||||||
"System.Private.CoreLib", "Type", "GetTypeFromHandle"
|
"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"
|
||||||
|
// https://github.com/dotnet/runtime/blob/d258af50034c192bf7f0a18856bf83d2903d98ae/src/libraries/System.Private.CoreLib/src/System/Math.cs#L127
|
||||||
|
// https://github.com/dotnet/runtime/blob/d258af50034c192bf7f0a18856bf83d2903d98ae/src/libraries/System.Private.CoreLib/src/System/Math.cs#L137
|
||||||
|
"System.Private.CoreLib", "Math", "Abs"
|
||||||
|
// https://github.com/dotnet/runtime/blob/d258af50034c192bf7f0a18856bf83d2903d98ae/src/libraries/System.Private.CoreLib/src/System/Math.cs#L965C10-L1062C19
|
||||||
|
"System.Private.CoreLib", "Math", "Max"
|
||||||
|
// https://github.com/dotnet/runtime/blob/d258af50034c192bf7f0a18856bf83d2903d98ae/src/libraries/System.Private.CoreLib/src/System/Buffer.cs#L150
|
||||||
|
"System.Private.CoreLib", "Buffer", "Memmove"
|
||||||
|
// https://github.com/dotnet/runtime/blob/1c3221b63340d7f81dfd829f3bcd822e582324f6/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs#L799
|
||||||
|
"System.Private.CoreLib", "Thread", "get_CurrentThread"
|
||||||
]
|
]
|
||||||
|> Set.ofList
|
|> Set.ofList
|
||||||
|
|
||||||
|
type private RefTypeProcessingStatus =
|
||||||
|
| InProgress
|
||||||
|
| Completed of bool
|
||||||
|
|
||||||
|
let rec private containsRefType
|
||||||
|
(loggerFactory : ILoggerFactory)
|
||||||
|
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
||||||
|
(state : IlMachineState)
|
||||||
|
(seenSoFar : ImmutableDictionary<TypeInfo<TypeDefn, TypeDefn>, RefTypeProcessingStatus>)
|
||||||
|
(td : TypeInfo<TypeDefn, TypeDefn>)
|
||||||
|
: IlMachineState * ImmutableDictionary<_, RefTypeProcessingStatus> * bool
|
||||||
|
=
|
||||||
|
match seenSoFar.TryGetValue td with
|
||||||
|
| true, InProgress ->
|
||||||
|
// We've hit a cycle. Optimistically assume this path does not introduce a reference type.
|
||||||
|
// If another path finds a reference type, its 'true' will override this.
|
||||||
|
state, seenSoFar, false
|
||||||
|
| true, Completed v ->
|
||||||
|
// We've already calculated this; return the memoized result.
|
||||||
|
state, seenSoFar, v
|
||||||
|
| false, _ ->
|
||||||
|
// Check if this type itself is a reference type.
|
||||||
|
let baseType =
|
||||||
|
td.BaseType
|
||||||
|
|> DumpedAssembly.resolveBaseType baseClassTypes state._LoadedAssemblies td.Assembly
|
||||||
|
|
||||||
|
match baseType with
|
||||||
|
| ResolvedBaseType.Delegate
|
||||||
|
| ResolvedBaseType.Object ->
|
||||||
|
// Short-circuit: if the type itself is a reference type, we're done.
|
||||||
|
let seenSoFar = seenSoFar.Add (td, Completed true)
|
||||||
|
state, seenSoFar, true
|
||||||
|
| ResolvedBaseType.Enum
|
||||||
|
| ResolvedBaseType.ValueType ->
|
||||||
|
// It's a value type, so we must check its fields.
|
||||||
|
// Mark as in progress before recursing.
|
||||||
|
let seenSoFarWithInProgress = seenSoFar.Add (td, InProgress)
|
||||||
|
|
||||||
|
let stateAfterFieldResolution, nonStaticFields =
|
||||||
|
((state, []), td.Fields)
|
||||||
|
||> List.fold (fun (currentState, acc) field ->
|
||||||
|
if field.IsStatic then
|
||||||
|
currentState, acc
|
||||||
|
else
|
||||||
|
// TODO: generics
|
||||||
|
let newState, _, info =
|
||||||
|
IlMachineState.resolveTypeFromDefn
|
||||||
|
loggerFactory
|
||||||
|
baseClassTypes
|
||||||
|
field.Signature
|
||||||
|
ImmutableArray.Empty
|
||||||
|
ImmutableArray.Empty
|
||||||
|
(currentState.LoadedAssembly (td.Assembly) |> Option.get)
|
||||||
|
currentState
|
||||||
|
|
||||||
|
newState, info :: acc
|
||||||
|
)
|
||||||
|
|
||||||
|
// Recurse through the fields, correctly propagating state.
|
||||||
|
let finalState, finalSeenSoFar, fieldsContainRefType =
|
||||||
|
((stateAfterFieldResolution, seenSoFarWithInProgress, false), nonStaticFields)
|
||||||
|
||> List.fold (fun (currentState, currentSeenSoFar, currentResult) field ->
|
||||||
|
if currentResult then
|
||||||
|
(currentState, currentSeenSoFar, true) // Short-circuit
|
||||||
|
else
|
||||||
|
let newState, newSeenSoFar, fieldResult =
|
||||||
|
containsRefType loggerFactory baseClassTypes currentState currentSeenSoFar field
|
||||||
|
|
||||||
|
(newState, newSeenSoFar, currentResult || fieldResult)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mark as completed with the final result before returning.
|
||||||
|
let finalSeenSoFar = finalSeenSoFar.SetItem (td, Completed fieldsContainRefType)
|
||||||
|
finalState, finalSeenSoFar, fieldsContainRefType
|
||||||
|
|
||||||
let call
|
let call
|
||||||
|
(loggerFactory : ILoggerFactory)
|
||||||
(baseClassTypes : BaseClassTypes<_>)
|
(baseClassTypes : BaseClassTypes<_>)
|
||||||
(methodToCall : WoofWare.PawPrint.MethodInfo<ConcreteTypeHandle, ConcreteTypeHandle, ConcreteTypeHandle>)
|
(methodToCall : WoofWare.PawPrint.MethodInfo<ConcreteTypeHandle, ConcreteTypeHandle, ConcreteTypeHandle>)
|
||||||
(currentThread : ThreadId)
|
(currentThread : ThreadId)
|
||||||
@@ -42,6 +133,8 @@ module Intrinsics =
|
|||||||
None
|
None
|
||||||
else
|
else
|
||||||
|
|
||||||
|
// In general, some implementations are in:
|
||||||
|
// https://github.com/dotnet/runtime/blob/108fa7856efcfd39bc991c2d849eabbf7ba5989c/src/coreclr/tools/Common/TypeSystem/IL/Stubs/UnsafeIntrinsics.cs#L192
|
||||||
match methodToCall.DeclaringType.Assembly.Name, methodToCall.DeclaringType.Name, methodToCall.Name with
|
match methodToCall.DeclaringType.Assembly.Name, methodToCall.DeclaringType.Name, methodToCall.Name with
|
||||||
| "System.Private.CoreLib", "Type", "get_TypeHandle" ->
|
| "System.Private.CoreLib", "Type", "get_TypeHandle" ->
|
||||||
// TODO: check return type is RuntimeTypeHandle
|
// TODO: check return type is RuntimeTypeHandle
|
||||||
@@ -50,7 +143,14 @@ module Intrinsics =
|
|||||||
| _ -> ()
|
| _ -> ()
|
||||||
|
|
||||||
// https://github.com/dotnet/runtime/blob/ec11903827fc28847d775ba17e0cd1ff56cfbc2e/src/libraries/System.Private.CoreLib/src/System/Type.cs#L470
|
// https://github.com/dotnet/runtime/blob/ec11903827fc28847d775ba17e0cd1ff56cfbc2e/src/libraries/System.Private.CoreLib/src/System/Type.cs#L470
|
||||||
|
|
||||||
|
// TODO: check return type is RuntimeTypeHandle
|
||||||
|
match methodToCall.Signature.ParameterTypes with
|
||||||
|
| _ :: _ -> failwith "bad signature Type.get_TypeHandle"
|
||||||
|
| _ -> ()
|
||||||
|
|
||||||
// no args, returns RuntimeTypeHandle, a struct with a single field (a RuntimeType class)
|
// no args, returns RuntimeTypeHandle, a struct with a single field (a RuntimeType class)
|
||||||
|
// https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs#L18
|
||||||
|
|
||||||
// The thing on top of the stack will be a RuntimeType.
|
// The thing on top of the stack will be a RuntimeType.
|
||||||
let arg, state = IlMachineState.popEvalStack currentThread state
|
let arg, state = IlMachineState.popEvalStack currentThread state
|
||||||
@@ -58,8 +158,12 @@ module Intrinsics =
|
|||||||
let arg =
|
let arg =
|
||||||
let rec go (arg : EvalStackValue) =
|
let rec go (arg : EvalStackValue) =
|
||||||
match arg with
|
match arg with
|
||||||
| EvalStackValue.UserDefinedValueType [ _, s ] -> go s
|
| EvalStackValue.UserDefinedValueType vt ->
|
||||||
|
match CliValueType.TryExactlyOneField vt with
|
||||||
|
| None -> failwith "TODO"
|
||||||
|
| Some field -> go (EvalStackValue.ofCliType field.Contents)
|
||||||
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> failwith "TODO: throw NRE"
|
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> failwith "TODO: throw NRE"
|
||||||
|
| EvalStackValue.ObjectRef addr
|
||||||
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) -> Some addr
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) -> Some addr
|
||||||
| s -> failwith $"TODO: called with unrecognised arg %O{s}"
|
| s -> failwith $"TODO: called with unrecognised arg %O{s}"
|
||||||
|
|
||||||
@@ -67,34 +171,72 @@ module Intrinsics =
|
|||||||
|
|
||||||
let state =
|
let state =
|
||||||
let vt =
|
let vt =
|
||||||
|
// https://github.com/dotnet/runtime/blob/2b21c73fa2c32fa0195e4a411a435dda185efd08/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs#L92
|
||||||
{
|
{
|
||||||
Fields = [ "m_type", CliType.ObjectRef arg ]
|
Name = "m_type"
|
||||||
|
Contents = CliType.ObjectRef arg
|
||||||
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
state.ConcreteTypes
|
||||||
|
(baseClassTypes.RuntimeType.Assembly,
|
||||||
|
baseClassTypes.RuntimeType.Namespace,
|
||||||
|
baseClassTypes.RuntimeType.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
}
|
}
|
||||||
|
|> List.singleton
|
||||||
|
|> CliValueType.OfFields Layout.Default
|
||||||
|
|
||||||
IlMachineState.pushToEvalStack (CliType.ValueType vt) currentThread state
|
IlMachineState.pushToEvalStack (CliType.ValueType vt) currentThread state
|
||||||
|> IlMachineState.advanceProgramCounter currentThread
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
Some state
|
Some state
|
||||||
|
| "System.Private.CoreLib", "Type", "get_IsValueType" ->
|
||||||
|
match methodToCall.Signature.ParameterTypes, methodToCall.Signature.ReturnType with
|
||||||
|
| [], ConcreteBool state.ConcreteTypes -> ()
|
||||||
|
| _ -> failwith "bad signature Type.get_IsValueType"
|
||||||
|
|
||||||
|
let this, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let this =
|
||||||
|
match this with
|
||||||
|
| EvalStackValue.ObjectRef ptr ->
|
||||||
|
IlMachineState.dereferencePointer state (ManagedPointerSource.Heap ptr)
|
||||||
|
| EvalStackValue.ManagedPointer ptr -> IlMachineState.dereferencePointer state ptr
|
||||||
|
| EvalStackValue.Float _
|
||||||
|
| EvalStackValue.Int32 _
|
||||||
|
| EvalStackValue.Int64 _ -> failwith "refusing to dereference literal"
|
||||||
|
| _ -> failwith "TODO"
|
||||||
|
// `this` should be of type Type
|
||||||
|
let ty =
|
||||||
|
match this with
|
||||||
|
| CliType.ValueType cvt ->
|
||||||
|
match CliValueType.DereferenceField "m_handle" cvt with
|
||||||
|
| CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.TypeHandlePtr cth)) -> cth
|
||||||
|
| _ -> failwith ""
|
||||||
|
| _ -> failwith "expected a Type"
|
||||||
|
|
||||||
|
let ty = AllConcreteTypes.lookup ty state.ConcreteTypes |> Option.get
|
||||||
|
let ty = state.LoadedAssembly(ty.Assembly).Value.TypeDefs.[ty.Definition.Get]
|
||||||
|
|
||||||
|
let isValueType =
|
||||||
|
match DumpedAssembly.resolveBaseType baseClassTypes state._LoadedAssemblies ty.Assembly ty.BaseType with
|
||||||
|
| ResolvedBaseType.Enum
|
||||||
|
| ResolvedBaseType.ValueType -> true
|
||||||
|
| ResolvedBaseType.Object
|
||||||
|
| ResolvedBaseType.Delegate -> false
|
||||||
|
|
||||||
|
IlMachineState.pushToEvalStack (CliType.ofBool isValueType) currentThread state
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|> Some
|
||||||
| "System.Private.CoreLib", "Unsafe", "AsPointer" ->
|
| "System.Private.CoreLib", "Unsafe", "AsPointer" ->
|
||||||
// Method signature: 1 generic parameter, we take a Byref of that parameter, and return a TypeDefn.Pointer(Void)
|
// Method signature: 1 generic parameter, we take a Byref of that parameter, and return a TypeDefn.Pointer(Void)
|
||||||
let arg, state = IlMachineState.popEvalStack currentThread state
|
let arg, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
let toPush =
|
let toPush =
|
||||||
match arg with
|
match arg with
|
||||||
| EvalStackValue.ManagedPointer ptr ->
|
| EvalStackValue.ManagedPointer ptr -> CliRuntimePointer.Managed ptr
|
||||||
match ptr with
|
|
||||||
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
|
|
||||||
CliRuntimePointer.Managed (
|
|
||||||
CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, whichVar)
|
|
||||||
)
|
|
||||||
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
|
|
||||||
CliRuntimePointer.Managed (
|
|
||||||
CliRuntimePointerSource.Argument (sourceThread, methodFrame, whichVar)
|
|
||||||
)
|
|
||||||
| ManagedPointerSource.Heap managedHeapAddress ->
|
|
||||||
CliRuntimePointer.Managed (CliRuntimePointerSource.Heap managedHeapAddress)
|
|
||||||
| ManagedPointerSource.Null -> failwith "todo"
|
|
||||||
| ManagedPointerSource.ArrayIndex _ -> failwith "TODO"
|
|
||||||
| x -> failwith $"TODO: Unsafe.AsPointer(%O{x})"
|
| x -> failwith $"TODO: Unsafe.AsPointer(%O{x})"
|
||||||
|
|
||||||
IlMachineState.pushToEvalStack (CliType.RuntimePointer toPush) currentThread state
|
IlMachineState.pushToEvalStack (CliType.RuntimePointer toPush) currentThread state
|
||||||
@@ -131,6 +273,47 @@ module Intrinsics =
|
|||||||
let result =
|
let result =
|
||||||
BitConverter.Int32BitsToSingle arg |> CliNumericType.Float32 |> CliType.Numeric
|
BitConverter.Int32BitsToSingle arg |> CliNumericType.Float32 |> CliType.Numeric
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack result currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|> Some
|
||||||
|
| "System.Private.CoreLib", "BitConverter", "DoubleToUInt64Bits" ->
|
||||||
|
match methodToCall.Signature.ParameterTypes, methodToCall.Signature.ReturnType with
|
||||||
|
| [ ConcreteDouble state.ConcreteTypes ], ConcreteUInt64 state.ConcreteTypes -> ()
|
||||||
|
| _ -> failwith "bad signature BitConverter.DoubleToUInt64Bits"
|
||||||
|
|
||||||
|
let arg, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let arg =
|
||||||
|
match arg with
|
||||||
|
| EvalStackValue.Float i -> i
|
||||||
|
| _ -> failwith "$TODO: {arr}"
|
||||||
|
|
||||||
|
let result =
|
||||||
|
BitConverter.DoubleToUInt64Bits arg
|
||||||
|
|> int64<uint64>
|
||||||
|
|> CliNumericType.Int64
|
||||||
|
|> CliType.Numeric
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack result currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|> Some
|
||||||
|
| "System.Private.CoreLib", "BitConverter", "UInt64BitsToDouble" ->
|
||||||
|
match methodToCall.Signature.ParameterTypes, methodToCall.Signature.ReturnType with
|
||||||
|
| [ ConcreteUInt64 state.ConcreteTypes ], ConcreteDouble state.ConcreteTypes -> ()
|
||||||
|
| _ -> failwith "bad signature BitConverter.DoubleToUInt64Bits"
|
||||||
|
|
||||||
|
let arg, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let arg =
|
||||||
|
match arg with
|
||||||
|
| EvalStackValue.Int64 i -> uint64 i
|
||||||
|
| _ -> failwith "$TODO: {arr}"
|
||||||
|
|
||||||
|
let result =
|
||||||
|
BitConverter.UInt64BitsToDouble arg |> CliNumericType.Float64 |> CliType.Numeric
|
||||||
|
|
||||||
state
|
state
|
||||||
|> IlMachineState.pushToEvalStack result currentThread
|
|> IlMachineState.pushToEvalStack result currentThread
|
||||||
|> IlMachineState.advanceProgramCounter currentThread
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
@@ -166,6 +349,44 @@ module Intrinsics =
|
|||||||
| EvalStackValue.Float f -> BitConverter.DoubleToInt64Bits f |> EvalStackValue.Int64
|
| EvalStackValue.Float f -> BitConverter.DoubleToInt64Bits f |> EvalStackValue.Int64
|
||||||
| _ -> failwith "TODO"
|
| _ -> failwith "TODO"
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack' result currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|> Some
|
||||||
|
| "System.Private.CoreLib", "BitConverter", "SingleToUInt32Bits" ->
|
||||||
|
match methodToCall.Signature.ParameterTypes, methodToCall.Signature.ReturnType with
|
||||||
|
| [ ConcreteSingle state.ConcreteTypes ], ConcreteUInt32 state.ConcreteTypes -> ()
|
||||||
|
| _ -> failwith "bad signature BitConverter.SingleToUInt32Bits"
|
||||||
|
|
||||||
|
let arg, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let result =
|
||||||
|
match arg with
|
||||||
|
| EvalStackValue.Float f ->
|
||||||
|
BitConverter.SingleToUInt32Bits (float32<float> f)
|
||||||
|
|> int<uint32>
|
||||||
|
|> EvalStackValue.Int32
|
||||||
|
| _ -> failwith "TODO"
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack' result currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|> Some
|
||||||
|
| "System.Private.CoreLib", "BitConverter", "UInt32BitsToSingle" ->
|
||||||
|
match methodToCall.Signature.ParameterTypes, methodToCall.Signature.ReturnType with
|
||||||
|
| [ ConcreteUInt32 state.ConcreteTypes ], ConcreteSingle state.ConcreteTypes -> ()
|
||||||
|
| _ -> failwith "bad signature BitConverter.UInt32BitsToSingle"
|
||||||
|
|
||||||
|
let arg, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let result =
|
||||||
|
match arg with
|
||||||
|
| EvalStackValue.Int32 f ->
|
||||||
|
BitConverter.UInt32BitsToSingle (uint32<int> f)
|
||||||
|
|> float<float32>
|
||||||
|
|> EvalStackValue.Float
|
||||||
|
| _ -> failwith "TODO"
|
||||||
|
|
||||||
state
|
state
|
||||||
|> IlMachineState.pushToEvalStack' result currentThread
|
|> IlMachineState.pushToEvalStack' result currentThread
|
||||||
|> IlMachineState.advanceProgramCounter currentThread
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
@@ -179,7 +400,8 @@ module Intrinsics =
|
|||||||
let arg1 =
|
let arg1 =
|
||||||
match arg1 with
|
match arg1 with
|
||||||
| EvalStackValue.ObjectRef h
|
| EvalStackValue.ObjectRef h
|
||||||
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap h) -> h
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap h) -> Some h
|
||||||
|
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> None
|
||||||
| EvalStackValue.Int32 _
|
| EvalStackValue.Int32 _
|
||||||
| EvalStackValue.Int64 _
|
| EvalStackValue.Int64 _
|
||||||
| EvalStackValue.Float _ -> failwith $"this isn't a string! {arg1}"
|
| EvalStackValue.Float _ -> failwith $"this isn't a string! {arg1}"
|
||||||
@@ -190,29 +412,38 @@ module Intrinsics =
|
|||||||
let arg2 =
|
let arg2 =
|
||||||
match arg2 with
|
match arg2 with
|
||||||
| EvalStackValue.ObjectRef h
|
| EvalStackValue.ObjectRef h
|
||||||
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap h) -> h
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap h) -> Some h
|
||||||
|
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> None
|
||||||
| EvalStackValue.Int32 _
|
| EvalStackValue.Int32 _
|
||||||
| EvalStackValue.Int64 _
|
| EvalStackValue.Int64 _
|
||||||
| EvalStackValue.Float _ -> failwith $"this isn't a string! {arg2}"
|
| EvalStackValue.Float _ -> failwith $"this isn't a string! {arg2}"
|
||||||
| _ -> failwith $"TODO: %O{arg2}"
|
| _ -> failwith $"TODO: %O{arg2}"
|
||||||
|
|
||||||
if arg1 = arg2 then
|
let areEqual =
|
||||||
state
|
match arg1, arg2 with
|
||||||
|> IlMachineState.pushToEvalStack (CliType.ofBool true) currentThread
|
| None, None -> true
|
||||||
|> IlMachineState.advanceProgramCounter currentThread
|
| Some _, None
|
||||||
|> Some
|
| None, Some _ -> false
|
||||||
else
|
| Some arg1, Some arg2 ->
|
||||||
|
if arg1 = arg2 then
|
||||||
|
true
|
||||||
|
else
|
||||||
|
|
||||||
let arg1 = ManagedHeap.Get arg1 state.ManagedHeap
|
let arg1 = ManagedHeap.get arg1 state.ManagedHeap
|
||||||
let arg2 = ManagedHeap.Get arg2 state.ManagedHeap
|
let arg2 = ManagedHeap.get arg2 state.ManagedHeap
|
||||||
|
|
||||||
if arg1.Fields.["_firstChar"] <> arg2.Fields.["_firstChar"] then
|
if
|
||||||
state
|
AllocatedNonArrayObject.DereferenceField "_firstChar" arg1
|
||||||
|> IlMachineState.pushToEvalStack (CliType.ofBool false) currentThread
|
<> AllocatedNonArrayObject.DereferenceField "_firstChar" arg2
|
||||||
|> IlMachineState.advanceProgramCounter currentThread
|
then
|
||||||
|> Some
|
false
|
||||||
else
|
else
|
||||||
failwith "TODO"
|
failwith "TODO"
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack (CliType.ofBool areEqual) currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|> Some
|
||||||
| _ -> None
|
| _ -> None
|
||||||
| "System.Private.CoreLib", "Unsafe", "ReadUnaligned" ->
|
| "System.Private.CoreLib", "Unsafe", "ReadUnaligned" ->
|
||||||
let ptr, state = IlMachineState.popEvalStack currentThread state
|
let ptr, state = IlMachineState.popEvalStack currentThread state
|
||||||
@@ -220,19 +451,13 @@ module Intrinsics =
|
|||||||
let v : CliType =
|
let v : CliType =
|
||||||
let rec go ptr =
|
let rec go ptr =
|
||||||
match ptr with
|
match ptr with
|
||||||
| EvalStackValue.ManagedPointer src ->
|
| EvalStackValue.ManagedPointer src -> IlMachineState.dereferencePointer state src
|
||||||
match src with
|
|
||||||
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) -> failwith "todo"
|
|
||||||
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) -> failwith "todo"
|
|
||||||
| ManagedPointerSource.Heap managedHeapAddress -> failwith "todo"
|
|
||||||
| ManagedPointerSource.ArrayIndex (arr, index) ->
|
|
||||||
state |> IlMachineState.getArrayValue arr index
|
|
||||||
| ManagedPointerSource.Null -> failwith "TODO: throw NRE"
|
|
||||||
| EvalStackValue.NativeInt src -> failwith "TODO"
|
| EvalStackValue.NativeInt src -> failwith "TODO"
|
||||||
| EvalStackValue.ObjectRef ptr -> failwith "TODO"
|
| EvalStackValue.ObjectRef ptr -> failwith "TODO"
|
||||||
| EvalStackValue.UserDefinedValueType [ _, field ] -> go field
|
| EvalStackValue.UserDefinedValueType vt ->
|
||||||
| EvalStackValue.UserDefinedValueType []
|
match CliValueType.TryExactlyOneField vt with
|
||||||
| EvalStackValue.UserDefinedValueType (_ :: _ :: _)
|
| None -> failwith "TODO"
|
||||||
|
| Some field -> go (EvalStackValue.ofCliType field.Contents)
|
||||||
| EvalStackValue.Int32 _
|
| EvalStackValue.Int32 _
|
||||||
| EvalStackValue.Int64 _
|
| EvalStackValue.Int64 _
|
||||||
| EvalStackValue.Float _ -> failwith $"this isn't a pointer! {ptr}"
|
| EvalStackValue.Float _ -> failwith $"this isn't a pointer! {ptr}"
|
||||||
@@ -277,15 +502,46 @@ module Intrinsics =
|
|||||||
| [], ConcreteBool state.ConcreteTypes -> ()
|
| [], ConcreteBool state.ConcreteTypes -> ()
|
||||||
| _ -> failwith "bad signature for System.Private.CoreLib.RuntimeHelpers.IsReferenceOrContainsReference"
|
| _ -> failwith "bad signature for System.Private.CoreLib.RuntimeHelpers.IsReferenceOrContainsReference"
|
||||||
|
|
||||||
let generic =
|
let arg = Seq.exactlyOne methodToCall.Generics
|
||||||
AllConcreteTypes.lookup (Seq.exactlyOne methodToCall.Generics) state.ConcreteTypes
|
|
||||||
|
|
||||||
let generic =
|
let state, result =
|
||||||
match generic with
|
// Some types appear circular, because they're hardcoded in the runtime. We have to special-case them.
|
||||||
| None -> failwith "somehow have not already concretised type in IsReferenceOrContainsReferences"
|
match arg with
|
||||||
| Some generic -> generic
|
| ConcreteChar state.ConcreteTypes -> state, 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
|
||||||
|
| ResolvedBaseType.ValueType ->
|
||||||
|
td
|
||||||
|
|> TypeInfo.mapGeneric (fun (par, _) -> TypeDefn.GenericTypeParameter par.SequenceNumber)
|
||||||
|
|> containsRefType loggerFactory baseClassTypes state ImmutableDictionary.Empty
|
||||||
|
|> fun (state, _, result) -> state, result
|
||||||
|
| ResolvedBaseType.Object
|
||||||
|
| ResolvedBaseType.Delegate -> state, true
|
||||||
|
|
||||||
|
let state =
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack (CliType.ofBool result) currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
|
Some state
|
||||||
| "System.Private.CoreLib", "RuntimeHelpers", "InitializeArray" ->
|
| "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
|
// 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
|
match methodToCall.Signature.ParameterTypes, methodToCall.Signature.ReturnType with
|
||||||
@@ -297,11 +553,110 @@ module Intrinsics =
|
|||||||
failwith "TODO: if arg1 contains null handle, throw ArgumentException"
|
failwith "TODO: if arg1 contains null handle, throw ArgumentException"
|
||||||
|
|
||||||
failwith "TODO: array initialization"
|
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
|
||||||
|
|
||||||
|
let inputAddr, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let ptr =
|
||||||
|
match inputAddr with
|
||||||
|
| EvalStackValue.Int32 _
|
||||||
|
| EvalStackValue.Int64 _
|
||||||
|
| EvalStackValue.Float _ -> failwith "expected pointer type"
|
||||||
|
| EvalStackValue.NativeInt nativeIntSource -> failwith "todo"
|
||||||
|
| EvalStackValue.ManagedPointer src ->
|
||||||
|
ManagedPointerSource.InterpretedAsType (src, to_)
|
||||||
|
|> EvalStackValue.ManagedPointer
|
||||||
|
| EvalStackValue.ObjectRef addr ->
|
||||||
|
ManagedPointerSource.InterpretedAsType (ManagedPointerSource.Heap addr, to_)
|
||||||
|
|> EvalStackValue.ManagedPointer
|
||||||
|
| EvalStackValue.UserDefinedValueType evalStackValueUserType -> failwith "todo"
|
||||||
|
|
||||||
|
let state =
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack' ptr currentThread
|
||||||
|
|> 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" ->
|
| "System.Private.CoreLib", "RuntimeHelpers", "CreateSpan" ->
|
||||||
// https://github.com/dotnet/runtime/blob/9e5e6aa7bc36aeb2a154709a9d1192030c30a2ef/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs#L153
|
// https://github.com/dotnet/runtime/blob/9e5e6aa7bc36aeb2a154709a9d1192030c30a2ef/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs#L153
|
||||||
None
|
None
|
||||||
| "System.Private.CoreLib", "Type", "op_Equality" ->
|
| "System.Private.CoreLib", "Type", "op_Equality" ->
|
||||||
// https://github.com/dotnet/runtime/blob/ec11903827fc28847d775ba17e0cd1ff56cfbc2e/src/libraries/System.Private.CoreLib/src/System/Type.cs#L703
|
// https://github.com/dotnet/runtime/blob/ec11903827fc28847d775ba17e0cd1ff56cfbc2e/src/libraries/System.Private.CoreLib/src/System/Type.cs#L703
|
||||||
None
|
None
|
||||||
|
| "System.Private.CoreLib", "MemoryMarshal", "GetArrayDataReference" ->
|
||||||
|
// https://github.com/dotnet/runtime/blob/d258af50034c192bf7f0a18856bf83d2903d98ae/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.CoreCLR.cs#L20
|
||||||
|
let generic = Seq.exactlyOne methodToCall.Generics
|
||||||
|
|
||||||
|
match methodToCall.Signature.ParameterTypes, methodToCall.Signature.ReturnType with
|
||||||
|
| [ ConcreteGenericArray state.ConcreteTypes generic ], ConcreteByref t when t = generic -> ()
|
||||||
|
| _ -> failwith "bad signature MemoryMarshal.GetArrayDataReference"
|
||||||
|
|
||||||
|
let arr, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let toPush =
|
||||||
|
match arr with
|
||||||
|
| EvalStackValue.Int32 _
|
||||||
|
| EvalStackValue.Int64 _
|
||||||
|
| EvalStackValue.Float _ -> failwith "expected reference"
|
||||||
|
| EvalStackValue.NativeInt nativeIntSource -> failwith "todo"
|
||||||
|
| EvalStackValue.ObjectRef addr
|
||||||
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) ->
|
||||||
|
if not (state.ManagedHeap.Arrays.ContainsKey addr) then
|
||||||
|
failwith "array not found"
|
||||||
|
|
||||||
|
EvalStackValue.ManagedPointer (ManagedPointerSource.ArrayIndex (addr, 0))
|
||||||
|
| EvalStackValue.UserDefinedValueType evalStackValueUserType -> failwith "todo"
|
||||||
|
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> failwith "TODO: raise NRE"
|
||||||
|
| EvalStackValue.ManagedPointer _ -> failwith "todo"
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack' toPush currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|> Some
|
||||||
| a, b, c -> failwith $"TODO: implement JIT intrinsic {a}.{b}.{c}"
|
| a, b, c -> failwith $"TODO: implement JIT intrinsic {a}.{b}.{c}"
|
||||||
|> Option.map (fun s -> s.WithThreadSwitchedToAssembly callerAssy currentThread |> fst)
|
|> Option.map (fun s -> s.WithThreadSwitchedToAssembly callerAssy currentThread |> fst)
|
||||||
|
12
WoofWare.PawPrint/List.fs
Normal file
12
WoofWare.PawPrint/List.fs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace WoofWare.PawPrint
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module List =
|
||||||
|
let replaceWhere (f : 'a -> 'a option) (l : 'a list) : 'a list =
|
||||||
|
([], l)
|
||||||
|
||> List.fold (fun acc x ->
|
||||||
|
match f x with
|
||||||
|
| None -> x :: acc
|
||||||
|
| Some y -> y :: acc
|
||||||
|
)
|
||||||
|
|> List.rev
|
@@ -8,11 +8,20 @@ type SyncBlock =
|
|||||||
|
|
||||||
type AllocatedNonArrayObject =
|
type AllocatedNonArrayObject =
|
||||||
{
|
{
|
||||||
Fields : Map<string, CliType>
|
// TODO: this is a slightly odd domain; the same type for value types as class types!
|
||||||
|
Contents : CliValueType
|
||||||
ConcreteType : ConcreteTypeHandle
|
ConcreteType : ConcreteTypeHandle
|
||||||
SyncBlock : SyncBlock
|
SyncBlock : SyncBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static member DereferenceField (name : string) (f : AllocatedNonArrayObject) : CliType =
|
||||||
|
CliValueType.DereferenceField name f.Contents
|
||||||
|
|
||||||
|
static member SetField (name : string) (v : CliType) (f : AllocatedNonArrayObject) : AllocatedNonArrayObject =
|
||||||
|
{ f with
|
||||||
|
Contents = CliValueType.WithFieldSet name v f.Contents
|
||||||
|
}
|
||||||
|
|
||||||
type AllocatedArray =
|
type AllocatedArray =
|
||||||
{
|
{
|
||||||
Length : int
|
Length : int
|
||||||
@@ -29,7 +38,9 @@ type ManagedHeap =
|
|||||||
StringArrayData : ImmutableArray<char>
|
StringArrayData : ImmutableArray<char>
|
||||||
}
|
}
|
||||||
|
|
||||||
static member Empty : ManagedHeap =
|
[<RequireQualifiedAccess>]
|
||||||
|
module ManagedHeap =
|
||||||
|
let empty : ManagedHeap =
|
||||||
{
|
{
|
||||||
NonArrayObjects = Map.empty
|
NonArrayObjects = Map.empty
|
||||||
FirstAvailableAddress = 1
|
FirstAvailableAddress = 1
|
||||||
@@ -37,12 +48,12 @@ type ManagedHeap =
|
|||||||
StringArrayData = ImmutableArray.Empty
|
StringArrayData = ImmutableArray.Empty
|
||||||
}
|
}
|
||||||
|
|
||||||
static member GetSyncBlock (addr : ManagedHeapAddress) (heap : ManagedHeap) : SyncBlock =
|
let getSyncBlock (addr : ManagedHeapAddress) (heap : ManagedHeap) : SyncBlock =
|
||||||
match heap.NonArrayObjects.TryGetValue addr with
|
match heap.NonArrayObjects.TryGetValue addr with
|
||||||
| false, _ -> failwith "TODO: getting sync block of array"
|
| false, _ -> failwith "TODO: getting sync block of array"
|
||||||
| true, v -> v.SyncBlock
|
| true, v -> v.SyncBlock
|
||||||
|
|
||||||
static member SetSyncBlock (addr : ManagedHeapAddress) (syncValue : SyncBlock) (heap : ManagedHeap) : ManagedHeap =
|
let setSyncBlock (addr : ManagedHeapAddress) (syncValue : SyncBlock) (heap : ManagedHeap) : ManagedHeap =
|
||||||
match heap.NonArrayObjects.TryGetValue addr with
|
match heap.NonArrayObjects.TryGetValue addr with
|
||||||
| false, _ -> failwith "TODO: locked on an array object"
|
| false, _ -> failwith "TODO: locked on an array object"
|
||||||
| true, v ->
|
| true, v ->
|
||||||
@@ -55,7 +66,7 @@ type ManagedHeap =
|
|||||||
NonArrayObjects = heap.NonArrayObjects |> Map.add addr newV
|
NonArrayObjects = heap.NonArrayObjects |> Map.add addr newV
|
||||||
}
|
}
|
||||||
|
|
||||||
static member AllocateArray (ty : AllocatedArray) (heap : ManagedHeap) : ManagedHeapAddress * ManagedHeap =
|
let allocateArray (ty : AllocatedArray) (heap : ManagedHeap) : ManagedHeapAddress * ManagedHeap =
|
||||||
let addr = heap.FirstAvailableAddress
|
let addr = heap.FirstAvailableAddress
|
||||||
|
|
||||||
let heap =
|
let heap =
|
||||||
@@ -68,7 +79,7 @@ type ManagedHeap =
|
|||||||
|
|
||||||
ManagedHeapAddress addr, heap
|
ManagedHeapAddress addr, heap
|
||||||
|
|
||||||
static member AllocateString (len : int) (heap : ManagedHeap) : int * ManagedHeap =
|
let allocateString (len : int) (heap : ManagedHeap) : int * ManagedHeap =
|
||||||
let addr = heap.StringArrayData.Length
|
let addr = heap.StringArrayData.Length
|
||||||
|
|
||||||
let heap =
|
let heap =
|
||||||
@@ -80,7 +91,7 @@ type ManagedHeap =
|
|||||||
|
|
||||||
addr, heap
|
addr, heap
|
||||||
|
|
||||||
static member SetStringData (addr : int) (contents : string) (heap : ManagedHeap) : ManagedHeap =
|
let setStringData (addr : int) (contents : string) (heap : ManagedHeap) : ManagedHeap =
|
||||||
let newArr =
|
let newArr =
|
||||||
(heap.StringArrayData, seq { 0 .. contents.Length - 1 })
|
(heap.StringArrayData, seq { 0 .. contents.Length - 1 })
|
||||||
||> Seq.fold (fun data count -> data.SetItem (addr + count, contents.[count]))
|
||> Seq.fold (fun data count -> data.SetItem (addr + count, contents.[count]))
|
||||||
@@ -92,11 +103,7 @@ type ManagedHeap =
|
|||||||
|
|
||||||
heap
|
heap
|
||||||
|
|
||||||
static member AllocateNonArray
|
let allocateNonArray (ty : AllocatedNonArrayObject) (heap : ManagedHeap) : ManagedHeapAddress * ManagedHeap =
|
||||||
(ty : AllocatedNonArrayObject)
|
|
||||||
(heap : ManagedHeap)
|
|
||||||
: ManagedHeapAddress * ManagedHeap
|
|
||||||
=
|
|
||||||
let addr = heap.FirstAvailableAddress
|
let addr = heap.FirstAvailableAddress
|
||||||
|
|
||||||
let heap =
|
let heap =
|
||||||
@@ -109,7 +116,7 @@ type ManagedHeap =
|
|||||||
|
|
||||||
ManagedHeapAddress addr, heap
|
ManagedHeapAddress addr, heap
|
||||||
|
|
||||||
static member GetArrayValue (alloc : ManagedHeapAddress) (offset : int) (heap : ManagedHeap) : CliType =
|
let getArrayValue (alloc : ManagedHeapAddress) (offset : int) (heap : ManagedHeap) : CliType =
|
||||||
match heap.Arrays.TryGetValue alloc with
|
match heap.Arrays.TryGetValue alloc with
|
||||||
| false, _ -> failwith "TODO: array not on heap"
|
| false, _ -> failwith "TODO: array not on heap"
|
||||||
| true, arr ->
|
| true, arr ->
|
||||||
@@ -119,17 +126,17 @@ type ManagedHeap =
|
|||||||
|
|
||||||
arr.Elements.[offset]
|
arr.Elements.[offset]
|
||||||
|
|
||||||
static member Get (alloc : ManagedHeapAddress) (heap : ManagedHeap) : AllocatedNonArrayObject =
|
let get (alloc : ManagedHeapAddress) (heap : ManagedHeap) : AllocatedNonArrayObject =
|
||||||
// TODO: arrays too
|
// TODO: arrays too
|
||||||
heap.NonArrayObjects.[alloc]
|
heap.NonArrayObjects.[alloc]
|
||||||
|
|
||||||
static member SetArrayValue
|
let set (alloc : ManagedHeapAddress) (v : AllocatedNonArrayObject) (heap : ManagedHeap) : ManagedHeap =
|
||||||
(alloc : ManagedHeapAddress)
|
// TODO: arrays too
|
||||||
(offset : int)
|
{ heap with
|
||||||
(v : CliType)
|
NonArrayObjects = heap.NonArrayObjects |> Map.add alloc v
|
||||||
(heap : ManagedHeap)
|
}
|
||||||
: ManagedHeap
|
|
||||||
=
|
let setArrayValue (alloc : ManagedHeapAddress) (offset : int) (v : CliType) (heap : ManagedHeap) : ManagedHeap =
|
||||||
let newArrs =
|
let newArrs =
|
||||||
heap.Arrays
|
heap.Arrays
|
||||||
|> Map.change
|
|> Map.change
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
namespace WoofWare.PawPrint
|
namespace WoofWare.PawPrint
|
||||||
|
|
||||||
|
open System
|
||||||
open Microsoft.Extensions.Logging
|
open Microsoft.Extensions.Logging
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
@@ -37,19 +38,6 @@ module NullaryIlOp =
|
|||||||
| LdindR4 -> CliType.Numeric (CliNumericType.Float32 0.0f)
|
| LdindR4 -> CliType.Numeric (CliNumericType.Float32 0.0f)
|
||||||
| LdindR8 -> CliType.Numeric (CliNumericType.Float64 0.0)
|
| LdindR8 -> CliType.Numeric (CliNumericType.Float64 0.0)
|
||||||
|
|
||||||
/// Retrieve a value from a pointer
|
|
||||||
let private loadFromPointerSource (state : IlMachineState) (src : ManagedPointerSource) : CliType =
|
|
||||||
match src with
|
|
||||||
| ManagedPointerSource.Null -> failwith "unexpected null pointer in Ldind operation"
|
|
||||||
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
|
|
||||||
state.ThreadState.[sourceThread].MethodStates.[methodFrame].Arguments.[int<uint16> whichVar]
|
|
||||||
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
|
|
||||||
state.ThreadState.[sourceThread].MethodStates.[methodFrame].LocalVariables.[int<uint16> whichVar]
|
|
||||||
| ManagedPointerSource.Heap managedHeapAddress -> failwith "TODO: Heap pointer dereferencing not implemented"
|
|
||||||
| ManagedPointerSource.ArrayIndex (arr, index) ->
|
|
||||||
let arr = state.ManagedHeap.Arrays.[arr]
|
|
||||||
arr.Elements.[index]
|
|
||||||
|
|
||||||
// Unified Ldind implementation
|
// Unified Ldind implementation
|
||||||
let private executeLdind
|
let private executeLdind
|
||||||
(targetType : LdindTargetType)
|
(targetType : LdindTargetType)
|
||||||
@@ -61,11 +49,11 @@ module NullaryIlOp =
|
|||||||
|
|
||||||
let loadedValue =
|
let loadedValue =
|
||||||
match popped with
|
match popped with
|
||||||
| EvalStackValue.ManagedPointer src -> loadFromPointerSource state src
|
| EvalStackValue.ManagedPointer src -> IlMachineState.dereferencePointer state src
|
||||||
| EvalStackValue.NativeInt nativeIntSource ->
|
| EvalStackValue.NativeInt nativeIntSource ->
|
||||||
failwith $"TODO: Native int pointer dereferencing not implemented for {targetType}"
|
failwith $"TODO: Native int pointer dereferencing not implemented for {targetType}"
|
||||||
| EvalStackValue.ObjectRef managedHeapAddress ->
|
| 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}"
|
| other -> failwith $"Unexpected eval stack value for Ldind operation: {other}"
|
||||||
|
|
||||||
let loadedValue = loadedValue |> EvalStackValue.ofCliType
|
let loadedValue = loadedValue |> EvalStackValue.ofCliType
|
||||||
@@ -94,52 +82,39 @@ module NullaryIlOp =
|
|||||||
| EvalStackValue.ManagedPointer src ->
|
| EvalStackValue.ManagedPointer src ->
|
||||||
match src with
|
match src with
|
||||||
| ManagedPointerSource.Null -> failwith "TODO: throw NullReferenceException"
|
| ManagedPointerSource.Null -> failwith "TODO: throw NullReferenceException"
|
||||||
|
| ManagedPointerSource.InterpretedAsType (src, ty) -> failwith "TODO"
|
||||||
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
|
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
|
||||||
failwith "unexpected - can we really write to an argument?"
|
failwith "unexpected - can we really write to an argument?"
|
||||||
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
|
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
|
||||||
{ state with
|
state
|
||||||
ThreadState =
|
|> IlMachineState.setLocalVariable
|
||||||
state.ThreadState
|
sourceThread
|
||||||
|> Map.change
|
methodFrame
|
||||||
sourceThread
|
whichVar
|
||||||
(fun state ->
|
(EvalStackValue.toCliTypeCoerced varType valueToStore)
|
||||||
match state with
|
|
||||||
| None -> failwith "tried to store in local variables of nonexistent stack frame"
|
|
||||||
| Some state ->
|
|
||||||
let frame = state.MethodStates.[methodFrame]
|
|
||||||
|
|
||||||
let frame =
|
|
||||||
{ frame with
|
|
||||||
LocalVariables =
|
|
||||||
frame.LocalVariables.SetItem (
|
|
||||||
int<uint16> whichVar,
|
|
||||||
EvalStackValue.toCliTypeCoerced varType valueToStore
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
{ state with
|
|
||||||
MethodStates = state.MethodStates.SetItem (methodFrame, frame)
|
|
||||||
}
|
|
||||||
|> Some
|
|
||||||
)
|
|
||||||
}
|
|
||||||
| ManagedPointerSource.Heap managedHeapAddress -> failwith "todo"
|
| ManagedPointerSource.Heap managedHeapAddress -> failwith "todo"
|
||||||
| ManagedPointerSource.ArrayIndex _ -> failwith "todo"
|
| ManagedPointerSource.ArrayIndex _ -> failwith "todo"
|
||||||
|
| ManagedPointerSource.Field (managedPointerSource, fieldName) ->
|
||||||
|
state
|
||||||
|
|> IlMachineState.setFieldValue
|
||||||
|
managedPointerSource
|
||||||
|
(EvalStackValue.toCliTypeCoerced varType valueToStore)
|
||||||
|
fieldName
|
||||||
| EvalStackValue.ObjectRef managedHeapAddress -> failwith "todo"
|
| EvalStackValue.ObjectRef managedHeapAddress -> failwith "todo"
|
||||||
|
|
||||||
let internal ldElem
|
let internal getArrayElt
|
||||||
(targetCliTypeZero : CliType)
|
|
||||||
(index : EvalStackValue)
|
(index : EvalStackValue)
|
||||||
(arr : EvalStackValue)
|
(arr : EvalStackValue)
|
||||||
(currentThread : ThreadId)
|
(currentThread : ThreadId)
|
||||||
(state : IlMachineState)
|
(state : IlMachineState)
|
||||||
: ExecutionResult
|
: CliType
|
||||||
=
|
=
|
||||||
let index =
|
let index =
|
||||||
match index with
|
match index with
|
||||||
| EvalStackValue.NativeInt src ->
|
| EvalStackValue.NativeInt src ->
|
||||||
match src with
|
match src with
|
||||||
| NativeIntSource.FunctionPointer _
|
| NativeIntSource.FunctionPointer _
|
||||||
|
| NativeIntSource.FieldHandlePtr _
|
||||||
| NativeIntSource.TypeHandlePtr _
|
| NativeIntSource.TypeHandlePtr _
|
||||||
| NativeIntSource.ManagedPointer _ -> failwith "Refusing to treat a pointer as an array index"
|
| NativeIntSource.ManagedPointer _ -> failwith "Refusing to treat a pointer as an array index"
|
||||||
| NativeIntSource.Verbatim i -> i |> int32
|
| NativeIntSource.Verbatim i -> i |> int32
|
||||||
@@ -153,14 +128,7 @@ module NullaryIlOp =
|
|||||||
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> failwith "TODO: throw NRE"
|
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> failwith "TODO: throw NRE"
|
||||||
| _ -> failwith $"Invalid array: %O{arr}"
|
| _ -> failwith $"Invalid array: %O{arr}"
|
||||||
|
|
||||||
let value = IlMachineState.getArrayValue arrAddr index state
|
IlMachineState.getArrayValue arrAddr index state
|
||||||
|
|
||||||
let state =
|
|
||||||
state
|
|
||||||
|> IlMachineState.pushToEvalStack value currentThread
|
|
||||||
|> IlMachineState.advanceProgramCounter currentThread
|
|
||||||
|
|
||||||
ExecutionResult.Stepped (state, WhatWeDid.Executed)
|
|
||||||
|
|
||||||
let internal stElem
|
let internal stElem
|
||||||
(targetCliTypeZero : CliType)
|
(targetCliTypeZero : CliType)
|
||||||
@@ -176,6 +144,7 @@ module NullaryIlOp =
|
|||||||
| EvalStackValue.NativeInt src ->
|
| EvalStackValue.NativeInt src ->
|
||||||
match src with
|
match src with
|
||||||
| NativeIntSource.FunctionPointer _
|
| NativeIntSource.FunctionPointer _
|
||||||
|
| NativeIntSource.FieldHandlePtr _
|
||||||
| NativeIntSource.TypeHandlePtr _
|
| NativeIntSource.TypeHandlePtr _
|
||||||
| NativeIntSource.ManagedPointer _ -> failwith "Refusing to treat a pointer as an array index"
|
| NativeIntSource.ManagedPointer _ -> failwith "Refusing to treat a pointer as an array index"
|
||||||
| NativeIntSource.Verbatim i -> i |> int32
|
| NativeIntSource.Verbatim i -> i |> int32
|
||||||
@@ -442,7 +411,7 @@ module NullaryIlOp =
|
|||||||
| Sub ->
|
| Sub ->
|
||||||
let val2, state = IlMachineState.popEvalStack currentThread state
|
let val2, state = IlMachineState.popEvalStack currentThread state
|
||||||
let val1, state = IlMachineState.popEvalStack currentThread state
|
let val1, state = IlMachineState.popEvalStack currentThread state
|
||||||
let result = BinaryArithmetic.execute ArithmeticOperation.sub val1 val2
|
let result = BinaryArithmetic.execute ArithmeticOperation.sub state val1 val2
|
||||||
|
|
||||||
state
|
state
|
||||||
|> IlMachineState.pushToEvalStack' result currentThread
|
|> IlMachineState.pushToEvalStack' result currentThread
|
||||||
@@ -452,30 +421,84 @@ module NullaryIlOp =
|
|||||||
| Sub_ovf -> failwith "TODO: Sub_ovf unimplemented"
|
| Sub_ovf -> failwith "TODO: Sub_ovf unimplemented"
|
||||||
| Sub_ovf_un -> failwith "TODO: Sub_ovf_un unimplemented"
|
| Sub_ovf_un -> failwith "TODO: Sub_ovf_un unimplemented"
|
||||||
| Add ->
|
| Add ->
|
||||||
let val1, state = IlMachineState.popEvalStack currentThread state
|
|
||||||
let val2, state = IlMachineState.popEvalStack currentThread state
|
let val2, state = IlMachineState.popEvalStack currentThread state
|
||||||
let result = BinaryArithmetic.execute ArithmeticOperation.add val1 val2
|
let val1, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let result = BinaryArithmetic.execute ArithmeticOperation.add state val1 val2
|
||||||
|
|
||||||
state
|
state
|
||||||
|> IlMachineState.pushToEvalStack' result currentThread
|
|> IlMachineState.pushToEvalStack' result currentThread
|
||||||
|> IlMachineState.advanceProgramCounter currentThread
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|> Tuple.withRight WhatWeDid.Executed
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
|> ExecutionResult.Stepped
|
|> ExecutionResult.Stepped
|
||||||
| Add_ovf -> failwith "TODO: Add_ovf unimplemented"
|
| Add_ovf ->
|
||||||
|
let val2, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let val1, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let result =
|
||||||
|
try
|
||||||
|
BinaryArithmetic.execute ArithmeticOperation.addOvf state val1 val2 |> Ok
|
||||||
|
with :? OverflowException as e ->
|
||||||
|
Error e
|
||||||
|
|
||||||
|
let state =
|
||||||
|
match result with
|
||||||
|
| Ok result -> state |> IlMachineState.pushToEvalStack' result currentThread
|
||||||
|
| Error excToThrow -> failwith "TODO: throw OverflowException"
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
|
|> ExecutionResult.Stepped
|
||||||
| Add_ovf_un -> failwith "TODO: Add_ovf_un unimplemented"
|
| Add_ovf_un -> failwith "TODO: Add_ovf_un unimplemented"
|
||||||
| Mul ->
|
| Mul ->
|
||||||
let val1, state = IlMachineState.popEvalStack currentThread state
|
|
||||||
let val2, state = IlMachineState.popEvalStack currentThread state
|
let val2, state = IlMachineState.popEvalStack currentThread state
|
||||||
let result = BinaryArithmetic.execute ArithmeticOperation.mul val1 val2
|
let val1, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let result = BinaryArithmetic.execute ArithmeticOperation.mul state val1 val2
|
||||||
|
|
||||||
state
|
state
|
||||||
|> IlMachineState.pushToEvalStack' result currentThread
|
|> IlMachineState.pushToEvalStack' result currentThread
|
||||||
|> IlMachineState.advanceProgramCounter currentThread
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|> Tuple.withRight WhatWeDid.Executed
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
|> ExecutionResult.Stepped
|
|> ExecutionResult.Stepped
|
||||||
| Mul_ovf -> failwith "TODO: Mul_ovf unimplemented"
|
| Mul_ovf ->
|
||||||
|
let val2, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let val1, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let result =
|
||||||
|
try
|
||||||
|
BinaryArithmetic.execute ArithmeticOperation.mulOvf state val1 val2 |> Ok
|
||||||
|
with :? OverflowException as e ->
|
||||||
|
Error e
|
||||||
|
|
||||||
|
let state =
|
||||||
|
match result with
|
||||||
|
| Ok result -> state |> IlMachineState.pushToEvalStack' result currentThread
|
||||||
|
| Error excToThrow -> failwith "TODO: throw OverflowException"
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
|
|> ExecutionResult.Stepped
|
||||||
| Mul_ovf_un -> failwith "TODO: Mul_ovf_un unimplemented"
|
| Mul_ovf_un -> failwith "TODO: Mul_ovf_un unimplemented"
|
||||||
| Div -> failwith "TODO: Div unimplemented"
|
| Div ->
|
||||||
|
let val2, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let val1, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let result =
|
||||||
|
try
|
||||||
|
BinaryArithmetic.execute ArithmeticOperation.div state val1 val2 |> Ok
|
||||||
|
with :? OverflowException as e ->
|
||||||
|
Error e
|
||||||
|
|
||||||
|
let state =
|
||||||
|
match result with
|
||||||
|
| Ok result -> state |> IlMachineState.pushToEvalStack' result currentThread
|
||||||
|
| Error excToThrow -> failwith "TODO: throw OverflowException"
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
|
|> ExecutionResult.Stepped
|
||||||
| Div_un -> failwith "TODO: Div_un unimplemented"
|
| Div_un -> failwith "TODO: Div_un unimplemented"
|
||||||
| Shr ->
|
| Shr ->
|
||||||
let shift, state = IlMachineState.popEvalStack currentThread state
|
let shift, state = IlMachineState.popEvalStack currentThread state
|
||||||
@@ -502,7 +525,33 @@ module NullaryIlOp =
|
|||||||
|> IlMachineState.advanceProgramCounter currentThread
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
|
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
|
||||||
| Shr_un -> failwith "TODO: Shr_un unimplemented"
|
| Shr_un ->
|
||||||
|
let shift, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let number, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let shift =
|
||||||
|
match shift with
|
||||||
|
| EvalStackValue.Int32 i -> i
|
||||||
|
| EvalStackValue.NativeInt (NativeIntSource.Verbatim i) -> int<int64> i
|
||||||
|
| _ -> failwith $"Not allowed shift of {shift}"
|
||||||
|
|
||||||
|
let result =
|
||||||
|
// See table III.6
|
||||||
|
match number with
|
||||||
|
| EvalStackValue.Int32 i -> uint32<int> i >>> shift |> int32<uint32> |> EvalStackValue.Int32
|
||||||
|
| EvalStackValue.Int64 i -> uint64<int64> i >>> shift |> int64<uint64> |> EvalStackValue.Int64
|
||||||
|
| EvalStackValue.NativeInt (NativeIntSource.Verbatim i) ->
|
||||||
|
(uint64<int64> i >>> shift |> int64<uint64>)
|
||||||
|
|> NativeIntSource.Verbatim
|
||||||
|
|> EvalStackValue.NativeInt
|
||||||
|
| _ -> failwith $"Not allowed to shift {number}"
|
||||||
|
|
||||||
|
let state =
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack' result currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
|
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
|
||||||
| Shl ->
|
| Shl ->
|
||||||
let shift, state = IlMachineState.popEvalStack currentThread state
|
let shift, state = IlMachineState.popEvalStack currentThread state
|
||||||
let number, state = IlMachineState.popEvalStack currentThread state
|
let number, state = IlMachineState.popEvalStack currentThread state
|
||||||
@@ -590,7 +639,37 @@ module NullaryIlOp =
|
|||||||
|> IlMachineState.advanceProgramCounter currentThread
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
|
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
|
||||||
| Xor -> failwith "TODO: Xor unimplemented"
|
| Xor ->
|
||||||
|
let v2, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let v1, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let result =
|
||||||
|
match v1, v2 with
|
||||||
|
| EvalStackValue.Int32 v1, EvalStackValue.Int32 v2 -> v1 ^^^ v2 |> EvalStackValue.Int32
|
||||||
|
| EvalStackValue.Int32 v1, EvalStackValue.NativeInt (NativeIntSource.Verbatim v2) ->
|
||||||
|
int64<int32> v1 ^^^ v2 |> NativeIntSource.Verbatim |> EvalStackValue.NativeInt
|
||||||
|
| EvalStackValue.Int32 _, EvalStackValue.NativeInt _ ->
|
||||||
|
failwith $"can't do binary operation on non-verbatim native int {v2}"
|
||||||
|
| EvalStackValue.Int64 v1, EvalStackValue.Int64 v2 -> v1 ^^^ v2 |> EvalStackValue.Int64
|
||||||
|
| EvalStackValue.NativeInt (NativeIntSource.Verbatim v1), EvalStackValue.Int32 v2 ->
|
||||||
|
v1 ^^^ int64<int32> v2 |> NativeIntSource.Verbatim |> EvalStackValue.NativeInt
|
||||||
|
| EvalStackValue.NativeInt _, EvalStackValue.Int32 _ ->
|
||||||
|
failwith $"can't do binary operation on non-verbatim native int {v1}"
|
||||||
|
| EvalStackValue.NativeInt (NativeIntSource.Verbatim v1),
|
||||||
|
EvalStackValue.NativeInt (NativeIntSource.Verbatim v2) ->
|
||||||
|
v1 ^^^ v2 |> NativeIntSource.Verbatim |> EvalStackValue.NativeInt
|
||||||
|
| EvalStackValue.NativeInt (NativeIntSource.Verbatim _), EvalStackValue.NativeInt _ ->
|
||||||
|
failwith $"can't do binary operation on non-verbatim native int {v2}"
|
||||||
|
| EvalStackValue.NativeInt _, EvalStackValue.NativeInt (NativeIntSource.Verbatim _) ->
|
||||||
|
failwith $"can't do binary operation on non-verbatim native int {v1}"
|
||||||
|
| _, _ -> failwith $"refusing to do binary operation on {v1} and {v2}"
|
||||||
|
|
||||||
|
let state =
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack' result currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
|
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
|
||||||
| Conv_I ->
|
| Conv_I ->
|
||||||
let popped, state = IlMachineState.popEvalStack currentThread state
|
let popped, state = IlMachineState.popEvalStack currentThread state
|
||||||
let converted = EvalStackValue.toNativeInt popped
|
let converted = EvalStackValue.toNativeInt popped
|
||||||
@@ -661,7 +740,20 @@ module NullaryIlOp =
|
|||||||
let state = state |> IlMachineState.advanceProgramCounter currentThread
|
let state = state |> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
|
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
|
||||||
| Conv_U1 -> failwith "TODO: Conv_U1 unimplemented"
|
| Conv_U1 ->
|
||||||
|
let popped, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let converted = EvalStackValue.convToUInt8 popped
|
||||||
|
|
||||||
|
let state =
|
||||||
|
match converted with
|
||||||
|
| None -> failwith "TODO: Conv_U8 conversion failure unimplemented"
|
||||||
|
| Some conv ->
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack' (EvalStackValue.Int32 conv) currentThread
|
||||||
|
|
||||||
|
let state = state |> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
|
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
|
||||||
| Conv_U2 -> failwith "TODO: Conv_U2 unimplemented"
|
| Conv_U2 -> failwith "TODO: Conv_U2 unimplemented"
|
||||||
| Conv_U4 -> failwith "TODO: Conv_U4 unimplemented"
|
| Conv_U4 -> failwith "TODO: Conv_U4 unimplemented"
|
||||||
| Conv_U8 ->
|
| Conv_U8 ->
|
||||||
@@ -684,6 +776,7 @@ module NullaryIlOp =
|
|||||||
let popped =
|
let popped =
|
||||||
match popped with
|
match popped with
|
||||||
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> failwith "TODO: throw NRE"
|
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> failwith "TODO: throw NRE"
|
||||||
|
| EvalStackValue.ObjectRef addr
|
||||||
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) -> addr
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) -> addr
|
||||||
| _ -> failwith $"can't get len of {popped}"
|
| _ -> failwith $"can't get len of {popped}"
|
||||||
|
|
||||||
@@ -868,7 +961,16 @@ module NullaryIlOp =
|
|||||||
| Ldind_u8 -> failwith "TODO: Ldind_u8 unimplemented"
|
| Ldind_u8 -> failwith "TODO: Ldind_u8 unimplemented"
|
||||||
| Ldind_r4 -> executeLdind LdindTargetType.LdindR4 currentThread state
|
| Ldind_r4 -> executeLdind LdindTargetType.LdindR4 currentThread state
|
||||||
| Ldind_r8 -> executeLdind LdindTargetType.LdindR8 currentThread state
|
| Ldind_r8 -> executeLdind LdindTargetType.LdindR8 currentThread state
|
||||||
| Rem -> failwith "TODO: Rem unimplemented"
|
| Rem ->
|
||||||
|
let val2, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let val1, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let result = BinaryArithmetic.execute ArithmeticOperation.rem state val1 val2
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack' result currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
|
|> ExecutionResult.Stepped
|
||||||
| Rem_un -> failwith "TODO: Rem_un unimplemented"
|
| Rem_un -> failwith "TODO: Rem_un unimplemented"
|
||||||
| Volatile -> failwith "TODO: Volatile unimplemented"
|
| Volatile -> failwith "TODO: Volatile unimplemented"
|
||||||
| Tail -> failwith "TODO: Tail unimplemented"
|
| Tail -> failwith "TODO: Tail unimplemented"
|
||||||
@@ -885,26 +987,33 @@ module NullaryIlOp =
|
|||||||
| Conv_ovf_i -> failwith "TODO: Conv_ovf_i unimplemented"
|
| Conv_ovf_i -> failwith "TODO: Conv_ovf_i unimplemented"
|
||||||
| Conv_ovf_u -> failwith "TODO: Conv_ovf_u unimplemented"
|
| Conv_ovf_u -> failwith "TODO: Conv_ovf_u unimplemented"
|
||||||
| Neg -> failwith "TODO: Neg unimplemented"
|
| Neg -> failwith "TODO: Neg unimplemented"
|
||||||
| Not -> failwith "TODO: Not unimplemented"
|
| Not ->
|
||||||
|
let val1, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let result =
|
||||||
|
match val1 with
|
||||||
|
| EvalStackValue.Int32 i -> ~~~i |> EvalStackValue.Int32
|
||||||
|
| EvalStackValue.Int64 i -> ~~~i |> EvalStackValue.Int64
|
||||||
|
| EvalStackValue.ManagedPointer _
|
||||||
|
| EvalStackValue.ObjectRef _ -> failwith "refusing to negate a pointer"
|
||||||
|
| _ -> failwith "TODO"
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack' result currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
|
|> ExecutionResult.Stepped
|
||||||
| Ldind_ref ->
|
| Ldind_ref ->
|
||||||
let addr, state = IlMachineState.popEvalStack currentThread state
|
let addr, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
let referenced =
|
let referenced =
|
||||||
match addr with
|
match addr with
|
||||||
| EvalStackValue.ManagedPointer src ->
|
| EvalStackValue.ManagedPointer src -> IlMachineState.dereferencePointer state src
|
||||||
match src with
|
|
||||||
| ManagedPointerSource.Null -> failwith "TODO: throw NRE"
|
|
||||||
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
|
|
||||||
state.ThreadState.[sourceThread].MethodStates.[methodFrame].LocalVariables
|
|
||||||
.[int<uint16> whichVar]
|
|
||||||
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
|
|
||||||
state.ThreadState.[sourceThread].MethodStates.[methodFrame].Arguments.[int<uint16> whichVar]
|
|
||||||
| ManagedPointerSource.Heap managedHeapAddress -> failwith "todo"
|
|
||||||
| ManagedPointerSource.ArrayIndex _ -> failwith "todo"
|
|
||||||
| a -> failwith $"TODO: {a}"
|
| a -> failwith $"TODO: {a}"
|
||||||
|
|
||||||
let state =
|
let state =
|
||||||
match referenced with
|
match referenced with
|
||||||
|
| CliType.RuntimePointer (CliRuntimePointer.Managed _)
|
||||||
| CliType.ObjectRef _ -> IlMachineState.pushToEvalStack referenced currentThread state
|
| CliType.ObjectRef _ -> IlMachineState.pushToEvalStack referenced currentThread state
|
||||||
| _ -> failwith $"Unexpected non-reference {referenced}"
|
| _ -> failwith $"Unexpected non-reference {referenced}"
|
||||||
|> IlMachineState.advanceProgramCounter currentThread
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
@@ -928,19 +1037,92 @@ module NullaryIlOp =
|
|||||||
arr
|
arr
|
||||||
(EvalStackValue.toCliTypeCoerced (CliType.ObjectRef None) value)
|
(EvalStackValue.toCliTypeCoerced (CliType.ObjectRef None) value)
|
||||||
index
|
index
|
||||||
|
| ManagedPointerSource.Field _ -> failwith "TODO"
|
||||||
|
| ManagedPointerSource.InterpretedAsType (src, ty) -> failwith "TODO"
|
||||||
| addr -> failwith $"TODO: {addr}"
|
| addr -> failwith $"TODO: {addr}"
|
||||||
|
|
||||||
let state = state |> IlMachineState.advanceProgramCounter currentThread
|
let state = state |> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
|
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped
|
||||||
| Ldelem_i -> failwith "TODO: Ldelem_i unimplemented"
|
| Ldelem_i ->
|
||||||
| Ldelem_i1 -> failwith "TODO: Ldelem_i1 unimplemented"
|
let index, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let arr, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let value = getArrayElt index arr currentThread state
|
||||||
|
|
||||||
|
match value with
|
||||||
|
| CliType.Numeric (CliNumericType.NativeInt _) -> ()
|
||||||
|
| _ -> failwith "expected native int in Ldelem.i"
|
||||||
|
|
||||||
|
let state =
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack value currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
|
ExecutionResult.Stepped (state, WhatWeDid.Executed)
|
||||||
|
| Ldelem_i1 ->
|
||||||
|
let index, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let arr, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let value = getArrayElt index arr currentThread state
|
||||||
|
|
||||||
|
failwith "TODO: we got back an int8; turn it into int32"
|
||||||
|
|
||||||
|
let state =
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack value currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
|
ExecutionResult.Stepped (state, WhatWeDid.Executed)
|
||||||
| Ldelem_u1 -> failwith "TODO: Ldelem_u1 unimplemented"
|
| Ldelem_u1 -> failwith "TODO: Ldelem_u1 unimplemented"
|
||||||
| Ldelem_i2 -> failwith "TODO: Ldelem_i2 unimplemented"
|
| Ldelem_i2 ->
|
||||||
|
let index, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let arr, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let value = getArrayElt index arr currentThread state
|
||||||
|
|
||||||
|
failwith "TODO: we got back an int16; turn it into int32"
|
||||||
|
|
||||||
|
let state =
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack value currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
|
ExecutionResult.Stepped (state, WhatWeDid.Executed)
|
||||||
| Ldelem_u2 -> failwith "TODO: Ldelem_u2 unimplemented"
|
| Ldelem_u2 -> failwith "TODO: Ldelem_u2 unimplemented"
|
||||||
| Ldelem_i4 -> failwith "TODO: Ldelem_i4 unimplemented"
|
| Ldelem_i4 ->
|
||||||
|
let index, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let arr, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let value = getArrayElt index arr currentThread state
|
||||||
|
|
||||||
|
match value with
|
||||||
|
| CliType.Numeric (CliNumericType.Int32 _) -> ()
|
||||||
|
| _ -> failwith "expected int32 in Ldelem.i4"
|
||||||
|
|
||||||
|
let state =
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack value currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
|
ExecutionResult.Stepped (state, WhatWeDid.Executed)
|
||||||
| Ldelem_u4 -> failwith "TODO: Ldelem_u4 unimplemented"
|
| Ldelem_u4 -> failwith "TODO: Ldelem_u4 unimplemented"
|
||||||
| Ldelem_i8 -> failwith "TODO: Ldelem_i8 unimplemented"
|
| Ldelem_i8 ->
|
||||||
|
let index, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
let arr, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
|
let value = getArrayElt index arr currentThread state
|
||||||
|
|
||||||
|
match value with
|
||||||
|
| CliType.Numeric (CliNumericType.Int64 _) -> ()
|
||||||
|
| _ -> failwith "expected int64 in Ldelem.i8"
|
||||||
|
|
||||||
|
let state =
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack value currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
|
ExecutionResult.Stepped (state, WhatWeDid.Executed)
|
||||||
| Ldelem_u8 -> failwith "TODO: Ldelem_u8 unimplemented"
|
| Ldelem_u8 -> failwith "TODO: Ldelem_u8 unimplemented"
|
||||||
| Ldelem_r4 -> failwith "TODO: Ldelem_r4 unimplemented"
|
| Ldelem_r4 -> failwith "TODO: Ldelem_r4 unimplemented"
|
||||||
| Ldelem_r8 -> failwith "TODO: Ldelem_r8 unimplemented"
|
| Ldelem_r8 -> failwith "TODO: Ldelem_r8 unimplemented"
|
||||||
@@ -948,7 +1130,19 @@ module NullaryIlOp =
|
|||||||
let index, state = IlMachineState.popEvalStack currentThread state
|
let index, state = IlMachineState.popEvalStack currentThread state
|
||||||
let arr, state = IlMachineState.popEvalStack currentThread state
|
let arr, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
|
||||||
ldElem (CliType.ObjectRef None) index arr currentThread state
|
let value = getArrayElt index arr currentThread state
|
||||||
|
|
||||||
|
match value with
|
||||||
|
| CliType.ObjectRef _
|
||||||
|
| CliType.RuntimePointer _ -> ()
|
||||||
|
| _ -> failwith "expected object reference in Ldelem.ref"
|
||||||
|
|
||||||
|
let state =
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack value currentThread
|
||||||
|
|> IlMachineState.advanceProgramCounter currentThread
|
||||||
|
|
||||||
|
ExecutionResult.Stepped (state, WhatWeDid.Executed)
|
||||||
| Stelem_i ->
|
| Stelem_i ->
|
||||||
let value, state = IlMachineState.popEvalStack currentThread state
|
let value, state = IlMachineState.popEvalStack currentThread state
|
||||||
let index, state = IlMachineState.popEvalStack currentThread state
|
let index, state = IlMachineState.popEvalStack currentThread state
|
||||||
|
@@ -10,18 +10,16 @@ open Microsoft.Extensions.Logging
|
|||||||
module Program =
|
module Program =
|
||||||
/// Returns the pointer to the resulting array on the heap.
|
/// Returns the pointer to the resulting array on the heap.
|
||||||
let allocateArgs
|
let allocateArgs
|
||||||
|
(loggerFactory : ILoggerFactory)
|
||||||
(args : string list)
|
(args : string list)
|
||||||
(corelib : BaseClassTypes<DumpedAssembly>)
|
(corelib : BaseClassTypes<DumpedAssembly>)
|
||||||
(state : IlMachineState)
|
(state : IlMachineState)
|
||||||
: ManagedHeapAddress * IlMachineState
|
: ManagedHeapAddress * IlMachineState
|
||||||
=
|
=
|
||||||
let state, stringType =
|
let state, stringType =
|
||||||
TypeDefn.FromDefinition (
|
DumpedAssembly.typeInfoToTypeDefn' corelib state._LoadedAssemblies corelib.String
|
||||||
ComparableTypeDefinitionHandle.Make corelib.String.TypeDefHandle,
|
|
||||||
corelib.Corelib.Name.FullName,
|
|
||||||
SignatureTypeKind.Class
|
|
||||||
)
|
|
||||||
|> IlMachineState.concretizeType
|
|> IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
corelib
|
corelib
|
||||||
state
|
state
|
||||||
corelib.Corelib.Name
|
corelib.Corelib.Name
|
||||||
@@ -126,7 +124,7 @@ module Program =
|
|||||||
let rec go state =
|
let rec go state =
|
||||||
// Resolve the type reference to find which assembly it's in
|
// Resolve the type reference to find which assembly it's in
|
||||||
match
|
match
|
||||||
Assembly.resolveTypeRef state._LoadedAssemblies currentAssembly typeRef ImmutableArray.Empty
|
Assembly.resolveTypeRef state._LoadedAssemblies currentAssembly ImmutableArray.Empty typeRef
|
||||||
with
|
with
|
||||||
| TypeResolutionResult.FirstLoadAssy assyRef ->
|
| TypeResolutionResult.FirstLoadAssy assyRef ->
|
||||||
// Need to load this assembly first
|
// Need to load this assembly first
|
||||||
@@ -289,7 +287,7 @@ module Program =
|
|||||||
let arrayAllocation, state =
|
let arrayAllocation, state =
|
||||||
match mainMethodFromMetadata.Signature.ParameterTypes |> Seq.toList with
|
match mainMethodFromMetadata.Signature.ParameterTypes |> Seq.toList with
|
||||||
| [ TypeDefn.OneDimensionalArrayLowerBoundZero (TypeDefn.PrimitiveType PrimitiveType.String) ] ->
|
| [ TypeDefn.OneDimensionalArrayLowerBoundZero (TypeDefn.PrimitiveType PrimitiveType.String) ] ->
|
||||||
allocateArgs argv baseClassTypes state
|
allocateArgs loggerFactory argv baseClassTypes state
|
||||||
| _ -> failwith "Main method must take an array of strings; other signatures not yet implemented"
|
| _ -> failwith "Main method must take an array of strings; other signatures not yet implemented"
|
||||||
|
|
||||||
match mainMethodFromMetadata.Signature.ReturnType with
|
match mainMethodFromMetadata.Signature.ReturnType with
|
||||||
@@ -304,6 +302,11 @@ module Program =
|
|||||||
|
|
||||||
logger.LogInformation "Main method class now initialised"
|
logger.LogInformation "Main method class now initialised"
|
||||||
|
|
||||||
|
let state =
|
||||||
|
{ state with
|
||||||
|
ConcreteTypes = Corelib.concretizeAll state._LoadedAssemblies baseClassTypes state.ConcreteTypes
|
||||||
|
}
|
||||||
|
|
||||||
// Now that BCL initialisation has taken place and the user-code classes are constructed,
|
// Now that BCL initialisation has taken place and the user-code classes are constructed,
|
||||||
// overwrite the main thread completely using the already-concretized method.
|
// overwrite the main thread completely using the already-concretized method.
|
||||||
let methodState =
|
let methodState =
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
namespace WoofWare.PawPrint
|
namespace WoofWare.PawPrint
|
||||||
|
|
||||||
|
open System.Collections.Immutable
|
||||||
|
|
||||||
type TypeHandleRegistry =
|
type TypeHandleRegistry =
|
||||||
private
|
private
|
||||||
{
|
{
|
||||||
@@ -17,8 +19,10 @@ module TypeHandleRegistry =
|
|||||||
|
|
||||||
/// Returns an allocated System.RuntimeType as well.
|
/// Returns an allocated System.RuntimeType as well.
|
||||||
let getOrAllocate
|
let getOrAllocate
|
||||||
|
(allConcreteTypes : AllConcreteTypes)
|
||||||
|
(corelib : BaseClassTypes<DumpedAssembly>)
|
||||||
(allocState : 'allocState)
|
(allocState : 'allocState)
|
||||||
(allocate : (string * CliType) list -> 'allocState -> ManagedHeapAddress * 'allocState)
|
(allocate : CliValueType -> 'allocState -> ManagedHeapAddress * 'allocState)
|
||||||
(def : ConcreteTypeHandle)
|
(def : ConcreteTypeHandle)
|
||||||
(reg : TypeHandleRegistry)
|
(reg : TypeHandleRegistry)
|
||||||
: ManagedHeapAddress * TypeHandleRegistry * 'allocState
|
: ManagedHeapAddress * TypeHandleRegistry * 'allocState
|
||||||
@@ -29,17 +33,64 @@ module TypeHandleRegistry =
|
|||||||
|
|
||||||
// Here follows the class System.RuntimeType, which is an internal class type with a constructor
|
// Here follows the class System.RuntimeType, which is an internal class type with a constructor
|
||||||
// whose only purpose is to throw.
|
// whose only purpose is to throw.
|
||||||
|
// https://github.com/dotnet/runtime/blob/2b21c73fa2c32fa0195e4a411a435dda185efd08/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs#L14
|
||||||
|
// and https://github.com/dotnet/runtime/blob/f0168ee80ba9aca18a7e7140b2bb436defda623c/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs#L44
|
||||||
let fields =
|
let fields =
|
||||||
[
|
[
|
||||||
// for the GC, I think?
|
// for the GC, I think?
|
||||||
"m_keepalive", CliType.ObjectRef None
|
{
|
||||||
// TODO: this is actually a System.IntPtr https://github.com/dotnet/runtime/blob/ec11903827fc28847d775ba17e0cd1ff56cfbc2e/src/coreclr/nativeaot/Runtime.Base/src/System/Primitives.cs#L339
|
Name = "m_keepalive"
|
||||||
"m_cache", CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.Verbatim 0L))
|
Contents = CliType.ObjectRef None
|
||||||
"m_handle", CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.TypeHandlePtr def))
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
allConcreteTypes
|
||||||
|
(corelib.Object.Assembly,
|
||||||
|
corelib.Object.Namespace,
|
||||||
|
corelib.Object.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Name = "m_cache"
|
||||||
|
Contents = CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.Verbatim 0L))
|
||||||
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
allConcreteTypes
|
||||||
|
(corelib.IntPtr.Assembly,
|
||||||
|
corelib.IntPtr.Namespace,
|
||||||
|
corelib.IntPtr.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Name = "m_handle"
|
||||||
|
Contents = CliType.Numeric (CliNumericType.NativeInt (NativeIntSource.TypeHandlePtr def))
|
||||||
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
allConcreteTypes
|
||||||
|
(corelib.IntPtr.Assembly,
|
||||||
|
corelib.IntPtr.Namespace,
|
||||||
|
corelib.IntPtr.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
|
}
|
||||||
// This is the const -1, apparently?!
|
// This is the const -1, apparently?!
|
||||||
// https://github.com/dotnet/runtime/blob/f0168ee80ba9aca18a7e7140b2bb436defda623c/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs#L2496
|
// https://github.com/dotnet/runtime/blob/f0168ee80ba9aca18a7e7140b2bb436defda623c/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs#L2496
|
||||||
"GenericParameterCountAny", CliType.Numeric (CliNumericType.Int32 -1)
|
{
|
||||||
|
Name = "GenericParameterCountAny"
|
||||||
|
Contents = CliType.Numeric (CliNumericType.Int32 -1)
|
||||||
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
allConcreteTypes
|
||||||
|
(corelib.Int32.Assembly, corelib.Int32.Namespace, corelib.Int32.Name, ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|> CliValueType.OfFields Layout.Default
|
||||||
|
|
||||||
let alloc, state = allocate fields allocState
|
let alloc, state = allocate fields allocState
|
||||||
|
|
||||||
|
@@ -107,8 +107,8 @@ module internal UnaryConstIlOp =
|
|||||||
| EvalStackValue.NativeInt i -> not (NativeIntSource.isZero i)
|
| EvalStackValue.NativeInt i -> not (NativeIntSource.isZero i)
|
||||||
| EvalStackValue.Float f -> failwith "TODO: Brfalse_s float semantics undocumented"
|
| EvalStackValue.Float f -> failwith "TODO: Brfalse_s float semantics undocumented"
|
||||||
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> false
|
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> false
|
||||||
|
| EvalStackValue.ObjectRef _
|
||||||
| EvalStackValue.ManagedPointer _ -> true
|
| EvalStackValue.ManagedPointer _ -> true
|
||||||
| EvalStackValue.ObjectRef _ -> failwith "TODO: Brfalse_s ObjectRef comparison unimplemented"
|
|
||||||
| EvalStackValue.UserDefinedValueType _ ->
|
| EvalStackValue.UserDefinedValueType _ ->
|
||||||
failwith "TODO: Brfalse_s UserDefinedValueType comparison unimplemented"
|
failwith "TODO: Brfalse_s UserDefinedValueType comparison unimplemented"
|
||||||
|
|
||||||
@@ -129,8 +129,8 @@ module internal UnaryConstIlOp =
|
|||||||
| EvalStackValue.NativeInt i -> not (NativeIntSource.isZero i)
|
| EvalStackValue.NativeInt i -> not (NativeIntSource.isZero i)
|
||||||
| EvalStackValue.Float f -> failwith "TODO: Brtrue_s float semantics undocumented"
|
| EvalStackValue.Float f -> failwith "TODO: Brtrue_s float semantics undocumented"
|
||||||
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> false
|
| EvalStackValue.ManagedPointer ManagedPointerSource.Null -> false
|
||||||
|
| EvalStackValue.ObjectRef _
|
||||||
| EvalStackValue.ManagedPointer _ -> true
|
| EvalStackValue.ManagedPointer _ -> true
|
||||||
| EvalStackValue.ObjectRef _ -> failwith "TODO: Brtrue_s ObjectRef comparison unimplemented"
|
|
||||||
| EvalStackValue.UserDefinedValueType _ ->
|
| EvalStackValue.UserDefinedValueType _ ->
|
||||||
failwith "TODO: Brtrue_s UserDefinedValueType comparison unimplemented"
|
failwith "TODO: Brtrue_s UserDefinedValueType comparison unimplemented"
|
||||||
|
|
||||||
@@ -422,6 +422,7 @@ module internal UnaryConstIlOp =
|
|||||||
| EvalStackValue.Float v1, _ -> failwith $"invalid comparison, {v1} with {value2}"
|
| EvalStackValue.Float v1, _ -> failwith $"invalid comparison, {v1} with {value2}"
|
||||||
| EvalStackValue.NativeInt v1, EvalStackValue.NativeInt v2 -> v1 <> v2
|
| EvalStackValue.NativeInt v1, EvalStackValue.NativeInt v2 -> v1 <> v2
|
||||||
| EvalStackValue.ManagedPointer ptr1, EvalStackValue.ManagedPointer ptr2 -> ptr1 <> ptr2
|
| EvalStackValue.ManagedPointer ptr1, EvalStackValue.ManagedPointer ptr2 -> ptr1 <> ptr2
|
||||||
|
| EvalStackValue.ObjectRef ptr1, EvalStackValue.ObjectRef ptr2 -> ptr1 <> ptr2
|
||||||
| _, _ -> failwith $"TODO {value1} {value2} (see table III.4)"
|
| _, _ -> failwith $"TODO {value1} {value2} (see table III.4)"
|
||||||
|
|
||||||
state
|
state
|
||||||
|
@@ -28,6 +28,24 @@ module internal UnaryMetadataIlOp =
|
|||||||
| MetadataToken.MethodSpecification h ->
|
| MetadataToken.MethodSpecification h ->
|
||||||
let spec = activeAssy.MethodSpecs.[h]
|
let spec = activeAssy.MethodSpecs.[h]
|
||||||
|
|
||||||
|
let state, methodGenerics =
|
||||||
|
((state, []), spec.Signature)
|
||||||
|
||> Seq.fold (fun (state, acc) typeDefn ->
|
||||||
|
let state, concreteType =
|
||||||
|
IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
|
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
|
match spec.Method with
|
||||||
| MetadataToken.MethodDef token ->
|
| MetadataToken.MethodDef token ->
|
||||||
let method =
|
let method =
|
||||||
@@ -44,6 +62,7 @@ module internal UnaryMetadataIlOp =
|
|||||||
baseClassTypes
|
baseClassTypes
|
||||||
thread
|
thread
|
||||||
(state.ActiveAssembly thread)
|
(state.ActiveAssembly thread)
|
||||||
|
methodGenerics
|
||||||
ref
|
ref
|
||||||
state
|
state
|
||||||
|
|
||||||
@@ -58,6 +77,7 @@ module internal UnaryMetadataIlOp =
|
|||||||
baseClassTypes
|
baseClassTypes
|
||||||
thread
|
thread
|
||||||
(state.ActiveAssembly thread)
|
(state.ActiveAssembly thread)
|
||||||
|
currentMethod.DeclaringType.Generics
|
||||||
h
|
h
|
||||||
state
|
state
|
||||||
|
|
||||||
@@ -114,6 +134,24 @@ module internal UnaryMetadataIlOp =
|
|||||||
| MetadataToken.MethodSpecification h ->
|
| MetadataToken.MethodSpecification h ->
|
||||||
let spec = activeAssy.MethodSpecs.[h]
|
let spec = activeAssy.MethodSpecs.[h]
|
||||||
|
|
||||||
|
let state, methodGenerics =
|
||||||
|
((state, []), spec.Signature)
|
||||||
|
||> Seq.fold (fun (state, acc) typeDefn ->
|
||||||
|
let state, concreteType =
|
||||||
|
IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
|
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
|
match spec.Method with
|
||||||
| MetadataToken.MethodDef token ->
|
| MetadataToken.MethodDef token ->
|
||||||
let method =
|
let method =
|
||||||
@@ -128,6 +166,7 @@ module internal UnaryMetadataIlOp =
|
|||||||
baseClassTypes
|
baseClassTypes
|
||||||
thread
|
thread
|
||||||
(state.ActiveAssembly thread)
|
(state.ActiveAssembly thread)
|
||||||
|
methodGenerics
|
||||||
ref
|
ref
|
||||||
state
|
state
|
||||||
|
|
||||||
@@ -142,6 +181,7 @@ module internal UnaryMetadataIlOp =
|
|||||||
baseClassTypes
|
baseClassTypes
|
||||||
thread
|
thread
|
||||||
(state.ActiveAssembly thread)
|
(state.ActiveAssembly thread)
|
||||||
|
ImmutableArray.Empty
|
||||||
h
|
h
|
||||||
state
|
state
|
||||||
|
|
||||||
@@ -203,6 +243,7 @@ module internal UnaryMetadataIlOp =
|
|||||||
baseClassTypes
|
baseClassTypes
|
||||||
thread
|
thread
|
||||||
(state.ActiveAssembly thread)
|
(state.ActiveAssembly thread)
|
||||||
|
ImmutableArray.Empty
|
||||||
mr
|
mr
|
||||||
state
|
state
|
||||||
|
|
||||||
@@ -255,7 +296,7 @@ module internal UnaryMetadataIlOp =
|
|||||||
((state, []), instanceFields)
|
((state, []), instanceFields)
|
||||||
||> List.fold (fun (state, zeros) field ->
|
||> List.fold (fun (state, zeros) field ->
|
||||||
// TODO: generics
|
// TODO: generics
|
||||||
let state, zero =
|
let state, zero, concreteType =
|
||||||
IlMachineState.cliTypeZeroOf
|
IlMachineState.cliTypeZeroOf
|
||||||
loggerFactory
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
@@ -265,10 +306,18 @@ module internal UnaryMetadataIlOp =
|
|||||||
ImmutableArray.Empty
|
ImmutableArray.Empty
|
||||||
state
|
state
|
||||||
|
|
||||||
state, (field.Name, zero) :: zeros
|
let field =
|
||||||
|
{
|
||||||
|
Name = field.Name
|
||||||
|
Contents = zero
|
||||||
|
Offset = field.Offset
|
||||||
|
Type = concreteType
|
||||||
|
}
|
||||||
|
|
||||||
|
state, field :: zeros
|
||||||
)
|
)
|
||||||
|
|
||||||
let fields = List.rev fieldZeros
|
let fields = List.rev fieldZeros |> CliValueType.OfFields ctorType.Layout
|
||||||
|
|
||||||
// Note: this is a bit unorthodox for value types, which *aren't* heap-allocated.
|
// 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
|
// We'll perform their construction on the heap, though, to keep the interface
|
||||||
@@ -347,7 +396,7 @@ module internal UnaryMetadataIlOp =
|
|||||||
ref
|
ref
|
||||||
| x -> failwith $"TODO: Newarr element type resolution unimplemented for {x}"
|
| x -> failwith $"TODO: Newarr element type resolution unimplemented for {x}"
|
||||||
|
|
||||||
let state, zeroOfType =
|
let state, zeroOfType, concreteTypeHandle =
|
||||||
IlMachineState.cliTypeZeroOf
|
IlMachineState.cliTypeZeroOf
|
||||||
loggerFactory
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
@@ -367,7 +416,55 @@ module internal UnaryMetadataIlOp =
|
|||||||
|> IlMachineState.advanceProgramCounter thread
|
|> IlMachineState.advanceProgramCounter thread
|
||||||
|
|
||||||
state, WhatWeDid.Executed
|
state, WhatWeDid.Executed
|
||||||
| Box -> failwith "TODO: Box unimplemented"
|
| Box ->
|
||||||
|
let state, ty, assy =
|
||||||
|
match metadataToken with
|
||||||
|
| MetadataToken.TypeDefinition h ->
|
||||||
|
let state, ty = IlMachineState.lookupTypeDefn baseClassTypes state activeAssy h
|
||||||
|
state, ty, activeAssy
|
||||||
|
| MetadataToken.TypeReference ref ->
|
||||||
|
IlMachineState.lookupTypeRef
|
||||||
|
loggerFactory
|
||||||
|
baseClassTypes
|
||||||
|
state
|
||||||
|
activeAssy
|
||||||
|
currentMethod.DeclaringType.Generics
|
||||||
|
ref
|
||||||
|
| MetadataToken.TypeSpecification spec -> state, activeAssy.TypeSpecs.[spec].Signature, activeAssy
|
||||||
|
| _ -> failwith $"unexpected token {metadataToken} in Box"
|
||||||
|
|
||||||
|
let state, typeHandle =
|
||||||
|
IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
|
baseClassTypes
|
||||||
|
state
|
||||||
|
assy.Name
|
||||||
|
currentMethod.DeclaringType.Generics
|
||||||
|
currentMethod.Generics
|
||||||
|
ty
|
||||||
|
|
||||||
|
let toBox, state = state |> IlMachineState.popEvalStack thread
|
||||||
|
|
||||||
|
let targetType =
|
||||||
|
AllConcreteTypes.lookup typeHandle state.ConcreteTypes |> Option.get
|
||||||
|
|
||||||
|
let defn =
|
||||||
|
state._LoadedAssemblies.[targetType.Assembly.FullName].TypeDefs.[targetType.Definition.Get]
|
||||||
|
|
||||||
|
let baseType =
|
||||||
|
DumpedAssembly.resolveBaseType baseClassTypes state._LoadedAssemblies targetType.Assembly defn.BaseType
|
||||||
|
|
||||||
|
let toPush =
|
||||||
|
match baseType with
|
||||||
|
| ResolvedBaseType.Enum
|
||||||
|
| ResolvedBaseType.ValueType -> failwith "TODO: implement Box"
|
||||||
|
| ResolvedBaseType.Object
|
||||||
|
| ResolvedBaseType.Delegate -> toBox
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack' toPush thread
|
||||||
|
|> IlMachineState.advanceProgramCounter thread
|
||||||
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
| Ldelema ->
|
| Ldelema ->
|
||||||
let index, state = IlMachineState.popEvalStack thread state
|
let index, state = IlMachineState.popEvalStack thread state
|
||||||
let arr, state = IlMachineState.popEvalStack thread state
|
let arr, state = IlMachineState.popEvalStack thread state
|
||||||
@@ -409,22 +506,10 @@ module internal UnaryMetadataIlOp =
|
|||||||
let activeAssy = state.ActiveAssembly thread
|
let activeAssy = state.ActiveAssembly thread
|
||||||
let ty = activeAssy.TypeDefs.[td]
|
let ty = activeAssy.TypeDefs.[td]
|
||||||
|
|
||||||
let baseTy =
|
let result =
|
||||||
DumpedAssembly.resolveBaseType
|
DumpedAssembly.typeInfoToTypeDefn' baseClassTypes state._LoadedAssemblies ty
|
||||||
baseClassTypes
|
|
||||||
state._LoadedAssemblies
|
|
||||||
activeAssy.Name
|
|
||||||
ty.BaseType
|
|
||||||
|
|
||||||
let sigType =
|
state, result
|
||||||
match baseTy with
|
|
||||||
| ResolvedBaseType.Enum
|
|
||||||
| ResolvedBaseType.ValueType -> SignatureTypeKind.ValueType
|
|
||||||
| ResolvedBaseType.Object -> SignatureTypeKind.Class
|
|
||||||
| ResolvedBaseType.Delegate -> SignatureTypeKind.Class
|
|
||||||
|
|
||||||
state,
|
|
||||||
TypeDefn.FromDefinition (ComparableTypeDefinitionHandle.Make td, activeAssy.Name.FullName, sigType)
|
|
||||||
| MetadataToken.TypeSpecification handle ->
|
| MetadataToken.TypeSpecification handle ->
|
||||||
state, state.ActiveAssembly(thread).TypeSpecs.[handle].Signature
|
state, state.ActiveAssembly(thread).TypeSpecs.[handle].Signature
|
||||||
| MetadataToken.TypeReference handle ->
|
| MetadataToken.TypeReference handle ->
|
||||||
@@ -436,26 +521,12 @@ module internal UnaryMetadataIlOp =
|
|||||||
ImmutableArray.Empty
|
ImmutableArray.Empty
|
||||||
state
|
state
|
||||||
|
|
||||||
let baseTy =
|
state, DumpedAssembly.typeInfoToTypeDefn baseClassTypes state._LoadedAssemblies resol
|
||||||
DumpedAssembly.resolveBaseType baseClassTypes state._LoadedAssemblies assy.Name resol.BaseType
|
|
||||||
|
|
||||||
let sigType =
|
|
||||||
match baseTy with
|
|
||||||
| ResolvedBaseType.Enum
|
|
||||||
| ResolvedBaseType.ValueType -> SignatureTypeKind.ValueType
|
|
||||||
| ResolvedBaseType.Object -> SignatureTypeKind.Class
|
|
||||||
| ResolvedBaseType.Delegate -> SignatureTypeKind.Class
|
|
||||||
|
|
||||||
state,
|
|
||||||
TypeDefn.FromDefinition (
|
|
||||||
ComparableTypeDefinitionHandle.Make resol.TypeDefHandle,
|
|
||||||
assy.Name.FullName,
|
|
||||||
sigType
|
|
||||||
)
|
|
||||||
| m -> failwith $"unexpected metadata token {m} in IsInst"
|
| m -> failwith $"unexpected metadata token {m} in IsInst"
|
||||||
|
|
||||||
let state, targetConcreteType =
|
let state, targetConcreteType =
|
||||||
IlMachineState.concretizeType
|
IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
activeAssy.Name
|
activeAssy.Name
|
||||||
@@ -469,13 +540,15 @@ module internal UnaryMetadataIlOp =
|
|||||||
// null IsInstance check always succeeds and results in a null reference
|
// null IsInstance check always succeeds and results in a null reference
|
||||||
EvalStackValue.ManagedPointer ManagedPointerSource.Null
|
EvalStackValue.ManagedPointer ManagedPointerSource.Null
|
||||||
| EvalStackValue.ManagedPointer (ManagedPointerSource.LocalVariable _) -> failwith "TODO"
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.LocalVariable _) -> failwith "TODO"
|
||||||
|
| EvalStackValue.ObjectRef addr
|
||||||
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) ->
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) ->
|
||||||
match state.ManagedHeap.NonArrayObjects.TryGetValue addr with
|
match state.ManagedHeap.NonArrayObjects.TryGetValue addr with
|
||||||
| true, v ->
|
| true, v ->
|
||||||
if v.ConcreteType = targetConcreteType then
|
if v.ConcreteType = targetConcreteType then
|
||||||
actualObj
|
actualObj
|
||||||
else
|
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, _ ->
|
| false, _ ->
|
||||||
|
|
||||||
match state.ManagedHeap.Arrays.TryGetValue addr with
|
match state.ManagedHeap.Arrays.TryGetValue addr with
|
||||||
@@ -500,7 +573,14 @@ module internal UnaryMetadataIlOp =
|
|||||||
state, field
|
state, field
|
||||||
| MetadataToken.MemberReference mr ->
|
| MetadataToken.MemberReference mr ->
|
||||||
let state, _, field, _ =
|
let state, _, field, _ =
|
||||||
IlMachineState.resolveMember loggerFactory baseClassTypes thread activeAssy mr state
|
IlMachineState.resolveMember
|
||||||
|
loggerFactory
|
||||||
|
baseClassTypes
|
||||||
|
thread
|
||||||
|
activeAssy
|
||||||
|
ImmutableArray.Empty
|
||||||
|
mr
|
||||||
|
state
|
||||||
|
|
||||||
match field with
|
match field with
|
||||||
| Choice1Of2 _method -> failwith "member reference was unexpectedly a method"
|
| Choice1Of2 _method -> failwith "member reference was unexpectedly a method"
|
||||||
@@ -511,17 +591,18 @@ module internal UnaryMetadataIlOp =
|
|||||||
logger.LogInformation (
|
logger.LogInformation (
|
||||||
"Storing in object field {FieldAssembly}.{FieldDeclaringType}.{FieldName} (type {FieldType})",
|
"Storing in object field {FieldAssembly}.{FieldDeclaringType}.{FieldName} (type {FieldType})",
|
||||||
field.DeclaringType.Assembly.Name,
|
field.DeclaringType.Assembly.Name,
|
||||||
field.Name,
|
field.DeclaringType.Name,
|
||||||
field.Name,
|
field.Name,
|
||||||
field.Signature
|
field.Signature
|
||||||
)
|
)
|
||||||
|
|
||||||
let valueToStore, state = IlMachineState.popEvalStack thread state
|
let valueToStore, state = IlMachineState.popEvalStack thread state
|
||||||
|
let currentObj, state = IlMachineState.popEvalStack thread state
|
||||||
|
|
||||||
let state, declaringTypeHandle, typeGenerics =
|
let state, declaringTypeHandle, typeGenerics =
|
||||||
IlMachineState.concretizeFieldForExecution loggerFactory baseClassTypes thread field state
|
IlMachineState.concretizeFieldForExecution loggerFactory baseClassTypes thread field state
|
||||||
|
|
||||||
let state, zero =
|
let state, zero, concreteTypeHandle =
|
||||||
IlMachineState.cliTypeZeroOf
|
IlMachineState.cliTypeZeroOf
|
||||||
loggerFactory
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
@@ -533,8 +614,6 @@ module internal UnaryMetadataIlOp =
|
|||||||
|
|
||||||
let valueToStore = EvalStackValue.toCliTypeCoerced zero valueToStore
|
let valueToStore = EvalStackValue.toCliTypeCoerced zero valueToStore
|
||||||
|
|
||||||
let currentObj, state = IlMachineState.popEvalStack thread state
|
|
||||||
|
|
||||||
if field.Attributes.HasFlag FieldAttributes.Static then
|
if field.Attributes.HasFlag FieldAttributes.Static then
|
||||||
let state =
|
let state =
|
||||||
IlMachineState.setStatic declaringTypeHandle field.Name valueToStore state
|
IlMachineState.setStatic declaringTypeHandle field.Name valueToStore state
|
||||||
@@ -548,33 +627,41 @@ module internal UnaryMetadataIlOp =
|
|||||||
| EvalStackValue.Int64 _ -> failwith "unexpectedly setting field on an int64"
|
| EvalStackValue.Int64 _ -> failwith "unexpectedly setting field on an int64"
|
||||||
| EvalStackValue.NativeInt _ -> failwith "unexpectedly setting field on a nativeint"
|
| EvalStackValue.NativeInt _ -> failwith "unexpectedly setting field on a nativeint"
|
||||||
| EvalStackValue.Float _ -> failwith "unexpectedly setting field on a float"
|
| EvalStackValue.Float _ -> failwith "unexpectedly setting field on a float"
|
||||||
| EvalStackValue.ManagedPointer source ->
|
| EvalStackValue.ObjectRef addr
|
||||||
match source with
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) ->
|
||||||
| ManagedPointerSource.Null -> failwith "TODO: raise NullReferenceException"
|
match state.ManagedHeap.NonArrayObjects.TryGetValue addr with
|
||||||
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
|
| false, _ -> failwith $"todo: array {addr}"
|
||||||
state
|
| true, v ->
|
||||||
|> IlMachineState.setLocalVariable sourceThread methodFrame whichVar valueToStore
|
let v = AllocatedNonArrayObject.SetField field.Name valueToStore v
|
||||||
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) -> failwith "todo"
|
|
||||||
| ManagedPointerSource.ArrayIndex (arr, index) ->
|
|
||||||
state |> IlMachineState.setArrayValue arr valueToStore index
|
|
||||||
| ManagedPointerSource.Heap addr ->
|
|
||||||
match state.ManagedHeap.NonArrayObjects.TryGetValue addr with
|
|
||||||
| false, _ -> failwith $"todo: array {addr}"
|
|
||||||
| true, v ->
|
|
||||||
let v =
|
|
||||||
{ v with
|
|
||||||
Fields = v.Fields |> Map.add field.Name valueToStore
|
|
||||||
}
|
|
||||||
|
|
||||||
let heap =
|
let heap =
|
||||||
{ state.ManagedHeap with
|
{ state.ManagedHeap with
|
||||||
NonArrayObjects = state.ManagedHeap.NonArrayObjects |> Map.add addr v
|
NonArrayObjects = state.ManagedHeap.NonArrayObjects |> Map.add addr v
|
||||||
}
|
|
||||||
|
|
||||||
{ state with
|
|
||||||
ManagedHeap = heap
|
|
||||||
}
|
}
|
||||||
| EvalStackValue.ObjectRef managedHeapAddress -> failwith "todo"
|
|
||||||
|
{ state with
|
||||||
|
ManagedHeap = heap
|
||||||
|
}
|
||||||
|
| EvalStackValue.ManagedPointer ManagedPointerSource.Null ->
|
||||||
|
failwith "TODO: raise NullReferenceException"
|
||||||
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar)) ->
|
||||||
|
let newValue =
|
||||||
|
IlMachineState.getLocalVariable sourceThread methodFrame whichVar state
|
||||||
|
|> CliType.withFieldSet field.Name valueToStore
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.setLocalVariable sourceThread methodFrame whichVar newValue
|
||||||
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar)) ->
|
||||||
|
failwith "todo"
|
||||||
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.ArrayIndex (arr, index)) ->
|
||||||
|
let newValue =
|
||||||
|
IlMachineState.getArrayValue arr index state
|
||||||
|
|> CliType.withFieldSet field.Name valueToStore
|
||||||
|
|
||||||
|
state |> IlMachineState.setArrayValue arr newValue index
|
||||||
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Field (managedPointerSource, fieldName)) ->
|
||||||
|
failwith "todo"
|
||||||
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.InterpretedAsType (src, ty)) -> failwith "todo"
|
||||||
| EvalStackValue.UserDefinedValueType _ -> failwith "todo"
|
| EvalStackValue.UserDefinedValueType _ -> failwith "todo"
|
||||||
|
|
||||||
state
|
state
|
||||||
@@ -600,6 +687,7 @@ module internal UnaryMetadataIlOp =
|
|||||||
baseClassTypes
|
baseClassTypes
|
||||||
thread
|
thread
|
||||||
(state.ActiveAssembly thread)
|
(state.ActiveAssembly thread)
|
||||||
|
ImmutableArray.Empty
|
||||||
mr
|
mr
|
||||||
state
|
state
|
||||||
|
|
||||||
@@ -630,7 +718,7 @@ module internal UnaryMetadataIlOp =
|
|||||||
|
|
||||||
let popped, state = IlMachineState.popEvalStack thread state
|
let popped, state = IlMachineState.popEvalStack thread state
|
||||||
|
|
||||||
let state, zero =
|
let state, zero, concreteTypeHandle =
|
||||||
IlMachineState.cliTypeZeroOf
|
IlMachineState.cliTypeZeroOf
|
||||||
loggerFactory
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
@@ -659,7 +747,14 @@ module internal UnaryMetadataIlOp =
|
|||||||
state, field
|
state, field
|
||||||
| MetadataToken.MemberReference mr ->
|
| MetadataToken.MemberReference mr ->
|
||||||
let state, assyName, field, _ =
|
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
|
match field with
|
||||||
| Choice1Of2 _method -> failwith "member reference was unexpectedly a method"
|
| Choice1Of2 _method -> failwith "member reference was unexpectedly a method"
|
||||||
@@ -690,7 +785,7 @@ module internal UnaryMetadataIlOp =
|
|||||||
match IlMachineState.getStatic declaringTypeHandle field.Name state with
|
match IlMachineState.getStatic declaringTypeHandle field.Name state with
|
||||||
| Some v -> state, v
|
| Some v -> state, v
|
||||||
| None ->
|
| None ->
|
||||||
let state, zero =
|
let state, zero, concreteTypeHandle =
|
||||||
IlMachineState.cliTypeZeroOf
|
IlMachineState.cliTypeZeroOf
|
||||||
loggerFactory
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
@@ -713,39 +808,96 @@ module internal UnaryMetadataIlOp =
|
|||||||
| EvalStackValue.Int64 int64 -> failwith "todo: int64"
|
| EvalStackValue.Int64 int64 -> failwith "todo: int64"
|
||||||
| EvalStackValue.NativeInt nativeIntSource -> failwith $"todo: nativeint {nativeIntSource}"
|
| EvalStackValue.NativeInt nativeIntSource -> failwith $"todo: nativeint {nativeIntSource}"
|
||||||
| EvalStackValue.Float f -> failwith "todo: float"
|
| EvalStackValue.Float f -> failwith "todo: float"
|
||||||
| EvalStackValue.ManagedPointer managedPointerSource ->
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar)) ->
|
||||||
match managedPointerSource with
|
let currentValue =
|
||||||
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
|
state.ThreadState.[sourceThread].MethodStates.[methodFrame].LocalVariables
|
||||||
let currentValue =
|
.[int<uint16> whichVar]
|
||||||
state.ThreadState.[sourceThread].MethodStates.[methodFrame].LocalVariables
|
|> CliType.getField field.Name
|
||||||
.[int<uint16> whichVar]
|
|
||||||
|
|
||||||
IlMachineState.pushToEvalStack currentValue thread state
|
IlMachineState.pushToEvalStack currentValue thread state
|
||||||
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar)) ->
|
||||||
let currentValue =
|
let currentValue =
|
||||||
state.ThreadState.[sourceThread].MethodStates.[methodFrame].Arguments.[int<uint16> whichVar]
|
state.ThreadState.[sourceThread].MethodStates.[methodFrame].Arguments.[int<uint16> whichVar]
|
||||||
|
|> CliType.getField field.Name
|
||||||
|
|
||||||
IlMachineState.pushToEvalStack currentValue thread state
|
IlMachineState.pushToEvalStack currentValue thread state
|
||||||
| ManagedPointerSource.Heap managedHeapAddress ->
|
| EvalStackValue.ObjectRef managedHeapAddress
|
||||||
match state.ManagedHeap.NonArrayObjects.TryGetValue managedHeapAddress with
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap managedHeapAddress) ->
|
||||||
| false, _ -> failwith $"todo: array {managedHeapAddress}"
|
match state.ManagedHeap.NonArrayObjects.TryGetValue managedHeapAddress with
|
||||||
| true, v -> IlMachineState.pushToEvalStack v.Fields.[field.Name] thread state
|
| false, _ -> failwith $"todo: array {managedHeapAddress}"
|
||||||
| ManagedPointerSource.ArrayIndex (arr, index) ->
|
| true, v ->
|
||||||
let currentValue = state |> IlMachineState.getArrayValue arr index
|
IlMachineState.pushToEvalStack
|
||||||
IlMachineState.pushToEvalStack currentValue thread state
|
(AllocatedNonArrayObject.DereferenceField field.Name v)
|
||||||
| ManagedPointerSource.Null -> failwith "TODO: raise NullReferenceException"
|
thread
|
||||||
| EvalStackValue.ObjectRef managedHeapAddress -> failwith $"todo: {managedHeapAddress}"
|
state
|
||||||
| EvalStackValue.UserDefinedValueType fields ->
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.ArrayIndex (arr, index)) ->
|
||||||
let result =
|
let currentValue =
|
||||||
fields |> List.pick (fun (k, v) -> if k = field.Name then Some v else None)
|
state |> IlMachineState.getArrayValue arr index |> CliType.getField field.Name
|
||||||
|
|
||||||
IlMachineState.pushToEvalStack' result thread state
|
IlMachineState.pushToEvalStack currentValue thread state
|
||||||
|
| EvalStackValue.ManagedPointer ManagedPointerSource.Null ->
|
||||||
|
failwith "TODO: raise NullReferenceException"
|
||||||
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Field (src, fieldName)) ->
|
||||||
|
let currentValue =
|
||||||
|
IlMachineState.getFieldValue src fieldName state |> CliType.getField field.Name
|
||||||
|
|
||||||
|
IlMachineState.pushToEvalStack currentValue thread state
|
||||||
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.InterpretedAsType (src, ty)) -> failwith "TODO"
|
||||||
|
| EvalStackValue.UserDefinedValueType vt ->
|
||||||
|
let result = vt |> CliValueType.DereferenceField field.Name
|
||||||
|
|
||||||
|
IlMachineState.pushToEvalStack result thread state
|
||||||
|
|
||||||
state
|
state
|
||||||
|> IlMachineState.advanceProgramCounter thread
|
|> IlMachineState.advanceProgramCounter thread
|
||||||
|> Tuple.withRight WhatWeDid.Executed
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
|
|
||||||
| Ldflda -> failwith "TODO: Ldflda unimplemented"
|
| Ldflda ->
|
||||||
|
let ptr, state = IlMachineState.popEvalStack thread state
|
||||||
|
|
||||||
|
let ptr =
|
||||||
|
match ptr with
|
||||||
|
| Int32 _
|
||||||
|
| Int64 _
|
||||||
|
| Float _ -> failwith "expected pointer type"
|
||||||
|
| NativeInt nativeIntSource -> failwith "todo"
|
||||||
|
| ManagedPointer src -> src
|
||||||
|
| ObjectRef addr -> ManagedPointerSource.Heap addr
|
||||||
|
| UserDefinedValueType evalStackValueUserType -> failwith "todo"
|
||||||
|
|
||||||
|
let state, field =
|
||||||
|
match metadataToken with
|
||||||
|
| MetadataToken.FieldDefinition f ->
|
||||||
|
let field =
|
||||||
|
activeAssy.Fields.[f]
|
||||||
|
|> FieldInfo.mapTypeGenerics (fun _ -> failwith "no generics allowed on FieldDefinition")
|
||||||
|
|
||||||
|
state, field
|
||||||
|
| MetadataToken.MemberReference mr ->
|
||||||
|
let state, assyName, field, _ =
|
||||||
|
// TODO: generics
|
||||||
|
IlMachineState.resolveMember
|
||||||
|
loggerFactory
|
||||||
|
baseClassTypes
|
||||||
|
thread
|
||||||
|
activeAssy
|
||||||
|
ImmutableArray.Empty
|
||||||
|
mr
|
||||||
|
state
|
||||||
|
|
||||||
|
match field with
|
||||||
|
| Choice1Of2 _method -> failwith "member reference was unexpectedly a method"
|
||||||
|
| Choice2Of2 field -> state, field
|
||||||
|
| t -> failwith $"Unexpectedly asked to load from a non-field: {t}"
|
||||||
|
|
||||||
|
let result =
|
||||||
|
ManagedPointerSource.Field (ptr, field.Name) |> EvalStackValue.ManagedPointer
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack' result thread
|
||||||
|
|> IlMachineState.advanceProgramCounter thread
|
||||||
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
|
|
||||||
| Ldsfld ->
|
| Ldsfld ->
|
||||||
let state, field =
|
let state, field =
|
||||||
match metadataToken with
|
match metadataToken with
|
||||||
@@ -760,7 +912,14 @@ module internal UnaryMetadataIlOp =
|
|||||||
state, field
|
state, field
|
||||||
| MetadataToken.MemberReference mr ->
|
| MetadataToken.MemberReference mr ->
|
||||||
let state, _, field, _ =
|
let state, _, field, _ =
|
||||||
IlMachineState.resolveMember loggerFactory baseClassTypes thread activeAssy mr state
|
IlMachineState.resolveMember
|
||||||
|
loggerFactory
|
||||||
|
baseClassTypes
|
||||||
|
thread
|
||||||
|
activeAssy
|
||||||
|
ImmutableArray.Empty
|
||||||
|
mr
|
||||||
|
state
|
||||||
|
|
||||||
match field with
|
match field with
|
||||||
| Choice1Of2 _method -> failwith "member reference was unexpectedly a method"
|
| Choice1Of2 _method -> failwith "member reference was unexpectedly a method"
|
||||||
@@ -791,7 +950,7 @@ module internal UnaryMetadataIlOp =
|
|||||||
let fieldValue, state =
|
let fieldValue, state =
|
||||||
match IlMachineState.getStatic declaringTypeHandle field.Name state with
|
match IlMachineState.getStatic declaringTypeHandle field.Name state with
|
||||||
| None ->
|
| None ->
|
||||||
let state, newVal =
|
let state, newVal, concreteTypeHandle =
|
||||||
IlMachineState.cliTypeZeroOf
|
IlMachineState.cliTypeZeroOf
|
||||||
loggerFactory
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
@@ -867,14 +1026,9 @@ module internal UnaryMetadataIlOp =
|
|||||||
| _ -> failwith $"expected heap allocation for array, got {arr}"
|
| _ -> failwith $"expected heap allocation for array, got {arr}"
|
||||||
|
|
||||||
let elementType =
|
let elementType =
|
||||||
TypeInfo.toTypeDefn
|
DumpedAssembly.typeInfoToTypeDefn baseClassTypes state._LoadedAssemblies elementType
|
||||||
baseClassTypes
|
|
||||||
_.Name
|
|
||||||
(fun x y -> x.TypeDefs.[y])
|
|
||||||
(fun x y -> x.TypeRefs.[y] |> failwithf "%+A")
|
|
||||||
elementType
|
|
||||||
|
|
||||||
let state, zeroOfType =
|
let state, zeroOfType, concreteTypeHandle =
|
||||||
IlMachineState.cliTypeZeroOf
|
IlMachineState.cliTypeZeroOf
|
||||||
loggerFactory
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
@@ -941,7 +1095,72 @@ module internal UnaryMetadataIlOp =
|
|||||||
IlMachineState.pushToEvalStack toPush thread state
|
IlMachineState.pushToEvalStack toPush thread state
|
||||||
|> IlMachineState.advanceProgramCounter thread
|
|> IlMachineState.advanceProgramCounter thread
|
||||||
|> Tuple.withRight WhatWeDid.Executed
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
| Initobj -> failwith "TODO: Initobj unimplemented"
|
|
||||||
|
| Initobj ->
|
||||||
|
let popped, state = IlMachineState.popEvalStack thread state
|
||||||
|
let declaringTypeGenerics = currentMethod.DeclaringType.Generics
|
||||||
|
|
||||||
|
let state, assy, targetType =
|
||||||
|
match metadataToken with
|
||||||
|
| MetadataToken.TypeDefinition defn ->
|
||||||
|
state,
|
||||||
|
activeAssy,
|
||||||
|
activeAssy.TypeDefs.[defn]
|
||||||
|
|> TypeInfo.mapGeneric (fun (p, _) -> TypeDefn.GenericTypeParameter p.SequenceNumber)
|
||||||
|
| MetadataToken.TypeSpecification spec ->
|
||||||
|
let state, assy, ty =
|
||||||
|
IlMachineState.resolveTypeFromSpecConcrete
|
||||||
|
loggerFactory
|
||||||
|
baseClassTypes
|
||||||
|
spec
|
||||||
|
activeAssy
|
||||||
|
declaringTypeGenerics
|
||||||
|
currentMethod.Generics
|
||||||
|
state
|
||||||
|
|
||||||
|
state, assy, ty
|
||||||
|
| x -> failwith $"TODO: Ldelem element type resolution unimplemented for {x}"
|
||||||
|
|
||||||
|
let targetType =
|
||||||
|
targetType
|
||||||
|
|> DumpedAssembly.typeInfoToTypeDefn baseClassTypes state._LoadedAssemblies
|
||||||
|
|
||||||
|
let state, zeroOfType, concreteTypeHandle =
|
||||||
|
IlMachineState.cliTypeZeroOf
|
||||||
|
loggerFactory
|
||||||
|
baseClassTypes
|
||||||
|
assy
|
||||||
|
targetType
|
||||||
|
declaringTypeGenerics
|
||||||
|
ImmutableArray.Empty
|
||||||
|
state
|
||||||
|
|
||||||
|
let state =
|
||||||
|
match popped with
|
||||||
|
| EvalStackValue.Int32 _
|
||||||
|
| EvalStackValue.Int64 _
|
||||||
|
| EvalStackValue.NativeInt _
|
||||||
|
| EvalStackValue.Float _ -> failwith "unexpectedly not an address"
|
||||||
|
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr)
|
||||||
|
| EvalStackValue.ObjectRef addr -> failwith "todo"
|
||||||
|
| EvalStackValue.ManagedPointer src ->
|
||||||
|
match src with
|
||||||
|
| ManagedPointerSource.LocalVariable (thread, frame, var) ->
|
||||||
|
state |> IlMachineState.setLocalVariable thread frame var zeroOfType
|
||||||
|
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) -> failwith "todo"
|
||||||
|
| ManagedPointerSource.ArrayIndex (arr, index) ->
|
||||||
|
state |> IlMachineState.setArrayValue arr zeroOfType index
|
||||||
|
| ManagedPointerSource.Field (managedPointerSource, fieldName) ->
|
||||||
|
state |> IlMachineState.setFieldValue managedPointerSource zeroOfType fieldName
|
||||||
|
| ManagedPointerSource.Null -> failwith "runtime error: unexpectedly Initobj'ing null"
|
||||||
|
| ManagedPointerSource.InterpretedAsType (src, ty) -> failwith "TODO"
|
||||||
|
| ManagedPointerSource.Heap _ -> failwith "logic error"
|
||||||
|
| EvalStackValue.UserDefinedValueType evalStackValueUserType -> failwith "todo"
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.advanceProgramCounter thread
|
||||||
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
|
|
||||||
| Ldsflda ->
|
| Ldsflda ->
|
||||||
|
|
||||||
// TODO: check whether we should throw FieldAccessException
|
// TODO: check whether we should throw FieldAccessException
|
||||||
@@ -963,30 +1182,31 @@ module internal UnaryMetadataIlOp =
|
|||||||
| FirstLoadThis state -> state, WhatWeDid.SuspendedForClassInit
|
| FirstLoadThis state -> state, WhatWeDid.SuspendedForClassInit
|
||||||
| NothingToDo state ->
|
| NothingToDo state ->
|
||||||
|
|
||||||
if TypeDefn.isManaged field.Signature then
|
// TODO: if field type is unmanaged, push an unmanaged pointer
|
||||||
match IlMachineState.getStatic declaringTypeHandle field.Name state with
|
// TODO: Note that field may be a static global with an assigned relative virtual address
|
||||||
| Some v ->
|
// (the offset of the field from the base address at which its containing PE file is loaded into memory)
|
||||||
IlMachineState.pushToEvalStack v thread state
|
// where the memory is unmanaged.
|
||||||
|> IlMachineState.advanceProgramCounter thread
|
match IlMachineState.getStatic declaringTypeHandle field.Name state with
|
||||||
|> Tuple.withRight WhatWeDid.Executed
|
| Some v ->
|
||||||
| None ->
|
IlMachineState.pushToEvalStack v thread state
|
||||||
// Field is not yet initialised
|
|> IlMachineState.advanceProgramCounter thread
|
||||||
let state, zero =
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
IlMachineState.cliTypeZeroOf
|
| None ->
|
||||||
loggerFactory
|
// Field is not yet initialised
|
||||||
baseClassTypes
|
let state, zero, concreteTypeHandle =
|
||||||
activeAssy
|
IlMachineState.cliTypeZeroOf
|
||||||
field.Signature
|
loggerFactory
|
||||||
typeGenerics
|
baseClassTypes
|
||||||
ImmutableArray.Empty // field can't have its own generics
|
activeAssy
|
||||||
state
|
field.Signature
|
||||||
|
typeGenerics
|
||||||
|
ImmutableArray.Empty // field can't have its own generics
|
||||||
|
state
|
||||||
|
|
||||||
IlMachineState.setStatic declaringTypeHandle field.Name zero state
|
IlMachineState.setStatic declaringTypeHandle field.Name zero state
|
||||||
|> IlMachineState.pushToEvalStack (CliType.ObjectRef None) thread
|
|> IlMachineState.pushToEvalStack (CliType.ObjectRef None) thread
|
||||||
|> IlMachineState.advanceProgramCounter thread
|
|> IlMachineState.advanceProgramCounter thread
|
||||||
|> Tuple.withRight WhatWeDid.Executed
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
else
|
|
||||||
failwith "TODO: Ldsflda - push unmanaged pointer"
|
|
||||||
|
|
||||||
| Ldftn ->
|
| Ldftn ->
|
||||||
let method, methodGenerics =
|
let method, methodGenerics =
|
||||||
@@ -1038,6 +1258,50 @@ module internal UnaryMetadataIlOp =
|
|||||||
| Stobj -> failwith "TODO: Stobj unimplemented"
|
| Stobj -> failwith "TODO: Stobj unimplemented"
|
||||||
| Constrained -> failwith "TODO: Constrained unimplemented"
|
| Constrained -> failwith "TODO: Constrained unimplemented"
|
||||||
| Ldtoken ->
|
| 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
|
||||||
|
loggerFactory
|
||||||
|
baseClassTypes
|
||||||
|
state
|
||||||
|
activeAssy.Name
|
||||||
|
typeGenerics
|
||||||
|
methodGenerics
|
||||||
|
typeDefn
|
||||||
|
|
||||||
|
let alloc, state =
|
||||||
|
IlMachineState.getOrAllocateType loggerFactory 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
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
state.ConcreteTypes
|
||||||
|
(baseClassTypes.Object.Assembly,
|
||||||
|
baseClassTypes.Object.Namespace,
|
||||||
|
baseClassTypes.Object.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
|
}
|
||||||
|
|> List.singleton
|
||||||
|
|> CliValueType.OfFields Layout.Default
|
||||||
|
|
||||||
|
IlMachineState.pushToEvalStack (CliType.ValueType vt) thread state
|
||||||
|
|
||||||
let state =
|
let state =
|
||||||
match metadataToken with
|
match metadataToken with
|
||||||
| MetadataToken.FieldDefinition h ->
|
| MetadataToken.FieldDefinition h ->
|
||||||
@@ -1070,28 +1334,12 @@ module internal UnaryMetadataIlOp =
|
|||||||
methodGenerics
|
methodGenerics
|
||||||
state
|
state
|
||||||
|
|
||||||
let stk =
|
|
||||||
match
|
|
||||||
DumpedAssembly.resolveBaseType
|
|
||||||
baseClassTypes
|
|
||||||
state._LoadedAssemblies
|
|
||||||
assy.Name
|
|
||||||
typeDefn.BaseType
|
|
||||||
with
|
|
||||||
| ResolvedBaseType.ValueType
|
|
||||||
| ResolvedBaseType.Enum -> SignatureTypeKind.ValueType
|
|
||||||
| ResolvedBaseType.Delegate
|
|
||||||
| ResolvedBaseType.Object -> SignatureTypeKind.Class
|
|
||||||
|
|
||||||
let typeDefn =
|
let typeDefn =
|
||||||
TypeDefn.FromDefinition (
|
DumpedAssembly.typeInfoToTypeDefn baseClassTypes state._LoadedAssemblies typeDefn
|
||||||
ComparableTypeDefinitionHandle.Make typeDefn.TypeDefHandle,
|
|
||||||
assy.Name.FullName,
|
|
||||||
stk
|
|
||||||
)
|
|
||||||
|
|
||||||
let state, handle =
|
let state, handle =
|
||||||
IlMachineState.concretizeType
|
IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
assy.Name
|
assy.Name
|
||||||
@@ -1099,12 +1347,25 @@ module internal UnaryMetadataIlOp =
|
|||||||
methodGenerics
|
methodGenerics
|
||||||
typeDefn
|
typeDefn
|
||||||
|
|
||||||
let alloc, state = IlMachineState.getOrAllocateType baseClassTypes handle state
|
let alloc, state =
|
||||||
|
IlMachineState.getOrAllocateType loggerFactory baseClassTypes handle state
|
||||||
|
|
||||||
let vt =
|
let vt =
|
||||||
{
|
{
|
||||||
Fields = [ "m_type", CliType.ObjectRef (Some alloc) ]
|
Name = "m_type"
|
||||||
|
Contents = CliType.ObjectRef (Some alloc)
|
||||||
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
state.ConcreteTypes
|
||||||
|
(baseClassTypes.Object.Assembly,
|
||||||
|
baseClassTypes.Object.Namespace,
|
||||||
|
baseClassTypes.Object.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
}
|
}
|
||||||
|
|> List.singleton
|
||||||
|
|> CliValueType.OfFields Layout.Default
|
||||||
|
|
||||||
IlMachineState.pushToEvalStack (CliType.ValueType vt) thread state
|
IlMachineState.pushToEvalStack (CliType.ValueType vt) thread state
|
||||||
| MetadataToken.TypeReference h ->
|
| MetadataToken.TypeReference h ->
|
||||||
@@ -1123,6 +1384,7 @@ module internal UnaryMetadataIlOp =
|
|||||||
|
|
||||||
let state, handle =
|
let state, handle =
|
||||||
IlMachineState.concretizeType
|
IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
assy.Name
|
assy.Name
|
||||||
@@ -1130,52 +1392,102 @@ module internal UnaryMetadataIlOp =
|
|||||||
methodGenerics
|
methodGenerics
|
||||||
typeDefn
|
typeDefn
|
||||||
|
|
||||||
let alloc, state = IlMachineState.getOrAllocateType baseClassTypes handle state
|
let alloc, state =
|
||||||
|
IlMachineState.getOrAllocateType loggerFactory baseClassTypes handle state
|
||||||
|
|
||||||
let vt =
|
let vt =
|
||||||
{
|
{
|
||||||
Fields = [ "m_type", CliType.ObjectRef (Some alloc) ]
|
Name = "m_type"
|
||||||
|
Contents = CliType.ObjectRef (Some alloc)
|
||||||
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
state.ConcreteTypes
|
||||||
|
(baseClassTypes.Object.Assembly,
|
||||||
|
baseClassTypes.Object.Namespace,
|
||||||
|
baseClassTypes.Object.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
}
|
}
|
||||||
|
|> List.singleton
|
||||||
|
|> CliValueType.OfFields Layout.Default
|
||||||
|
|
||||||
IlMachineState.pushToEvalStack (CliType.ValueType vt) thread state
|
IlMachineState.pushToEvalStack (CliType.ValueType vt) thread state
|
||||||
| MetadataToken.TypeDefinition h ->
|
| 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 =
|
let state, typeDefn =
|
||||||
IlMachineState.lookupTypeDefn baseClassTypes state activeAssy h
|
IlMachineState.lookupTypeDefn baseClassTypes state activeAssy h
|
||||||
|
|
||||||
let state, handle =
|
handleTypeToken typeDefn state
|
||||||
IlMachineState.concretizeType
|
|
||||||
baseClassTypes
|
|
||||||
state
|
|
||||||
activeAssy.Name
|
|
||||||
typeGenerics
|
|
||||||
methodGenerics
|
|
||||||
typeDefn
|
|
||||||
|
|
||||||
let alloc, state = IlMachineState.getOrAllocateType baseClassTypes handle state
|
|
||||||
|
|
||||||
let vt =
|
|
||||||
{
|
|
||||||
Fields = [ "m_type", CliType.ObjectRef (Some alloc) ]
|
|
||||||
}
|
|
||||||
|
|
||||||
IlMachineState.pushToEvalStack (CliType.ValueType vt) thread state
|
|
||||||
| _ -> failwith $"Unexpected metadata token %O{metadataToken} in LdToken"
|
| _ -> failwith $"Unexpected metadata token %O{metadataToken} in LdToken"
|
||||||
|
|
||||||
state
|
state
|
||||||
|> IlMachineState.advanceProgramCounter thread
|
|> IlMachineState.advanceProgramCounter thread
|
||||||
|> Tuple.withRight WhatWeDid.Executed
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
| Cpobj -> failwith "TODO: Cpobj unimplemented"
|
| Cpobj -> failwith "TODO: Cpobj unimplemented"
|
||||||
| Ldobj -> failwith "TODO: Ldobj unimplemented"
|
| Ldobj ->
|
||||||
|
let state, ty, assy =
|
||||||
|
match metadataToken with
|
||||||
|
| MetadataToken.TypeDefinition h ->
|
||||||
|
let state, ty = IlMachineState.lookupTypeDefn baseClassTypes state activeAssy h
|
||||||
|
state, ty, activeAssy
|
||||||
|
| MetadataToken.TypeReference ref ->
|
||||||
|
IlMachineState.lookupTypeRef
|
||||||
|
loggerFactory
|
||||||
|
baseClassTypes
|
||||||
|
state
|
||||||
|
activeAssy
|
||||||
|
currentMethod.DeclaringType.Generics
|
||||||
|
ref
|
||||||
|
| MetadataToken.TypeSpecification spec -> state, activeAssy.TypeSpecs.[spec].Signature, activeAssy
|
||||||
|
| _ -> failwith $"unexpected token {metadataToken} in Ldobj"
|
||||||
|
|
||||||
|
let state, typeHandle =
|
||||||
|
IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
|
baseClassTypes
|
||||||
|
state
|
||||||
|
assy.Name
|
||||||
|
currentMethod.DeclaringType.Generics
|
||||||
|
currentMethod.Generics
|
||||||
|
ty
|
||||||
|
|
||||||
|
let addr, state = state |> IlMachineState.popEvalStack thread
|
||||||
|
|
||||||
|
let obj =
|
||||||
|
match addr with
|
||||||
|
| EvalStackValue.ObjectRef addr ->
|
||||||
|
IlMachineState.dereferencePointer state (ManagedPointerSource.Heap addr)
|
||||||
|
| EvalStackValue.ManagedPointer ptr -> IlMachineState.dereferencePointer state ptr
|
||||||
|
| EvalStackValue.Float _
|
||||||
|
| EvalStackValue.Int64 _
|
||||||
|
| EvalStackValue.Int32 _ -> failwith "refusing to interpret constant as address"
|
||||||
|
| _ -> failwith "TODO"
|
||||||
|
|
||||||
|
let targetType =
|
||||||
|
AllConcreteTypes.lookup typeHandle state.ConcreteTypes |> Option.get
|
||||||
|
|
||||||
|
let defn =
|
||||||
|
state._LoadedAssemblies.[targetType.Assembly.FullName].TypeDefs.[targetType.Definition.Get]
|
||||||
|
|
||||||
|
let baseType =
|
||||||
|
DumpedAssembly.resolveBaseType baseClassTypes state._LoadedAssemblies targetType.Assembly defn.BaseType
|
||||||
|
|
||||||
|
let toPush =
|
||||||
|
match baseType with
|
||||||
|
| ResolvedBaseType.Enum
|
||||||
|
| ResolvedBaseType.ValueType ->
|
||||||
|
failwith
|
||||||
|
$"TODO: push %O{obj} as type %s{targetType.Assembly.Name}.%s{targetType.Namespace}.%s{targetType.Name}"
|
||||||
|
| ResolvedBaseType.Object
|
||||||
|
| ResolvedBaseType.Delegate ->
|
||||||
|
// III.4.13: reference types are just copied as pointers.
|
||||||
|
// We should have received a pointer, so let's just pass it back.
|
||||||
|
obj
|
||||||
|
|
||||||
|
state
|
||||||
|
|> IlMachineState.pushToEvalStack toPush thread
|
||||||
|
|> IlMachineState.advanceProgramCounter thread
|
||||||
|
|> Tuple.withRight WhatWeDid.Executed
|
||||||
| Sizeof ->
|
| Sizeof ->
|
||||||
let state, ty, assy =
|
let state, ty, assy =
|
||||||
match metadataToken with
|
match metadataToken with
|
||||||
@@ -1190,10 +1502,12 @@ module internal UnaryMetadataIlOp =
|
|||||||
activeAssy
|
activeAssy
|
||||||
currentMethod.DeclaringType.Generics
|
currentMethod.DeclaringType.Generics
|
||||||
ref
|
ref
|
||||||
|
| MetadataToken.TypeSpecification spec -> state, activeAssy.TypeSpecs.[spec].Signature, activeAssy
|
||||||
| _ -> failwith $"unexpected token {metadataToken} in Sizeof"
|
| _ -> failwith $"unexpected token {metadataToken} in Sizeof"
|
||||||
|
|
||||||
let state, typeHandle =
|
let state, typeHandle =
|
||||||
IlMachineState.concretizeType
|
IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
assy.Name
|
assy.Name
|
||||||
|
@@ -2,12 +2,13 @@ namespace WoofWare.PawPrint
|
|||||||
|
|
||||||
open System.Collections.Immutable
|
open System.Collections.Immutable
|
||||||
open System.Reflection
|
open System.Reflection
|
||||||
open System.Reflection.Metadata
|
open Microsoft.Extensions.Logging
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||||
module internal UnaryStringTokenIlOp =
|
module internal UnaryStringTokenIlOp =
|
||||||
let execute
|
let execute
|
||||||
|
(loggerFactory : ILoggerFactory)
|
||||||
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
|
||||||
(op : UnaryStringTokenIlOp)
|
(op : UnaryStringTokenIlOp)
|
||||||
(sh : StringToken)
|
(sh : StringToken)
|
||||||
@@ -27,6 +28,8 @@ module internal UnaryStringTokenIlOp =
|
|||||||
|
|
||||||
let state = state |> IlMachineState.setStringData dataAddr stringToAllocate
|
let state = state |> IlMachineState.setStringData dataAddr stringToAllocate
|
||||||
|
|
||||||
|
// String type is:
|
||||||
|
// https://github.com/dotnet/runtime/blob/f0168ee80ba9aca18a7e7140b2bb436defda623c/src/libraries/System.Private.CoreLib/src/System/String.cs#L26
|
||||||
let stringInstanceFields =
|
let stringInstanceFields =
|
||||||
baseClassTypes.String.Fields
|
baseClassTypes.String.Fields
|
||||||
|> List.choose (fun field ->
|
|> List.choose (fun field ->
|
||||||
@@ -49,17 +52,39 @@ module internal UnaryStringTokenIlOp =
|
|||||||
|
|
||||||
let fields =
|
let fields =
|
||||||
[
|
[
|
||||||
"_firstChar", CliType.ofChar state.ManagedHeap.StringArrayData.[dataAddr]
|
{
|
||||||
"_stringLength", CliType.Numeric (CliNumericType.Int32 stringToAllocate.Length)
|
Name = "_firstChar"
|
||||||
|
Contents = CliType.ofChar state.ManagedHeap.StringArrayData.[dataAddr]
|
||||||
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
state.ConcreteTypes
|
||||||
|
(baseClassTypes.Char.Assembly,
|
||||||
|
baseClassTypes.Char.Namespace,
|
||||||
|
baseClassTypes.Char.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Name = "_stringLength"
|
||||||
|
Contents = CliType.Numeric (CliNumericType.Int32 stringToAllocate.Length)
|
||||||
|
Offset = None
|
||||||
|
Type =
|
||||||
|
AllConcreteTypes.findExistingConcreteType
|
||||||
|
state.ConcreteTypes
|
||||||
|
(baseClassTypes.Int32.Assembly,
|
||||||
|
baseClassTypes.Int32.Namespace,
|
||||||
|
baseClassTypes.Int32.Name,
|
||||||
|
ImmutableArray.Empty)
|
||||||
|
|> Option.get
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|> CliValueType.OfFields Layout.Default
|
||||||
|
|
||||||
let state, stringType =
|
let state, stringType =
|
||||||
TypeDefn.FromDefinition (
|
DumpedAssembly.typeInfoToTypeDefn' baseClassTypes state._LoadedAssemblies baseClassTypes.String
|
||||||
ComparableTypeDefinitionHandle.Make baseClassTypes.String.TypeDefHandle,
|
|
||||||
baseClassTypes.Corelib.Name.FullName,
|
|
||||||
SignatureTypeKind.Class
|
|
||||||
)
|
|
||||||
|> IlMachineState.concretizeType
|
|> IlMachineState.concretizeType
|
||||||
|
loggerFactory
|
||||||
baseClassTypes
|
baseClassTypes
|
||||||
state
|
state
|
||||||
baseClassTypes.Corelib.Name
|
baseClassTypes.Corelib.Name
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Tuple.fs" />
|
<Compile Include="Tuple.fs" />
|
||||||
|
<Compile Include="List.fs" />
|
||||||
<Compile Include="ImmutableArray.fs" />
|
<Compile Include="ImmutableArray.fs" />
|
||||||
<Compile Include="Result.fs" />
|
<Compile Include="Result.fs" />
|
||||||
<Compile Include="Corelib.fs" />
|
<Compile Include="Corelib.fs" />
|
||||||
@@ -19,10 +20,10 @@
|
|||||||
<Compile Include="Exceptions.fs" />
|
<Compile Include="Exceptions.fs" />
|
||||||
<Compile Include="EvalStack.fs" />
|
<Compile Include="EvalStack.fs" />
|
||||||
<Compile Include="EvalStackValueComparisons.fs" />
|
<Compile Include="EvalStackValueComparisons.fs" />
|
||||||
<Compile Include="BinaryArithmetic.fs" />
|
|
||||||
<Compile Include="MethodState.fs" />
|
<Compile Include="MethodState.fs" />
|
||||||
<Compile Include="ThreadState.fs" />
|
<Compile Include="ThreadState.fs" />
|
||||||
<Compile Include="IlMachineState.fs" />
|
<Compile Include="IlMachineState.fs" />
|
||||||
|
<Compile Include="BinaryArithmetic.fs" />
|
||||||
<Compile Include="Intrinsics.fs" />
|
<Compile Include="Intrinsics.fs" />
|
||||||
<Compile Include="IlMachineStateExecution.fs" />
|
<Compile Include="IlMachineStateExecution.fs" />
|
||||||
<Compile Include="NullaryIlOp.fs" />
|
<Compile Include="NullaryIlOp.fs" />
|
||||||
|
6
flake.lock
generated
6
flake.lock
generated
@@ -20,11 +20,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1755736253,
|
"lastModified": 1756381814,
|
||||||
"narHash": "sha256-jlIQRypNhB1PcB1BE+expE4xZeJxzoAGr1iUbHQta8s=",
|
"narHash": "sha256-tzo7YvAsGlzo4WiIHT0ooR59VHu+aKRQdHk7sIyoia4=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "596312aae91421d6923f18cecce934a7d3bfd6b8",
|
"rev": "aca2499b79170038df0dbaec8bf2f689b506ad32",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
Reference in New Issue
Block a user