mirror of
https://github.com/Smaug123/WoofWare.PawPrint
synced 2025-10-08 07:28:40 +00:00
Split WoofWare.PawPrint.Domain into a new subtree (#41)
This commit is contained in:
508
WoofWare.PawPrint.Domain/Assembly.fs
Normal file
508
WoofWare.PawPrint.Domain/Assembly.fs
Normal file
@@ -0,0 +1,508 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System
|
||||
open System.Collections.Generic
|
||||
open System.Collections.Immutable
|
||||
open System.IO
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
open System.Reflection.Metadata.Ecma335
|
||||
open System.Reflection.PortableExecutable
|
||||
open Microsoft.Extensions.Logging
|
||||
open Microsoft.FSharp.Core
|
||||
|
||||
/// <summary>
|
||||
/// Represents a .NET assembly definition.
|
||||
/// This is a strongly-typed representation of AssemblyDefinition from System.Reflection.Metadata.
|
||||
/// </summary>
|
||||
type AssemblyDefinition =
|
||||
{
|
||||
/// <summary>
|
||||
/// The fully specified name of the assembly, including name, version, culture, and public key token.
|
||||
/// </summary>
|
||||
Name : AssemblyName
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module AssemblyDefinition =
|
||||
let make (assy : System.Reflection.Metadata.AssemblyDefinition) : AssemblyDefinition =
|
||||
{
|
||||
Name = assy.GetAssemblyName ()
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a fully parsed .NET assembly with all its metadata components.
|
||||
/// This serves as the main container for accessing assembly information in the PawPrint library.
|
||||
/// </summary>
|
||||
type DumpedAssembly =
|
||||
{
|
||||
OriginalPath : string option
|
||||
|
||||
/// <summary>Logger for recording information about this assembly.</summary>
|
||||
Logger : ILogger
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of all type definitions in this assembly, keyed by their handle.
|
||||
/// </summary>
|
||||
TypeDefs :
|
||||
IReadOnlyDictionary<TypeDefinitionHandle, WoofWare.PawPrint.TypeInfo<WoofWare.PawPrint.GenericParameter>>
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of all type references in this assembly, keyed by their handle.
|
||||
/// </summary>
|
||||
TypeRefs : IReadOnlyDictionary<TypeReferenceHandle, WoofWare.PawPrint.TypeRef>
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of all type specifications in this assembly, keyed by their handle.
|
||||
/// Type specifications represent complex types like generic instantiations.
|
||||
/// </summary>
|
||||
TypeSpecs : IReadOnlyDictionary<TypeSpecificationHandle, WoofWare.PawPrint.TypeSpec>
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of all method definitions in this assembly, keyed by their handle.
|
||||
/// </summary>
|
||||
Methods : IReadOnlyDictionary<MethodDefinitionHandle, WoofWare.PawPrint.MethodInfo<FakeUnit>>
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of all member references in this assembly, keyed by their handle.
|
||||
/// </summary>
|
||||
Members : IReadOnlyDictionary<MemberReferenceHandle, WoofWare.PawPrint.MemberReference<MetadataToken>>
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of all field definitions in this assembly, keyed by their handle.
|
||||
/// </summary>
|
||||
Fields : IReadOnlyDictionary<FieldDefinitionHandle, WoofWare.PawPrint.FieldInfo<FakeUnit>>
|
||||
|
||||
/// <summary>
|
||||
/// The entry point method of the assembly, if one exists.
|
||||
/// </summary>
|
||||
MainMethod : MethodDefinitionHandle option
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of all method specifications in this assembly, keyed by their handle.
|
||||
/// Method specifications typically represent generic method instantiations.
|
||||
/// </summary>
|
||||
MethodSpecs : ImmutableDictionary<MethodSpecificationHandle, MethodSpec>
|
||||
|
||||
/// <summary>
|
||||
/// Function to resolve string tokens to their actual string values.
|
||||
/// </summary>
|
||||
Strings : StringToken -> string
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of all assembly references in this assembly, keyed by their handle.
|
||||
/// </summary>
|
||||
AssemblyReferences : ImmutableDictionary<AssemblyReferenceHandle, WoofWare.PawPrint.AssemblyReference>
|
||||
|
||||
/// <summary>
|
||||
/// Information about this assembly.
|
||||
/// </summary>
|
||||
ThisAssemblyDefinition : AssemblyDefinition
|
||||
|
||||
/// <summary>
|
||||
/// The root namespace of this assembly.
|
||||
/// </summary>
|
||||
RootNamespace : Namespace
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of all non-root namespaces in this assembly, keyed by their name components.
|
||||
/// </summary>
|
||||
NonRootNamespaces : ImmutableDictionary<string list, Namespace>
|
||||
|
||||
/// <summary>
|
||||
/// The PE reader for the underlying assembly file.
|
||||
/// TODO: work out how to render all the strings up front, then drop this.
|
||||
/// </summary>
|
||||
PeReader : PEReader
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of all custom attributes in this assembly, keyed by their handle.
|
||||
/// </summary>
|
||||
Attributes : ImmutableDictionary<CustomAttributeHandle, WoofWare.PawPrint.CustomAttribute>
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of all exported types in this assembly, keyed by their handle.
|
||||
/// </summary>
|
||||
ExportedTypes : ImmutableDictionary<ExportedTypeHandle, WoofWare.PawPrint.ExportedType>
|
||||
|
||||
/// <summary>
|
||||
/// Internal lookup for exported types by namespace and name.
|
||||
/// </summary>
|
||||
_ExportedTypesLookup : ImmutableDictionary<string option * string, WoofWare.PawPrint.ExportedType>
|
||||
|
||||
/// <summary>
|
||||
/// Internal lookup for type references by namespace and name.
|
||||
/// </summary>
|
||||
_TypeRefsLookup : ImmutableDictionary<string * string, WoofWare.PawPrint.TypeRef>
|
||||
|
||||
/// <summary>
|
||||
/// Internal lookup for type definitions by namespace and name.
|
||||
/// </summary>
|
||||
_TypeDefsLookup :
|
||||
ImmutableDictionary<string * string, WoofWare.PawPrint.TypeInfo<WoofWare.PawPrint.GenericParameter>>
|
||||
}
|
||||
|
||||
static member internal BuildExportedTypesLookup
|
||||
(logger : ILogger)
|
||||
(name : AssemblyName)
|
||||
(types : WoofWare.PawPrint.ExportedType seq)
|
||||
: ImmutableDictionary<string option * string, WoofWare.PawPrint.ExportedType>
|
||||
=
|
||||
let result = ImmutableDictionary.CreateBuilder ()
|
||||
let keys = HashSet ()
|
||||
|
||||
for ty in types do
|
||||
let key = ty.Namespace, ty.Name
|
||||
|
||||
if keys.Add key then
|
||||
result.Add (key, ty)
|
||||
else
|
||||
logger.LogDebug (
|
||||
"Duplicate types exported from assembly {ThisAssemblyName}: namespace {DuplicatedTypeNamespace}, type {DuplicatedTypeName}. Ignoring the duplicate.",
|
||||
name,
|
||||
ty.Namespace,
|
||||
ty.Name
|
||||
)
|
||||
|
||||
result.Remove key |> ignore<bool>
|
||||
|
||||
result.ToImmutable ()
|
||||
|
||||
static member internal BuildTypeRefsLookup
|
||||
(logger : ILogger)
|
||||
(name : AssemblyName)
|
||||
(typeRefs : WoofWare.PawPrint.TypeRef seq)
|
||||
=
|
||||
let result = ImmutableDictionary.CreateBuilder ()
|
||||
let keys = HashSet ()
|
||||
|
||||
for ty in typeRefs do
|
||||
let key = (ty.Namespace, ty.Name)
|
||||
|
||||
if keys.Add key then
|
||||
result.Add (key, ty)
|
||||
else
|
||||
// TODO: this is all very dubious, the ResolutionScope is supposed to tell us how to disambiguate these
|
||||
logger.LogDebug (
|
||||
"Duplicate type refs from assembly {ThisAssemblyName}: namespace {DuplicatedTypeNamespace}, type {DuplicatedTypeName}. Ignoring the duplicate.",
|
||||
name,
|
||||
ty.Namespace,
|
||||
ty.Name
|
||||
)
|
||||
|
||||
result.ToImmutable ()
|
||||
|
||||
static member internal BuildTypeDefsLookup
|
||||
(logger : ILogger)
|
||||
(name : AssemblyName)
|
||||
(typeDefs : WoofWare.PawPrint.TypeInfo<WoofWare.PawPrint.GenericParameter> seq)
|
||||
=
|
||||
let result = ImmutableDictionary.CreateBuilder ()
|
||||
let keys = HashSet ()
|
||||
|
||||
for ty in typeDefs do
|
||||
let key = (ty.Namespace, ty.Name)
|
||||
|
||||
if keys.Add key then
|
||||
result.Add (key, ty)
|
||||
else
|
||||
// TODO: this is all very dubious, the ResolutionScope is supposed to tell us how to disambiguate these
|
||||
logger.LogDebug (
|
||||
"Duplicate type defs from assembly {ThisAssemblyName}: namespace {DuplicatedTypeNamespace}, type {DuplicatedTypeName}. Ignoring the duplicate.",
|
||||
name,
|
||||
ty.Namespace,
|
||||
ty.Name
|
||||
)
|
||||
|
||||
result.ToImmutable ()
|
||||
|
||||
member this.Name = this.ThisAssemblyDefinition.Name
|
||||
|
||||
member this.TypeRef (``namespace`` : string) (name : string) : WoofWare.PawPrint.TypeRef option =
|
||||
match this._TypeRefsLookup.TryGetValue ((``namespace``, name)) with
|
||||
| false, _ -> None
|
||||
| true, v -> Some v
|
||||
|
||||
member this.TypeDef
|
||||
(``namespace`` : string)
|
||||
(name : string)
|
||||
: WoofWare.PawPrint.TypeInfo<WoofWare.PawPrint.GenericParameter> option
|
||||
=
|
||||
match this._TypeDefsLookup.TryGetValue ((``namespace``, name)) with
|
||||
| false, _ -> None
|
||||
| true, v -> Some v
|
||||
|
||||
member this.ExportedType (``namespace`` : string option) (name : string) : WoofWare.PawPrint.ExportedType option =
|
||||
match this._ExportedTypesLookup.TryGetValue ((``namespace``, name)) with
|
||||
| false, _ -> None
|
||||
| true, v -> Some v
|
||||
|
||||
interface IDisposable with
|
||||
member this.Dispose () = this.PeReader.Dispose ()
|
||||
|
||||
type TypeResolutionResult =
|
||||
| FirstLoadAssy of WoofWare.PawPrint.AssemblyReference
|
||||
| Resolved of DumpedAssembly * TypeInfo<TypeDefn>
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Assembly =
|
||||
let read (loggerFactory : ILoggerFactory) (originalPath : string option) (dllBytes : Stream) : DumpedAssembly =
|
||||
let peReader = new PEReader (dllBytes)
|
||||
let metadataReader = peReader.GetMetadataReader ()
|
||||
|
||||
let assy = metadataReader.GetAssemblyDefinition () |> AssemblyDefinition.make
|
||||
|
||||
let entryPoint =
|
||||
peReader.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress
|
||||
|> fun x -> if x = 0 then None else Some x
|
||||
|
||||
let entryPointMethod =
|
||||
entryPoint |> Option.map MetadataTokens.MethodDefinitionHandle
|
||||
|
||||
let assemblyRefs =
|
||||
let builder = ImmutableDictionary.CreateBuilder ()
|
||||
|
||||
for ref in metadataReader.AssemblyReferences do
|
||||
builder.Add (ref, AssemblyReference.make (ref, assy.Name) (metadataReader.GetAssemblyReference ref))
|
||||
|
||||
builder.ToImmutable ()
|
||||
|
||||
let typeRefs =
|
||||
let builder = ImmutableDictionary.CreateBuilder ()
|
||||
|
||||
for ty in metadataReader.TypeReferences do
|
||||
builder.Add (ty, TypeRef.make metadataReader ty)
|
||||
|
||||
builder.ToImmutable ()
|
||||
|
||||
let typeDefs =
|
||||
let builder = ImmutableDictionary.CreateBuilder ()
|
||||
|
||||
for ty in metadataReader.TypeDefinitions do
|
||||
builder.Add (ty, TypeInfo.read loggerFactory peReader assy.Name metadataReader ty)
|
||||
|
||||
builder.ToImmutable ()
|
||||
|
||||
// TODO: this probably misses any methods out which aren't associated with a type definition?
|
||||
let methods =
|
||||
typeDefs
|
||||
|> Seq.collect (fun (KeyValue (_, ty)) -> ty.Methods |> List.map (fun mi -> KeyValuePair (mi.Handle, mi)))
|
||||
|> ImmutableDictionary.CreateRange
|
||||
|
||||
let methodSpecs =
|
||||
Seq.init
|
||||
(metadataReader.GetTableRowCount TableIndex.MethodSpec)
|
||||
(fun i ->
|
||||
let i = i + 1
|
||||
let handle = MetadataTokens.MethodSpecificationHandle i
|
||||
KeyValuePair (handle, MethodSpec.make assy.Name (metadataReader.GetMethodSpecification handle))
|
||||
)
|
||||
|> ImmutableDictionary.CreateRange
|
||||
|
||||
let typeSpecs =
|
||||
let result = ImmutableDictionary.CreateBuilder ()
|
||||
|
||||
for i = 1 to metadataReader.GetTableRowCount TableIndex.TypeSpec do
|
||||
let handle = MetadataTokens.TypeSpecificationHandle i
|
||||
result.Add (handle, metadataReader.GetTypeSpecification handle |> TypeSpec.make assy.Name handle)
|
||||
|
||||
result.ToImmutable ()
|
||||
|
||||
let memberReferences =
|
||||
let builder = ImmutableDictionary.CreateBuilder ()
|
||||
|
||||
for c in metadataReader.MemberReferences do
|
||||
builder.Add (
|
||||
c,
|
||||
MemberReference.make<MetadataToken>
|
||||
metadataReader.GetBlobReader
|
||||
metadataReader.GetString
|
||||
MetadataToken.ofEntityHandle
|
||||
assy.Name
|
||||
(metadataReader.GetMemberReference c)
|
||||
)
|
||||
|
||||
builder.ToImmutable ()
|
||||
|
||||
// TODO: render all this up front
|
||||
let strings (token : StringToken) =
|
||||
match token with
|
||||
| StringToken.String s -> metadataReader.GetString s
|
||||
| StringToken.UserString s -> metadataReader.GetUserString s
|
||||
|
||||
let rootNamespace, nonRootNamespaces =
|
||||
metadataReader.GetNamespaceDefinitionRoot ()
|
||||
|> Namespace.make metadataReader.GetString metadataReader.GetNamespaceDefinition
|
||||
|
||||
let fields =
|
||||
let result = ImmutableDictionary.CreateBuilder ()
|
||||
|
||||
for field in metadataReader.FieldDefinitions do
|
||||
let fieldDefn =
|
||||
metadataReader.GetFieldDefinition field
|
||||
|> FieldInfo.make metadataReader assy.Name field
|
||||
|
||||
result.Add (field, fieldDefn)
|
||||
|
||||
result.ToImmutable ()
|
||||
|
||||
let exportedTypes =
|
||||
let result = ImmutableDictionary.CreateBuilder ()
|
||||
|
||||
for ty in metadataReader.ExportedTypes do
|
||||
result.Add (ty, ExportedType.make metadataReader.GetString ty (metadataReader.GetExportedType ty))
|
||||
|
||||
result.ToImmutable ()
|
||||
|
||||
let attrs =
|
||||
let result = ImmutableDictionary.CreateBuilder ()
|
||||
|
||||
for field in metadataReader.CustomAttributes do
|
||||
let fieldDefn =
|
||||
metadataReader.GetCustomAttribute field |> CustomAttribute.make field
|
||||
|
||||
result.Add (field, fieldDefn)
|
||||
|
||||
result.ToImmutable ()
|
||||
|
||||
let logger = loggerFactory.CreateLogger assy.Name.Name
|
||||
|
||||
{
|
||||
Logger = logger
|
||||
OriginalPath = originalPath
|
||||
TypeDefs = typeDefs
|
||||
TypeRefs = typeRefs
|
||||
TypeSpecs = typeSpecs
|
||||
MainMethod = entryPointMethod
|
||||
Methods = methods
|
||||
MethodSpecs = methodSpecs
|
||||
Members = memberReferences
|
||||
Strings = strings
|
||||
Fields = fields
|
||||
AssemblyReferences = assemblyRefs
|
||||
ThisAssemblyDefinition = assy
|
||||
RootNamespace = rootNamespace
|
||||
NonRootNamespaces = nonRootNamespaces
|
||||
PeReader = peReader
|
||||
Attributes = attrs
|
||||
ExportedTypes = exportedTypes
|
||||
_ExportedTypesLookup = DumpedAssembly.BuildExportedTypesLookup logger assy.Name exportedTypes.Values
|
||||
_TypeRefsLookup = DumpedAssembly.BuildTypeRefsLookup logger assy.Name typeRefs.Values
|
||||
_TypeDefsLookup = DumpedAssembly.BuildTypeDefsLookup logger assy.Name typeDefs.Values
|
||||
}
|
||||
|
||||
let print (main : MethodDefinitionHandle) (dumped : DumpedAssembly) : unit =
|
||||
for KeyValue (_, typ) in dumped.TypeDefs do
|
||||
Console.WriteLine $"\nType: %s{typ.Namespace}.%s{typ.Name}"
|
||||
|
||||
for method in typ.Methods do
|
||||
if method.Handle = main then
|
||||
Console.WriteLine "Entry point!"
|
||||
|
||||
Console.WriteLine $"\nMethod: %s{method.Name}"
|
||||
|
||||
match method.Instructions with
|
||||
| None -> Console.WriteLine "<no IL instructions>"
|
||||
| Some instructions ->
|
||||
instructions.Instructions
|
||||
|> List.map (fun (op, index) -> IlOp.Format op index)
|
||||
|> List.iter Console.WriteLine
|
||||
|
||||
let rec resolveTypeRef
|
||||
(assemblies : ImmutableDictionary<string, DumpedAssembly>)
|
||||
(referencedInAssembly : DumpedAssembly)
|
||||
(target : TypeRef)
|
||||
(genericArgs : ImmutableArray<TypeDefn> option)
|
||||
: TypeResolutionResult
|
||||
=
|
||||
match target.ResolutionScope with
|
||||
| TypeRefResolutionScope.Assembly r ->
|
||||
let assemblyRef = referencedInAssembly.AssemblyReferences.[r]
|
||||
let assemblyName = assemblyRef.Name
|
||||
|
||||
match assemblies.TryGetValue assemblyName.FullName with
|
||||
| false, _ -> TypeResolutionResult.FirstLoadAssy assemblyRef
|
||||
| true, assy ->
|
||||
|
||||
let nsPath = target.Namespace.Split '.' |> Array.toList
|
||||
|
||||
let targetNs = assy.NonRootNamespaces.[nsPath]
|
||||
|
||||
let targetType =
|
||||
targetNs.TypeDefinitions
|
||||
|> Seq.choose (fun td ->
|
||||
let ty = assy.TypeDefs.[td]
|
||||
|
||||
if ty.Name = target.Name && ty.Namespace = target.Namespace then
|
||||
Some ty
|
||||
else
|
||||
None
|
||||
)
|
||||
|> Seq.toList
|
||||
|
||||
match targetType with
|
||||
| [ t ] ->
|
||||
let t =
|
||||
t
|
||||
|> TypeInfo.mapGeneric (fun _ param ->
|
||||
match genericArgs with
|
||||
| None -> failwith "got a generic TypeRef but no generic args in context"
|
||||
| Some genericArgs -> genericArgs.[param.SequenceNumber]
|
||||
)
|
||||
|
||||
TypeResolutionResult.Resolved (assy, t)
|
||||
| _ :: _ :: _ -> failwith $"Multiple matching type definitions! {nsPath} {target.Name}"
|
||||
| [] ->
|
||||
match assy.ExportedType (Some target.Namespace) target.Name with
|
||||
| None -> failwith $"Failed to find type {nsPath} {target.Name} in {assy.Name.FullName}!"
|
||||
| Some ty -> resolveTypeFromExport assy assemblies ty genericArgs
|
||||
| k -> failwith $"Unexpected: {k}"
|
||||
|
||||
and resolveTypeFromName
|
||||
(assy : DumpedAssembly)
|
||||
(assemblies : ImmutableDictionary<string, DumpedAssembly>)
|
||||
(ns : string option)
|
||||
(name : string)
|
||||
(genericArgs : ImmutableArray<TypeDefn> option)
|
||||
: TypeResolutionResult
|
||||
=
|
||||
match ns with
|
||||
| None -> failwith "what are the semantics here"
|
||||
| Some ns ->
|
||||
|
||||
match assy.TypeDef ns name with
|
||||
| Some typeDef ->
|
||||
let typeDef =
|
||||
typeDef
|
||||
|> TypeInfo.mapGeneric (fun _ param ->
|
||||
match genericArgs with
|
||||
| None -> failwith<TypeDefn> $"tried to resolve generic type {ns}.{name} but no generics in scope"
|
||||
| Some genericArgs -> genericArgs.[param.SequenceNumber]
|
||||
)
|
||||
|
||||
TypeResolutionResult.Resolved (assy, typeDef)
|
||||
| None ->
|
||||
|
||||
match assy.TypeRef ns name with
|
||||
| Some typeRef -> resolveTypeRef assemblies assy typeRef genericArgs
|
||||
| None ->
|
||||
|
||||
match assy.ExportedType (Some ns) name with
|
||||
| Some export -> resolveTypeFromExport assy assemblies export genericArgs
|
||||
| None -> failwith $"TODO: type resolution unimplemented for {ns} {name}"
|
||||
|
||||
and resolveTypeFromExport
|
||||
(fromAssembly : DumpedAssembly)
|
||||
(assemblies : ImmutableDictionary<string, DumpedAssembly>)
|
||||
(ty : WoofWare.PawPrint.ExportedType)
|
||||
(genericArgs : ImmutableArray<TypeDefn> option)
|
||||
: TypeResolutionResult
|
||||
=
|
||||
match ty.Data with
|
||||
| NonForwarded _ -> failwith "Somehow didn't find type definition but it is exported"
|
||||
| ForwardsTo assy ->
|
||||
let assy = fromAssembly.AssemblyReferences.[assy]
|
||||
|
||||
match assemblies.TryGetValue assy.Name.FullName with
|
||||
| false, _ -> TypeResolutionResult.FirstLoadAssy assy
|
||||
| true, toAssy -> resolveTypeFromName toAssy assemblies ty.Namespace ty.Name genericArgs
|
30
WoofWare.PawPrint.Domain/AssemblyReference.fs
Normal file
30
WoofWare.PawPrint.Domain/AssemblyReference.fs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
|
||||
type AssemblyReference =
|
||||
{
|
||||
/// A handle relative to the specified assembly.
|
||||
Handle : AssemblyReferenceHandle * AssemblyName
|
||||
Culture : StringToken
|
||||
Flags : AssemblyFlags
|
||||
Name : AssemblyName
|
||||
Version : Version
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module AssemblyReference =
|
||||
let make
|
||||
(handle : AssemblyReferenceHandle * AssemblyName)
|
||||
(ref : System.Reflection.Metadata.AssemblyReference)
|
||||
: AssemblyReference
|
||||
=
|
||||
{
|
||||
Handle = handle
|
||||
Culture = StringToken.String ref.Culture
|
||||
Flags = ref.Flags
|
||||
Name = ref.GetAssemblyName ()
|
||||
Version = ref.Version
|
||||
}
|
36
WoofWare.PawPrint.Domain/ComparableSignatureHeader.fs
Normal file
36
WoofWare.PawPrint.Domain/ComparableSignatureHeader.fs
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System
|
||||
open System.Reflection.Metadata
|
||||
|
||||
[<CustomEquality>]
|
||||
[<CustomComparison>]
|
||||
type ComparableSignatureHeader =
|
||||
private
|
||||
{
|
||||
_Inner : SignatureHeader
|
||||
}
|
||||
|
||||
member this.Get = this._Inner
|
||||
|
||||
override this.Equals (other : obj) =
|
||||
match other with
|
||||
| :? ComparableSignatureHeader as other -> this._Inner.RawValue = other._Inner.RawValue
|
||||
| _ -> false
|
||||
|
||||
override this.GetHashCode () = this._Inner.RawValue.GetHashCode ()
|
||||
|
||||
interface IComparable<ComparableSignatureHeader> with
|
||||
member this.CompareTo (other : ComparableSignatureHeader) =
|
||||
this._Inner.RawValue.CompareTo other._Inner.RawValue
|
||||
|
||||
interface IComparable with
|
||||
member this.CompareTo (other : obj) =
|
||||
match other with
|
||||
| :? ComparableSignatureHeader as other -> (this :> IComparable<ComparableSignatureHeader>).CompareTo other
|
||||
| _ -> failwith "invalid comparison"
|
||||
|
||||
static member Make x : ComparableSignatureHeader =
|
||||
{
|
||||
_Inner = x
|
||||
}
|
37
WoofWare.PawPrint.Domain/ComparableTypeDefinitionHandle.fs
Normal file
37
WoofWare.PawPrint.Domain/ComparableTypeDefinitionHandle.fs
Normal file
@@ -0,0 +1,37 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System
|
||||
open System.Reflection.Metadata
|
||||
|
||||
[<CustomEquality>]
|
||||
[<CustomComparison>]
|
||||
type ComparableTypeDefinitionHandle =
|
||||
private
|
||||
{
|
||||
_Inner : TypeDefinitionHandle
|
||||
}
|
||||
|
||||
override this.Equals (other) =
|
||||
match other with
|
||||
| :? ComparableTypeDefinitionHandle as other -> this._Inner.GetHashCode () = other._Inner.GetHashCode ()
|
||||
| _ -> false
|
||||
|
||||
override this.GetHashCode () : int = this._Inner.GetHashCode ()
|
||||
|
||||
interface IComparable<ComparableTypeDefinitionHandle> with
|
||||
member this.CompareTo (other : ComparableTypeDefinitionHandle) : int =
|
||||
this._Inner.GetHashCode().CompareTo (other._Inner.GetHashCode ())
|
||||
|
||||
interface IComparable with
|
||||
member this.CompareTo (other : obj) : int =
|
||||
match other with
|
||||
| :? ComparableTypeDefinitionHandle as other ->
|
||||
(this :> IComparable<ComparableTypeDefinitionHandle>).CompareTo other
|
||||
| _ -> failwith "invalid comparison"
|
||||
|
||||
static member Make (h : TypeDefinitionHandle) =
|
||||
{
|
||||
_Inner = h
|
||||
}
|
||||
|
||||
member this.Get = this._Inner
|
106
WoofWare.PawPrint.Domain/ConcreteType.fs
Normal file
106
WoofWare.PawPrint.Domain/ConcreteType.fs
Normal file
@@ -0,0 +1,106 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
|
||||
type FakeUnit = private | FakeUnit
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module FakeUnit =
|
||||
let ofUnit () = FakeUnit.FakeUnit
|
||||
|
||||
let toUnit (f : FakeUnit) =
|
||||
match f with
|
||||
| FakeUnit.FakeUnit -> ()
|
||||
|
||||
/// A type which has been concretised, runtime-representable, etc.
|
||||
[<CustomEquality>]
|
||||
[<CustomComparison>]
|
||||
type ConcreteType<'typeGeneric when 'typeGeneric : comparison and 'typeGeneric :> IComparable<'typeGeneric>> =
|
||||
private
|
||||
{
|
||||
_AssemblyName : AssemblyName
|
||||
_Definition : ComparableTypeDefinitionHandle
|
||||
_Generics : 'typeGeneric list
|
||||
}
|
||||
|
||||
member this.Assembly : AssemblyName = this._AssemblyName
|
||||
member this.Definition : ComparableTypeDefinitionHandle = this._Definition
|
||||
member this.Generics : 'typeGeneric list = this._Generics
|
||||
|
||||
override this.Equals (other : obj) : bool =
|
||||
match other with
|
||||
| :? ConcreteType<'typeGeneric> as other ->
|
||||
this._Generics = other._Generics
|
||||
&& this._Definition = other._Definition
|
||||
&& this._AssemblyName.FullName = other._AssemblyName.FullName
|
||||
| _ -> false
|
||||
|
||||
override this.GetHashCode () : int =
|
||||
hash (this._AssemblyName.FullName, this._Definition, this._Generics)
|
||||
|
||||
interface IComparable<ConcreteType<'typeGeneric>> with
|
||||
member this.CompareTo (other : ConcreteType<'typeGeneric>) : int =
|
||||
let comp = this._AssemblyName.FullName.CompareTo other._AssemblyName.FullName
|
||||
|
||||
if comp = 0 then
|
||||
let comp =
|
||||
(this._Definition :> IComparable<ComparableTypeDefinitionHandle>).CompareTo other._Definition
|
||||
|
||||
if comp = 0 then
|
||||
let thisGen = (this._Generics : 'typeGeneric list) :> IComparable<'typeGeneric list>
|
||||
thisGen.CompareTo other._Generics
|
||||
else
|
||||
comp
|
||||
else
|
||||
comp
|
||||
|
||||
interface IComparable with
|
||||
member this.CompareTo other =
|
||||
match other with
|
||||
| :? ConcreteType<'typeGeneric> as other ->
|
||||
(this :> IComparable<ConcreteType<'typeGeneric>>).CompareTo other
|
||||
| _ -> failwith "bad comparison"
|
||||
|
||||
type RuntimeConcreteType = ConcreteType<TypeDefn>
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module ConcreteType =
|
||||
let make
|
||||
(assemblyName : AssemblyName)
|
||||
(defn : TypeDefinitionHandle)
|
||||
(generics : TypeDefn list)
|
||||
: RuntimeConcreteType
|
||||
=
|
||||
{
|
||||
_AssemblyName = assemblyName
|
||||
_Definition = ComparableTypeDefinitionHandle.Make defn
|
||||
_Generics = generics
|
||||
}
|
||||
|
||||
let make'
|
||||
(assemblyName : AssemblyName)
|
||||
(defn : TypeDefinitionHandle)
|
||||
(genericParamCount : int)
|
||||
: ConcreteType<FakeUnit>
|
||||
=
|
||||
{
|
||||
_AssemblyName = assemblyName
|
||||
_Definition = ComparableTypeDefinitionHandle.Make defn
|
||||
_Generics = List.replicate genericParamCount FakeUnit.FakeUnit
|
||||
}
|
||||
|
||||
let mapGeneric<'a, 'b
|
||||
when 'a : comparison and 'a :> IComparable<'a> and 'b : equality and 'b : comparison and 'b :> IComparable<'b>>
|
||||
(f : int -> 'a -> 'b)
|
||||
(x : ConcreteType<'a>)
|
||||
: ConcreteType<'b>
|
||||
=
|
||||
let generics = x._Generics |> List.mapi f
|
||||
|
||||
{
|
||||
_AssemblyName = x._AssemblyName
|
||||
_Definition = x._Definition
|
||||
_Generics = generics
|
||||
}
|
31
WoofWare.PawPrint.Domain/CustomAttribute.fs
Normal file
31
WoofWare.PawPrint.Domain/CustomAttribute.fs
Normal file
@@ -0,0 +1,31 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System.Reflection.Metadata
|
||||
|
||||
/// <summary>
|
||||
/// Represents a custom attribute applied to a type, method, field, or other metadata entity.
|
||||
/// This is a strongly-typed representation of CustomAttribute from System.Reflection.Metadata.
|
||||
/// </summary>
|
||||
type CustomAttribute =
|
||||
{
|
||||
/// <summary>
|
||||
/// The metadata token handle that uniquely identifies this custom attribute in the assembly.
|
||||
/// </summary>
|
||||
Handle : CustomAttributeHandle
|
||||
|
||||
/// <summary>
|
||||
/// The constructor method used to create this custom attribute instance.
|
||||
/// This token references the method that constructs the attribute.
|
||||
/// </summary>
|
||||
Constructor : MetadataToken
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module CustomAttribute =
|
||||
let make (handle : CustomAttributeHandle) (attr : System.Reflection.Metadata.CustomAttribute) : CustomAttribute =
|
||||
let ctor = attr.Constructor |> MetadataToken.ofEntityHandle
|
||||
|
||||
{
|
||||
Handle = handle
|
||||
Constructor = ctor
|
||||
}
|
21
WoofWare.PawPrint.Domain/EventDefn.fs
Normal file
21
WoofWare.PawPrint.Domain/EventDefn.fs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
|
||||
type EventDefn =
|
||||
{
|
||||
Name : string
|
||||
Attrs : EventAttributes
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module EventDefn =
|
||||
|
||||
let make (mr : MetadataReader) (event : EventDefinition) : EventDefn =
|
||||
let name = mr.GetString event.Name
|
||||
|
||||
{
|
||||
Name = name
|
||||
Attrs = event.Attributes
|
||||
}
|
94
WoofWare.PawPrint.Domain/ExportedType.fs
Normal file
94
WoofWare.PawPrint.Domain/ExportedType.fs
Normal file
@@ -0,0 +1,94 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
|
||||
/// <summary>
|
||||
/// Represents the implementation details of an exported type.
|
||||
/// This discriminated union indicates whether the type is forwarded to another assembly
|
||||
/// or references another exported type.
|
||||
/// </summary>
|
||||
type ExportedTypeData =
|
||||
/// <summary>
|
||||
/// Indicates the type is forwarded to another assembly.
|
||||
/// Type forwarders are used to redirect type references to implementations in other assemblies.
|
||||
/// </summary>
|
||||
| ForwardsTo of AssemblyReferenceHandle
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the type references another exported type within the assembly.
|
||||
/// This is often used for nested types.
|
||||
/// </summary>
|
||||
| NonForwarded of ExportedTypeHandle
|
||||
|
||||
/// <summary>
|
||||
/// Represents a type exported from an assembly.
|
||||
/// Exported types are types that are defined in one module but made visible
|
||||
/// at the assembly level, or types that are forwarded to another assembly.
|
||||
/// </summary>
|
||||
type ExportedType =
|
||||
{
|
||||
/// <summary>
|
||||
/// The metadata token handle that uniquely identifies this exported type.
|
||||
/// </summary>
|
||||
Handle : ExportedTypeHandle
|
||||
|
||||
/// <summary>
|
||||
/// The name of the exported type.
|
||||
/// </summary>
|
||||
Name : string
|
||||
|
||||
/// <summary>
|
||||
/// The namespace containing the exported type, if any.
|
||||
/// None if the type is not in a namespace.
|
||||
/// </summary>
|
||||
Namespace : string option
|
||||
|
||||
/// <summary>
|
||||
/// The metadata handle for the namespace definition containing this type.
|
||||
/// </summary>
|
||||
NamespaceDefn : NamespaceDefinitionHandle
|
||||
|
||||
/// <summary>
|
||||
/// The type attributes (visibility, inheritance characteristics, etc.) for this exported type.
|
||||
/// </summary>
|
||||
TypeAttrs : TypeAttributes
|
||||
|
||||
/// <summary>
|
||||
/// The implementation details of this exported type, indicating whether it forwards
|
||||
/// to another assembly or references another exported type.
|
||||
/// </summary>
|
||||
Data : ExportedTypeData
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module ExportedType =
|
||||
let make
|
||||
(getString : StringHandle -> string)
|
||||
(handle : ExportedTypeHandle)
|
||||
(ty : System.Reflection.Metadata.ExportedType)
|
||||
: ExportedType
|
||||
=
|
||||
let name = getString ty.Name
|
||||
let ns = getString ty.Namespace
|
||||
let impl = MetadataToken.ofEntityHandle ty.Implementation
|
||||
let nsDef = ty.NamespaceDefinition
|
||||
|
||||
let data =
|
||||
if ty.IsForwarder then
|
||||
match impl with
|
||||
| MetadataToken.AssemblyReference e -> ExportedTypeData.ForwardsTo e
|
||||
| _ -> failwith $"Expected forwarder type to have an assembly reference: {impl}"
|
||||
else
|
||||
match impl with
|
||||
| MetadataToken.ExportedType impl -> ExportedTypeData.NonForwarded impl
|
||||
| _ -> failwith $"Expected ExportedType implementation but got {impl}"
|
||||
|
||||
{
|
||||
Handle = handle
|
||||
Name = name
|
||||
Namespace = if nsDef.IsNil then None else Some ns
|
||||
NamespaceDefn = nsDef
|
||||
TypeAttrs = ty.Attributes
|
||||
Data = data
|
||||
}
|
76
WoofWare.PawPrint.Domain/FieldInfo.fs
Normal file
76
WoofWare.PawPrint.Domain/FieldInfo.fs
Normal file
@@ -0,0 +1,76 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
|
||||
/// <summary>
|
||||
/// Represents detailed information about a field in a .NET assembly.
|
||||
/// This is a strongly-typed representation of FieldDefinition from System.Reflection.Metadata.
|
||||
/// </summary>
|
||||
type FieldInfo<'typeGeneric when 'typeGeneric : comparison and 'typeGeneric :> IComparable<'typeGeneric>> =
|
||||
{
|
||||
/// <summary>
|
||||
/// The metadata token handle that uniquely identifies this field in the assembly.
|
||||
/// </summary>
|
||||
Handle : FieldDefinitionHandle
|
||||
|
||||
/// <summary>The name of the field.</summary>
|
||||
Name : string
|
||||
|
||||
/// <summary>
|
||||
/// The type that declares this field.
|
||||
/// </summary>
|
||||
DeclaringType : ConcreteType<'typeGeneric>
|
||||
|
||||
/// <summary>
|
||||
/// The type of the field.
|
||||
/// </summary>
|
||||
Signature : TypeDefn
|
||||
|
||||
/// <summary>
|
||||
/// The attributes applied to this field, including visibility, static/instance,
|
||||
/// literal, and other characteristics.
|
||||
/// </summary>
|
||||
Attributes : FieldAttributes
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module FieldInfo =
|
||||
let make
|
||||
(mr : MetadataReader)
|
||||
(assembly : AssemblyName)
|
||||
(handle : FieldDefinitionHandle)
|
||||
(def : FieldDefinition)
|
||||
: FieldInfo<FakeUnit>
|
||||
=
|
||||
let name = mr.GetString def.Name
|
||||
let fieldSig = def.DecodeSignature (TypeDefn.typeProvider assembly, ())
|
||||
let declaringType = def.GetDeclaringType ()
|
||||
let typeGenerics = mr.GetTypeDefinition(declaringType).GetGenericParameters().Count
|
||||
let declaringType = ConcreteType.make' assembly declaringType typeGenerics
|
||||
|
||||
{
|
||||
Name = name
|
||||
Signature = fieldSig
|
||||
DeclaringType = declaringType
|
||||
Handle = handle
|
||||
Attributes = def.Attributes
|
||||
}
|
||||
|
||||
let mapTypeGenerics<'a, 'b
|
||||
when 'a :> IComparable<'a> and 'a : comparison and 'b :> IComparable<'b> and 'b : comparison>
|
||||
(f : int -> 'a -> 'b)
|
||||
(input : FieldInfo<'a>)
|
||||
: FieldInfo<'b>
|
||||
=
|
||||
let declaringType = input.DeclaringType |> ConcreteType.mapGeneric f
|
||||
|
||||
{
|
||||
Handle = input.Handle
|
||||
Name = input.Name
|
||||
DeclaringType = declaringType
|
||||
Signature = input.Signature
|
||||
Attributes = input.Attributes
|
||||
|
||||
}
|
652
WoofWare.PawPrint.Domain/IlOp.fs
Normal file
652
WoofWare.PawPrint.Domain/IlOp.fs
Normal file
@@ -0,0 +1,652 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System.Collections.Immutable
|
||||
|
||||
type NullaryIlOp =
|
||||
| Nop
|
||||
/// Load the argument at index 0 onto the eval stack.
|
||||
| LdArg0
|
||||
/// Load the argument at index 1 onto the eval stack.
|
||||
| LdArg1
|
||||
/// Load the argument at index 2 onto the eval stack.
|
||||
| LdArg2
|
||||
/// Load the argument at index 3 onto the eval stack.
|
||||
| LdArg3
|
||||
/// Load the local variable at index 0 onto the eval stack.
|
||||
| Ldloc_0
|
||||
/// Load the local variable at index 1 onto the eval stack.
|
||||
| Ldloc_1
|
||||
/// Load the local variable at index 2 onto the eval stack.
|
||||
| Ldloc_2
|
||||
/// Load the local variable at index 3 onto the eval stack.
|
||||
| Ldloc_3
|
||||
/// Remove the value on top of the eval stack.
|
||||
| Pop
|
||||
/// Push another copy of the value on top of the eval stack onto the top of the eval stack.
|
||||
| Dup
|
||||
| Ret
|
||||
/// Push the int32 value 0 to the eval stack.
|
||||
| LdcI4_0
|
||||
/// Push the int32 value 1 to the eval stack.
|
||||
| LdcI4_1
|
||||
/// Push the int32 value 2 to the eval stack.
|
||||
| LdcI4_2
|
||||
/// Push the int32 value 3 to the eval stack.
|
||||
| LdcI4_3
|
||||
/// Push the int32 value 4 to the eval stack.
|
||||
| LdcI4_4
|
||||
/// Push the int32 value 5 to the eval stack.
|
||||
| LdcI4_5
|
||||
/// Push the int32 value 6 to the eval stack.
|
||||
| LdcI4_6
|
||||
/// Push the int32 value 7 to the eval stack.
|
||||
| LdcI4_7
|
||||
/// Push the int32 value 8 to the eval stack.
|
||||
| LdcI4_8
|
||||
| LdcI4_m1
|
||||
/// Push a null object reference onto the stack.
|
||||
| LdNull
|
||||
/// Pop two values from the stack; push 1 if they're equal, 0 otherwise
|
||||
| Ceq
|
||||
| Cgt
|
||||
| Cgt_un
|
||||
| Clt
|
||||
| Clt_un
|
||||
/// Pop from the eval stack and store the result in local variable 0
|
||||
| Stloc_0
|
||||
/// Pop from the eval stack and store the result in local variable 1
|
||||
| Stloc_1
|
||||
/// Pop from the eval stack and store the result in local variable 2
|
||||
| Stloc_2
|
||||
/// Pop from the eval stack and store the result in local variable 3
|
||||
| Stloc_3
|
||||
| Sub
|
||||
| Sub_ovf
|
||||
| Sub_ovf_un
|
||||
| Add
|
||||
| Add_ovf
|
||||
| Add_ovf_un
|
||||
| Mul
|
||||
| Mul_ovf
|
||||
| Mul_ovf_un
|
||||
| Div
|
||||
| Div_un
|
||||
| Rem
|
||||
| Rem_un
|
||||
| Neg
|
||||
| Not
|
||||
| Shr
|
||||
| Shr_un
|
||||
| Shl
|
||||
| Conv_ovf_i
|
||||
| Conv_ovf_u
|
||||
| And
|
||||
| Or
|
||||
| Xor
|
||||
/// Converts the value on top of the eval stack to nativeint
|
||||
| Conv_I
|
||||
/// Converts the value on top of the eval stack to int8, then pads to int32
|
||||
| Conv_I1
|
||||
/// Converts the value on top of the eval stack to int16, then pads to int32
|
||||
| Conv_I2
|
||||
/// Converts the value on top of the eval stack to int32
|
||||
| Conv_I4
|
||||
/// Converts the value on top of the eval stack to int64
|
||||
| Conv_I8
|
||||
| Conv_R4
|
||||
| Conv_R8
|
||||
/// Converts the value on top of the eval stack to unsigned nativeint, then extends to nativeint
|
||||
| Conv_U
|
||||
/// Converts the value on top of the eval stack to uint8, then extends to int32
|
||||
| Conv_U1
|
||||
/// Converts the value on top of the eval stack to uint16, then extends to int 32
|
||||
| Conv_U2
|
||||
/// Converts the value on top of the eval stack to uint32, then extends to int32
|
||||
| Conv_U4
|
||||
/// Converts the value on top of the eval stack to uint64, then extends to int64
|
||||
| Conv_U8
|
||||
| Conv_ovf_u1
|
||||
| Conv_ovf_u2
|
||||
| Conv_ovf_u4
|
||||
| Conv_ovf_u8
|
||||
| Conv_ovf_i1
|
||||
| Conv_ovf_i2
|
||||
| Conv_ovf_i4
|
||||
| Conv_ovf_i8
|
||||
/// Pushes the number of elements of a zero-based, one-dimensional array onto the evaluation stack.
|
||||
| LdLen
|
||||
| Endfilter
|
||||
| Endfinally
|
||||
| Rethrow
|
||||
| Throw
|
||||
| Localloc
|
||||
| Ldind_ref
|
||||
| Stind_ref
|
||||
| Stind_I
|
||||
| Stind_I1
|
||||
| Stind_I2
|
||||
| Stind_I4
|
||||
| Stind_I8
|
||||
| Stind_R4
|
||||
| Stind_R8
|
||||
| Ldind_i
|
||||
| Ldind_i1
|
||||
| Ldind_i2
|
||||
| Ldind_i4
|
||||
| Ldind_i8
|
||||
| Ldind_u1
|
||||
| Ldind_u2
|
||||
| Ldind_u4
|
||||
| Ldind_u8
|
||||
| Ldind_r4
|
||||
| Ldind_r8
|
||||
| Volatile
|
||||
| Tail
|
||||
| Conv_ovf_i_un
|
||||
| Conv_ovf_u_un
|
||||
| Conv_ovf_i1_un
|
||||
| Conv_ovf_u1_un
|
||||
| Conv_ovf_i2_un
|
||||
| Conv_ovf_u2_un
|
||||
| Conv_ovf_i4_un
|
||||
| Conv_ovf_u4_un
|
||||
| Conv_ovf_i8_un
|
||||
| Conv_ovf_u8_un
|
||||
| Ldelem_i
|
||||
| Ldelem_i1
|
||||
| Ldelem_u1
|
||||
| Ldelem_i2
|
||||
| Ldelem_u2
|
||||
| Ldelem_i4
|
||||
| Ldelem_u4
|
||||
| Ldelem_i8
|
||||
| Ldelem_u8
|
||||
| Ldelem_r4
|
||||
| Ldelem_r8
|
||||
| Ldelem_ref
|
||||
| Stelem_i
|
||||
| Stelem_i1
|
||||
| Stelem_u1
|
||||
| Stelem_i2
|
||||
| Stelem_u2
|
||||
| Stelem_i4
|
||||
| Stelem_u4
|
||||
| Stelem_i8
|
||||
| Stelem_u8
|
||||
| Stelem_r4
|
||||
| Stelem_r8
|
||||
| Stelem_ref
|
||||
| Cpblk
|
||||
| Initblk
|
||||
| Break
|
||||
| Conv_r_un
|
||||
| Arglist
|
||||
| Ckfinite
|
||||
| Readonly
|
||||
| Refanytype
|
||||
|
||||
override this.ToString () =
|
||||
match this with
|
||||
| NullaryIlOp.Nop -> "Nop"
|
||||
| NullaryIlOp.LdArg0 -> "LdArg0"
|
||||
| NullaryIlOp.LdArg1 -> "LdArg1"
|
||||
| NullaryIlOp.LdArg2 -> "LdArg2"
|
||||
| NullaryIlOp.LdArg3 -> "LdArg3"
|
||||
| NullaryIlOp.Ldloc_0 -> "Ldloc_0"
|
||||
| NullaryIlOp.Ldloc_1 -> "Ldloc_1"
|
||||
| NullaryIlOp.Ldloc_2 -> "Ldloc_2"
|
||||
| NullaryIlOp.Ldloc_3 -> "Ldloc_3"
|
||||
| NullaryIlOp.Pop -> "Pop"
|
||||
| NullaryIlOp.Dup -> "Dup"
|
||||
| NullaryIlOp.Ret -> "Ret"
|
||||
| NullaryIlOp.LdcI4_0 -> "LdcI4_0"
|
||||
| NullaryIlOp.LdcI4_1 -> "LdcI4_1"
|
||||
| NullaryIlOp.LdcI4_2 -> "LdcI4_2"
|
||||
| NullaryIlOp.LdcI4_3 -> "LdcI4_3"
|
||||
| NullaryIlOp.LdcI4_4 -> "LdcI4_4"
|
||||
| NullaryIlOp.LdcI4_5 -> "LdcI4_5"
|
||||
| NullaryIlOp.LdcI4_6 -> "LdcI4_6"
|
||||
| NullaryIlOp.LdcI4_7 -> "LdcI4_7"
|
||||
| NullaryIlOp.LdcI4_8 -> "LdcI4_8"
|
||||
| NullaryIlOp.LdcI4_m1 -> "LdcI4_m1"
|
||||
| NullaryIlOp.LdNull -> "LdNull"
|
||||
| NullaryIlOp.Ceq -> "Ceq"
|
||||
| NullaryIlOp.Cgt -> "Cgt"
|
||||
| NullaryIlOp.Cgt_un -> "Cgt_un"
|
||||
| NullaryIlOp.Clt -> "Clt"
|
||||
| NullaryIlOp.Clt_un -> "Clt_un"
|
||||
| NullaryIlOp.Stloc_0 -> "Stloc_0"
|
||||
| NullaryIlOp.Stloc_1 -> "Stloc_1"
|
||||
| NullaryIlOp.Stloc_2 -> "Stloc_2"
|
||||
| NullaryIlOp.Stloc_3 -> "Stloc_3"
|
||||
| NullaryIlOp.Sub -> "Sub"
|
||||
| NullaryIlOp.Sub_ovf -> "Sub_ovf"
|
||||
| NullaryIlOp.Sub_ovf_un -> "Sub_ovf_un"
|
||||
| NullaryIlOp.Add -> "Add"
|
||||
| NullaryIlOp.Add_ovf -> "Add_ovf"
|
||||
| NullaryIlOp.Add_ovf_un -> "Add_ovf_un"
|
||||
| NullaryIlOp.Mul -> "Mul"
|
||||
| NullaryIlOp.Mul_ovf -> "Mul_ovf"
|
||||
| NullaryIlOp.Mul_ovf_un -> "Mul_ovf_un"
|
||||
| NullaryIlOp.Div -> "Div"
|
||||
| NullaryIlOp.Div_un -> "Div_un"
|
||||
| NullaryIlOp.Rem -> "Rem"
|
||||
| NullaryIlOp.Rem_un -> "Rem_un"
|
||||
| NullaryIlOp.Neg -> "Neg"
|
||||
| NullaryIlOp.Not -> "Not"
|
||||
| NullaryIlOp.Shr -> "Shr"
|
||||
| NullaryIlOp.Shr_un -> "Shr_un"
|
||||
| NullaryIlOp.Shl -> "Shl"
|
||||
| NullaryIlOp.Conv_ovf_i -> "Conv_ovf_i"
|
||||
| NullaryIlOp.Conv_ovf_u -> "Conv_ovf_u"
|
||||
| NullaryIlOp.And -> "And"
|
||||
| NullaryIlOp.Or -> "Or"
|
||||
| NullaryIlOp.Xor -> "Xor"
|
||||
| NullaryIlOp.Conv_I -> "Conv_I"
|
||||
| NullaryIlOp.Conv_I1 -> "Conv_I1"
|
||||
| NullaryIlOp.Conv_I2 -> "Conv_I2"
|
||||
| NullaryIlOp.Conv_I4 -> "Conv_I4"
|
||||
| NullaryIlOp.Conv_I8 -> "Conv_I8"
|
||||
| NullaryIlOp.Conv_R4 -> "Conv_R4"
|
||||
| NullaryIlOp.Conv_R8 -> "Conv_R8"
|
||||
| NullaryIlOp.Conv_U -> "Conv_U"
|
||||
| NullaryIlOp.Conv_U1 -> "Conv_U1"
|
||||
| NullaryIlOp.Conv_U2 -> "Conv_U2"
|
||||
| NullaryIlOp.Conv_U4 -> "Conv_U4"
|
||||
| NullaryIlOp.Conv_U8 -> "Conv_U8"
|
||||
| NullaryIlOp.Conv_ovf_u1 -> "Conv_ovf_u1"
|
||||
| NullaryIlOp.Conv_ovf_u2 -> "Conv_ovf_u2"
|
||||
| NullaryIlOp.Conv_ovf_u4 -> "Conv_ovf_u4"
|
||||
| NullaryIlOp.Conv_ovf_u8 -> "Conv_ovf_u8"
|
||||
| NullaryIlOp.Conv_ovf_i1 -> "Conv_ovf_i1"
|
||||
| NullaryIlOp.Conv_ovf_i2 -> "Conv_ovf_i2"
|
||||
| NullaryIlOp.Conv_ovf_i4 -> "Conv_ovf_i4"
|
||||
| NullaryIlOp.Conv_ovf_i8 -> "Conv_ovf_i8"
|
||||
| NullaryIlOp.LdLen -> "LdLen"
|
||||
| NullaryIlOp.Endfilter -> "Endfilter"
|
||||
| NullaryIlOp.Endfinally -> "Endfinally"
|
||||
| NullaryIlOp.Rethrow -> "Rethrow"
|
||||
| NullaryIlOp.Throw -> "Throw"
|
||||
| NullaryIlOp.Localloc -> "Localloc"
|
||||
| NullaryIlOp.Ldind_ref -> "Ldind_ref"
|
||||
| NullaryIlOp.Stind_ref -> "Stind_ref"
|
||||
| NullaryIlOp.Stind_I -> "Stind_I"
|
||||
| NullaryIlOp.Stind_I1 -> "Stind_I1"
|
||||
| NullaryIlOp.Stind_I2 -> "Stind_I2"
|
||||
| NullaryIlOp.Stind_I4 -> "Stind_I4"
|
||||
| NullaryIlOp.Stind_I8 -> "Stind_I8"
|
||||
| NullaryIlOp.Stind_R4 -> "Stind_R4"
|
||||
| NullaryIlOp.Stind_R8 -> "Stind_R8"
|
||||
| NullaryIlOp.Ldind_i -> "Ldind_i"
|
||||
| NullaryIlOp.Ldind_i1 -> "Ldind_i1"
|
||||
| NullaryIlOp.Ldind_i2 -> "Ldind_i2"
|
||||
| NullaryIlOp.Ldind_i4 -> "Ldind_i4"
|
||||
| NullaryIlOp.Ldind_i8 -> "Ldind_i8"
|
||||
| NullaryIlOp.Ldind_u1 -> "Ldind_u1"
|
||||
| NullaryIlOp.Ldind_u2 -> "Ldind_u2"
|
||||
| NullaryIlOp.Ldind_u4 -> "Ldind_u4"
|
||||
| NullaryIlOp.Ldind_u8 -> "Ldind_u8"
|
||||
| NullaryIlOp.Ldind_r4 -> "Ldind_r4"
|
||||
| NullaryIlOp.Ldind_r8 -> "Ldind_r8"
|
||||
| NullaryIlOp.Volatile -> "Volatile"
|
||||
| NullaryIlOp.Tail -> "Tail"
|
||||
| NullaryIlOp.Conv_ovf_i_un -> "Conv_ovf_i_un"
|
||||
| NullaryIlOp.Conv_ovf_u_un -> "Conv_ovf_u_un"
|
||||
| NullaryIlOp.Conv_ovf_i1_un -> "Conv_ovf_i1_un"
|
||||
| NullaryIlOp.Conv_ovf_u1_un -> "Conv_ovf_u1_un"
|
||||
| NullaryIlOp.Conv_ovf_i2_un -> "Conv_ovf_i2_un"
|
||||
| NullaryIlOp.Conv_ovf_u2_un -> "Conv_ovf_u2_un"
|
||||
| NullaryIlOp.Conv_ovf_i4_un -> "Conv_ovf_i4_un"
|
||||
| NullaryIlOp.Conv_ovf_u4_un -> "Conv_ovf_u4_un"
|
||||
| NullaryIlOp.Conv_ovf_i8_un -> "Conv_ovf_i8_un"
|
||||
| NullaryIlOp.Conv_ovf_u8_un -> "Conv_ovf_u8_un"
|
||||
| NullaryIlOp.Ldelem_i -> "Ldelem_i"
|
||||
| NullaryIlOp.Ldelem_i1 -> "Ldelem_i1"
|
||||
| NullaryIlOp.Ldelem_u1 -> "Ldelem_u1"
|
||||
| NullaryIlOp.Ldelem_i2 -> "Ldelem_i2"
|
||||
| NullaryIlOp.Ldelem_u2 -> "Ldelem_u2"
|
||||
| NullaryIlOp.Ldelem_i4 -> "Ldelem_i4"
|
||||
| NullaryIlOp.Ldelem_u4 -> "Ldelem_u4"
|
||||
| NullaryIlOp.Ldelem_i8 -> "Ldelem_i8"
|
||||
| NullaryIlOp.Ldelem_u8 -> "Ldelem_u8"
|
||||
| NullaryIlOp.Ldelem_r4 -> "Ldelem_r4"
|
||||
| NullaryIlOp.Ldelem_r8 -> "Ldelem_r8"
|
||||
| NullaryIlOp.Ldelem_ref -> "Ldelem_ref"
|
||||
| NullaryIlOp.Stelem_i -> "Stelem_i"
|
||||
| NullaryIlOp.Stelem_i1 -> "Stelem_i1"
|
||||
| NullaryIlOp.Stelem_u1 -> "Stelem_u1"
|
||||
| NullaryIlOp.Stelem_i2 -> "Stelem_i2"
|
||||
| NullaryIlOp.Stelem_u2 -> "Stelem_u2"
|
||||
| NullaryIlOp.Stelem_i4 -> "Stelem_i4"
|
||||
| NullaryIlOp.Stelem_u4 -> "Stelem_u4"
|
||||
| NullaryIlOp.Stelem_i8 -> "Stelem_i8"
|
||||
| NullaryIlOp.Stelem_u8 -> "Stelem_u8"
|
||||
| NullaryIlOp.Stelem_r4 -> "Stelem_r4"
|
||||
| NullaryIlOp.Stelem_r8 -> "Stelem_r8"
|
||||
| NullaryIlOp.Stelem_ref -> "Stelem_ref"
|
||||
| NullaryIlOp.Cpblk -> "Cpblk"
|
||||
| NullaryIlOp.Initblk -> "Initblk"
|
||||
| NullaryIlOp.Break -> "Break"
|
||||
| NullaryIlOp.Conv_r_un -> "Conv_r_un"
|
||||
| NullaryIlOp.Arglist -> "Arglist"
|
||||
| NullaryIlOp.Ckfinite -> "Ckfinite"
|
||||
| NullaryIlOp.Readonly -> "Readonly"
|
||||
| NullaryIlOp.Refanytype -> "Refanytype"
|
||||
|
||||
/// The number of bytes this instruction takes in memory.
|
||||
static member NumberOfBytes (op : NullaryIlOp) : int =
|
||||
match op with
|
||||
| Arglist
|
||||
| Ceq
|
||||
| Cgt
|
||||
| Cgt_un
|
||||
| Clt
|
||||
| Clt_un
|
||||
| Localloc
|
||||
| Endfilter
|
||||
| Volatile
|
||||
| Tail
|
||||
| Cpblk
|
||||
| Initblk
|
||||
| Rethrow
|
||||
| Refanytype
|
||||
| Readonly -> 2
|
||||
| _ -> 1
|
||||
|
||||
type UnaryConstIlOp =
|
||||
| Stloc of uint16
|
||||
| Stloc_s of int8
|
||||
| Ldc_I8 of int64
|
||||
| Ldc_I4 of int32
|
||||
| Ldc_R4 of single
|
||||
| Ldc_R8 of float
|
||||
| Ldc_I4_s of int8
|
||||
| Br of int32
|
||||
| Br_s of int8
|
||||
| Brfalse_s of int8
|
||||
| Brtrue_s of int8
|
||||
| Brfalse of int32
|
||||
| Brtrue of int32
|
||||
| Beq_s of int8
|
||||
| Blt_s of int8
|
||||
| Ble_s of int8
|
||||
| Bgt_s of int8
|
||||
| Bge_s of int8
|
||||
| Beq of int32
|
||||
| Blt of int32
|
||||
| Ble of int32
|
||||
| Bgt of int32
|
||||
| Bge of int32
|
||||
| Bne_un_s of int8
|
||||
| Bge_un_s of int8
|
||||
| Bgt_un_s of int8
|
||||
| Ble_un_s of int8
|
||||
| Blt_un_s of int8
|
||||
| Bne_un of int32
|
||||
| Bge_un of int32
|
||||
| Bgt_un of int32
|
||||
| Ble_un of int32
|
||||
| Blt_un of int32
|
||||
| Ldloc_s of uint8
|
||||
| Ldloca_s of uint8
|
||||
| Ldarga of uint16
|
||||
| Ldarg_s of uint8
|
||||
| Ldarga_s of uint8
|
||||
/// Unconditionally transfer control to this offset from the next instruction;
|
||||
/// like Br but can leave a try/filter/catch block too, and ensures surrounding `finally` blocks execute.
|
||||
/// Unconditionally empties the evaluation stack; so a Leave outside an exception-handling block is just a Br which
|
||||
/// also clears the eval stack.
|
||||
| Leave of int32
|
||||
/// Unconditionally transfer control to this offset from the next instruction;
|
||||
/// like Br but can leave a try/filter/catch block too, and ensures surrounding `finally` blocks execute.
|
||||
/// Unconditionally empties the evaluation stack; so a Leave outside an exception-handling block is just a Br which
|
||||
/// also clears the eval stack.
|
||||
| Leave_s of int8
|
||||
| Starg_s of uint8
|
||||
| Starg of uint16
|
||||
| Unaligned of uint8
|
||||
| Ldloc of uint16
|
||||
| Ldloca of uint16
|
||||
| Ldarg of uint16
|
||||
|
||||
/// The number of bytes this instruction takes in memory, including its constant argument that is inline in the
|
||||
/// byte stream.
|
||||
static member NumberOfBytes (op : UnaryConstIlOp) : int =
|
||||
match op with
|
||||
| Ldarg _uint16
|
||||
| Ldarga _uint16
|
||||
| Starg _uint16
|
||||
| Ldloc _uint16
|
||||
| Ldloca _uint16
|
||||
| Stloc _uint16 -> 2 + 2 // Two-byte opcode + two-byte argument
|
||||
| Ldarg_s _
|
||||
| Ldarga_s _
|
||||
| Starg_s _
|
||||
| Ldloc_s _
|
||||
| Ldloca_s _
|
||||
| Stloc_s _
|
||||
| Ldc_I4_s _
|
||||
| Br_s _
|
||||
| Brfalse_s _
|
||||
| Brtrue_s _
|
||||
| Beq_s _
|
||||
| Blt_s _
|
||||
| Ble_s _
|
||||
| Bgt_s _
|
||||
| Bge_s _
|
||||
| Bne_un_s _
|
||||
| Bge_un_s _
|
||||
| Bgt_un_s _
|
||||
| Ble_un_s _
|
||||
| Blt_un_s _
|
||||
| Leave_s _
|
||||
| Unaligned _ -> 1 + 1 // One-byte opcode + one-byte argument
|
||||
| Ldc_I8 _ -> 1 + 8 // One-byte opcode + 8-byte argument
|
||||
| Ldc_I4 _
|
||||
| Br _
|
||||
| Brfalse _
|
||||
| Brtrue _
|
||||
| Beq _
|
||||
| Blt _
|
||||
| Ble _
|
||||
| Bgt _
|
||||
| Bge _
|
||||
| Bne_un _
|
||||
| Bge_un _
|
||||
| Bgt_un _
|
||||
| Ble_un _
|
||||
| Blt_un _
|
||||
| Leave _ -> 1 + 4 // One-byte opcode + 4-byte argument
|
||||
| Ldc_R4 _ -> 1 + 4 // One-byte opcode + 4-byte argument
|
||||
| Ldc_R8 _ -> 1 + 8 // One-byte opcode + 8-byte argument
|
||||
|
||||
override this.ToString () =
|
||||
match this with
|
||||
| UnaryConstIlOp.Stloc i -> $"Stloc %i{i}"
|
||||
| UnaryConstIlOp.Stloc_s i -> $"Stloc_s %i{i}"
|
||||
| UnaryConstIlOp.Ldc_I8 i -> $"Ldc_I8 %i{i}"
|
||||
| UnaryConstIlOp.Ldc_I4 i -> $"Ldc_I4 %i{i}"
|
||||
| UnaryConstIlOp.Ldc_R4 f -> $"Ldc_R4 %f{f}"
|
||||
| UnaryConstIlOp.Ldc_R8 f -> $"Ldc_R8 %f{f}"
|
||||
| UnaryConstIlOp.Ldc_I4_s i -> $"Ldc_I4_s %i{i}"
|
||||
| UnaryConstIlOp.Br i -> $"Br %i{i}"
|
||||
| UnaryConstIlOp.Br_s i -> $"Br_s %i{i}"
|
||||
| UnaryConstIlOp.Brfalse_s i -> $"Brfalse_s %i{i}"
|
||||
| UnaryConstIlOp.Brtrue_s i -> $"Brtrue_s %i{i}"
|
||||
| UnaryConstIlOp.Brfalse i -> $"Brfalse %i{i}"
|
||||
| UnaryConstIlOp.Brtrue i -> $"Brtrue %i{i}"
|
||||
| UnaryConstIlOp.Beq_s i -> $"Beq_s %i{i}"
|
||||
| UnaryConstIlOp.Blt_s i -> $"Blt_s %i{i}"
|
||||
| UnaryConstIlOp.Ble_s i -> $"Ble_s %i{i}"
|
||||
| UnaryConstIlOp.Bgt_s i -> $"Bgt_s %i{i}"
|
||||
| UnaryConstIlOp.Bge_s i -> $"Bge_s %i{i}"
|
||||
| UnaryConstIlOp.Beq i -> $"Beq %i{i}"
|
||||
| UnaryConstIlOp.Blt i -> $"Blt %i{i}"
|
||||
| UnaryConstIlOp.Ble i -> $"Ble %i{i}"
|
||||
| UnaryConstIlOp.Bgt i -> $"Bgt %i{i}"
|
||||
| UnaryConstIlOp.Bge i -> $"Bge %i{i}"
|
||||
| UnaryConstIlOp.Bne_un_s i -> $"Bne_un_s %i{i}"
|
||||
| UnaryConstIlOp.Bge_un_s i -> $"Bge_un_s %i{i}"
|
||||
| UnaryConstIlOp.Bgt_un_s i -> $"Bgt_un_s %i{i}"
|
||||
| UnaryConstIlOp.Ble_un_s i -> $"Ble_un_s %i{i}"
|
||||
| UnaryConstIlOp.Blt_un_s i -> $"Blt_un_s %i{i}"
|
||||
| UnaryConstIlOp.Bne_un i -> $"Bne_un %i{i}"
|
||||
| UnaryConstIlOp.Bge_un i -> $"Bge_un %i{i}"
|
||||
| UnaryConstIlOp.Bgt_un i -> $"Bgt_un %i{i}"
|
||||
| UnaryConstIlOp.Ble_un i -> $"Ble_un %i{i}"
|
||||
| UnaryConstIlOp.Blt_un i -> $"Blt_un %i{i}"
|
||||
| UnaryConstIlOp.Ldloc_s i -> $"Ldloc_s %i{i}"
|
||||
| UnaryConstIlOp.Ldloca_s i -> $"Ldloca_s %i{i}"
|
||||
| UnaryConstIlOp.Ldarga i -> $"Ldarga %i{i}"
|
||||
| UnaryConstIlOp.Ldarg_s i -> $"Ldarg_s %i{i}"
|
||||
| UnaryConstIlOp.Ldarga_s i -> $"Ldarga_s %i{i}"
|
||||
| UnaryConstIlOp.Leave i -> $"Leave %i{i}"
|
||||
| UnaryConstIlOp.Leave_s i -> $"Leave_s %i{i}"
|
||||
| UnaryConstIlOp.Starg_s i -> $"Starg_s %i{i}"
|
||||
| UnaryConstIlOp.Starg i -> $"Starg %i{i}"
|
||||
| UnaryConstIlOp.Unaligned i -> $"Unaligned %i{i}"
|
||||
| UnaryConstIlOp.Ldloc i -> $"Ldloc %i{i}"
|
||||
| UnaryConstIlOp.Ldloca i -> $"Ldloca %i{i}"
|
||||
| UnaryConstIlOp.Ldarg i -> $"Ldarg %i{i}"
|
||||
|
||||
type UnaryMetadataTokenIlOp =
|
||||
| Call
|
||||
| Calli
|
||||
| Callvirt
|
||||
| Castclass
|
||||
| Newobj
|
||||
| Newarr
|
||||
| Box
|
||||
| Ldelema
|
||||
| Isinst
|
||||
/// Pop value from stack; pop object ref from stack; set specified field on that object to that value.
|
||||
| Stfld
|
||||
/// Pop value from eval stack; set specified static field to that value.
|
||||
| Stsfld
|
||||
/// Pop object ref from eval stack; look up specified field on that object; push field's value to eval stack.
|
||||
| Ldfld
|
||||
/// Pop object ref from eval stack; find address of specified field on that object; push address to eval stack.
|
||||
| Ldflda
|
||||
/// Push value of specified static field onto eval stack.
|
||||
| Ldsfld
|
||||
| Ldsflda
|
||||
| Unbox_Any
|
||||
/// Replaces the array element at a given index with the value on the eval stack
|
||||
| Stelem
|
||||
/// Pushes the element at a specified array index onto the eval stack
|
||||
| Ldelem
|
||||
| Initobj
|
||||
/// Pushes an unmanaged pointer (native int) to the stack, pointing to native code.
|
||||
| Ldftn
|
||||
| Stobj
|
||||
| Constrained
|
||||
| Ldtoken
|
||||
| Cpobj
|
||||
| Ldobj
|
||||
| Sizeof
|
||||
| Unbox
|
||||
| Ldvirtftn
|
||||
| Mkrefany
|
||||
| Refanyval
|
||||
| Jmp
|
||||
|
||||
override this.ToString () =
|
||||
match this with
|
||||
| UnaryMetadataTokenIlOp.Call -> "Call"
|
||||
| UnaryMetadataTokenIlOp.Calli -> "Calli"
|
||||
| UnaryMetadataTokenIlOp.Callvirt -> "Callvirt"
|
||||
| UnaryMetadataTokenIlOp.Castclass -> "Castclass"
|
||||
| UnaryMetadataTokenIlOp.Newobj -> "Newobj"
|
||||
| UnaryMetadataTokenIlOp.Newarr -> "Newarr"
|
||||
| UnaryMetadataTokenIlOp.Box -> "Box"
|
||||
| UnaryMetadataTokenIlOp.Ldelema -> "Ldelema"
|
||||
| UnaryMetadataTokenIlOp.Isinst -> "Isinst"
|
||||
| UnaryMetadataTokenIlOp.Stfld -> "Stfld"
|
||||
| UnaryMetadataTokenIlOp.Stsfld -> "Stsfld"
|
||||
| UnaryMetadataTokenIlOp.Ldfld -> "Ldfld"
|
||||
| UnaryMetadataTokenIlOp.Ldflda -> "Ldflda"
|
||||
| UnaryMetadataTokenIlOp.Ldsfld -> "Ldsfld"
|
||||
| UnaryMetadataTokenIlOp.Ldsflda -> "Ldsflda"
|
||||
| UnaryMetadataTokenIlOp.Unbox_Any -> "Unbox_Any"
|
||||
| UnaryMetadataTokenIlOp.Stelem -> "Stelem"
|
||||
| UnaryMetadataTokenIlOp.Ldelem -> "Ldelem"
|
||||
| UnaryMetadataTokenIlOp.Initobj -> "Initobj"
|
||||
| UnaryMetadataTokenIlOp.Ldftn -> "Ldftn"
|
||||
| UnaryMetadataTokenIlOp.Stobj -> "Stobj"
|
||||
| UnaryMetadataTokenIlOp.Constrained -> "Constrained"
|
||||
| UnaryMetadataTokenIlOp.Ldtoken -> "Ldtoken"
|
||||
| UnaryMetadataTokenIlOp.Cpobj -> "Cpobj"
|
||||
| UnaryMetadataTokenIlOp.Ldobj -> "Ldobj"
|
||||
| UnaryMetadataTokenIlOp.Sizeof -> "Sizeof"
|
||||
| UnaryMetadataTokenIlOp.Unbox -> "Unbox"
|
||||
| UnaryMetadataTokenIlOp.Ldvirtftn -> "Ldvirtftn"
|
||||
| UnaryMetadataTokenIlOp.Mkrefany -> "Mkrefany"
|
||||
| UnaryMetadataTokenIlOp.Refanyval -> "Refanyval"
|
||||
| UnaryMetadataTokenIlOp.Jmp -> "Jmp"
|
||||
|
||||
/// The number of bytes this instruction takes in memory, including its metadata token argument.
|
||||
static member NumberOfBytes (op : UnaryMetadataTokenIlOp) : int =
|
||||
match op with
|
||||
| Ldftn
|
||||
| Ldvirtftn
|
||||
| Initobj
|
||||
| Constrained
|
||||
| Sizeof -> 2 + 4 // Two-byte opcode + 4-byte token
|
||||
| Call
|
||||
| Calli
|
||||
| Callvirt
|
||||
| Castclass
|
||||
| Newobj
|
||||
| Newarr
|
||||
| Box
|
||||
| Ldelema
|
||||
| Isinst
|
||||
| Stfld
|
||||
| Stsfld
|
||||
| Ldfld
|
||||
| Ldflda
|
||||
| Ldsfld
|
||||
| Ldsflda
|
||||
| Unbox_Any
|
||||
| Stelem
|
||||
| Ldelem
|
||||
| Stobj
|
||||
| Ldtoken
|
||||
| Cpobj
|
||||
| Ldobj
|
||||
| Unbox
|
||||
| Mkrefany
|
||||
| Refanyval
|
||||
| Jmp -> 1 + 4 // One-byte opcode + 4-byte token
|
||||
|
||||
type UnaryStringTokenIlOp =
|
||||
| Ldstr
|
||||
|
||||
static member NumberOfBytes (op : UnaryStringTokenIlOp) : int =
|
||||
match op with
|
||||
| Ldstr -> 1 + 4
|
||||
|
||||
type IlOp =
|
||||
| Nullary of NullaryIlOp
|
||||
| UnaryConst of UnaryConstIlOp
|
||||
| UnaryMetadataToken of UnaryMetadataTokenIlOp * MetadataToken
|
||||
| UnaryStringToken of UnaryStringTokenIlOp * StringToken
|
||||
| Switch of int32 ImmutableArray
|
||||
|
||||
override this.ToString () =
|
||||
match this with
|
||||
| IlOp.Nullary op -> $"Nullary %O{op}"
|
||||
| IlOp.UnaryConst op -> $"UnaryConst.%O{op}"
|
||||
| IlOp.UnaryMetadataToken (op, _) -> $"UnaryMetadataToken.%O{op}"
|
||||
| IlOp.UnaryStringToken (op, _) -> $"UnaryStringToken.%O{op}"
|
||||
| IlOp.Switch arr -> $"Switch[%i{arr.Length}]"
|
||||
|
||||
static member Format (opCode : IlOp) (offset : int) : string = $" IL_%04X{offset}: %-20O{opCode}"
|
||||
|
||||
static member NumberOfBytes (op : IlOp) =
|
||||
match op with
|
||||
| Nullary op -> NullaryIlOp.NumberOfBytes op
|
||||
| UnaryConst op -> UnaryConstIlOp.NumberOfBytes op
|
||||
| UnaryMetadataToken (op, _) -> UnaryMetadataTokenIlOp.NumberOfBytes op
|
||||
| UnaryStringToken (op, _) -> UnaryStringTokenIlOp.NumberOfBytes op
|
||||
| Switch arr -> 1 + 4 + arr.Length * 4
|
68
WoofWare.PawPrint.Domain/MemberReference.fs
Normal file
68
WoofWare.PawPrint.Domain/MemberReference.fs
Normal file
@@ -0,0 +1,68 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
|
||||
type MemberSignature =
|
||||
| Field of TypeDefn
|
||||
| Method of TypeMethodSignature<TypeDefn>
|
||||
|
||||
type MemberReference<'parent> =
|
||||
{
|
||||
Name : StringToken
|
||||
PrettyName : string
|
||||
Parent : 'parent
|
||||
Signature : MemberSignature
|
||||
}
|
||||
|
||||
type MemberRefSigSwitch =
|
||||
| Default
|
||||
| Field
|
||||
| VarArg
|
||||
| Generic
|
||||
|
||||
static member Identify (b : byte) =
|
||||
match b &&& 0xFuy with
|
||||
| 0uy -> MemberRefSigSwitch.Default
|
||||
| 5uy -> MemberRefSigSwitch.VarArg
|
||||
| 6uy -> MemberRefSigSwitch.Field
|
||||
| 0x10uy -> MemberRefSigSwitch.Generic
|
||||
| n -> failwith $"Bad member ref sig: %i{n}"
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module MemberReference =
|
||||
let make<'parent>
|
||||
(blobReader : BlobHandle -> BlobReader)
|
||||
(getString : StringHandle -> string)
|
||||
(makeParent : EntityHandle -> 'parent)
|
||||
(assemblyName : AssemblyName)
|
||||
(mr : System.Reflection.Metadata.MemberReference)
|
||||
: MemberReference<'parent>
|
||||
=
|
||||
let name = StringToken.String mr.Name
|
||||
|
||||
let br = blobReader mr.Signature
|
||||
let header = br.ReadSignatureHeader ()
|
||||
|
||||
let signature =
|
||||
match header.Kind with
|
||||
| SignatureKind.Method -> mr.DecodeMethodSignature (TypeDefn.typeProvider assemblyName, ()) |> Choice1Of2
|
||||
| SignatureKind.Field -> mr.DecodeFieldSignature (TypeDefn.typeProvider assemblyName, ()) |> Choice2Of2
|
||||
| SignatureKind.LocalVariables -> failwith "TODO: LocalVariables"
|
||||
| SignatureKind.Property -> failwith "TODO: Property"
|
||||
| SignatureKind.MethodSpecification -> failwith "TODO: MethodSpec"
|
||||
| i -> raise (ArgumentOutOfRangeException $"{i}")
|
||||
|
||||
let signature =
|
||||
match signature with
|
||||
| Choice1Of2 methodSignature -> TypeMethodSignature.make methodSignature |> MemberSignature.Method
|
||||
| Choice2Of2 typeDefn -> MemberSignature.Field typeDefn
|
||||
|
||||
{
|
||||
Name = name
|
||||
PrettyName = getString mr.Name
|
||||
// Horrible abuse to get this as an int
|
||||
Parent = makeParent mr.Parent
|
||||
Signature = signature
|
||||
}
|
694
WoofWare.PawPrint.Domain/MethodInfo.fs
Normal file
694
WoofWare.PawPrint.Domain/MethodInfo.fs
Normal file
@@ -0,0 +1,694 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
#nowarn "9"
|
||||
|
||||
open System
|
||||
open System.Collections.Immutable
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
open System.Reflection.PortableExecutable
|
||||
open Microsoft.Extensions.Logging
|
||||
|
||||
/// <summary>
|
||||
/// Represents information about a method parameter.
|
||||
/// Corresponds to Parameter in System.Reflection.Metadata.
|
||||
/// </summary>
|
||||
type Parameter =
|
||||
{
|
||||
/// <summary>The name of the parameter.</summary>
|
||||
Name : string
|
||||
|
||||
/// <summary>
|
||||
/// The default value of the parameter, if one is specified.
|
||||
/// This is used for optional parameters.
|
||||
/// </summary>
|
||||
DefaultValue : Constant
|
||||
|
||||
/// <summary>
|
||||
/// The position of the parameter in the parameter list.
|
||||
/// For instance methods, index 0 is the 'this' parameter.
|
||||
/// </summary>
|
||||
SequenceNumber : int
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Parameter =
|
||||
let readAll (metadata : MetadataReader) (param : ParameterHandleCollection) : Parameter ImmutableArray =
|
||||
let result = ImmutableArray.CreateBuilder ()
|
||||
|
||||
for param in param do
|
||||
let param = metadata.GetParameter param
|
||||
|
||||
// The spec doesn't seem to mention this behaviour, but a sequence number of 0 (and an unnamed parameter)
|
||||
// seems to correspond with a ref return.
|
||||
if param.SequenceNumber <> 0 then
|
||||
{
|
||||
Name = metadata.GetString param.Name
|
||||
DefaultValue = metadata.GetConstant (param.GetDefaultValue ())
|
||||
SequenceNumber = param.SequenceNumber
|
||||
}
|
||||
|> result.Add
|
||||
|
||||
result.ToImmutable ()
|
||||
|
||||
/// <summary>
|
||||
/// Represents a generic type or method parameter definition.
|
||||
/// Corresponds to GenericParameter in System.Reflection.Metadata.
|
||||
/// </summary>
|
||||
type GenericParameter =
|
||||
{
|
||||
/// <summary>The name of the generic parameter (e.g., 'T', 'TKey', etc.).</summary>
|
||||
Name : string
|
||||
|
||||
/// <summary>
|
||||
/// The zero-based index of the generic parameter in the generic parameter list.
|
||||
/// For example, in Dictionary<TKey, TValue&rt;, TKey has index 0 and TValue has index 1.
|
||||
/// </summary>
|
||||
SequenceNumber : int
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module GenericParameter =
|
||||
let readAll
|
||||
(metadata : MetadataReader)
|
||||
(param : GenericParameterHandleCollection)
|
||||
: GenericParameter ImmutableArray
|
||||
=
|
||||
param
|
||||
|> Seq.map (fun param ->
|
||||
let param = metadata.GetGenericParameter param
|
||||
|
||||
{
|
||||
Name = metadata.GetString param.Name
|
||||
SequenceNumber = param.Index
|
||||
}
|
||||
)
|
||||
|> ImmutableArray.CreateRange
|
||||
|
||||
type ExceptionOffset =
|
||||
{
|
||||
TryLength : int
|
||||
TryOffset : int
|
||||
HandlerLength : int
|
||||
HandlerOffset : int
|
||||
}
|
||||
|
||||
type ExceptionRegion =
|
||||
| Filter of filterOffset : int * ExceptionOffset
|
||||
/// Token is a TypeRef, TypeDef, or TypeSpec
|
||||
| Catch of MetadataToken * ExceptionOffset
|
||||
| Finally of ExceptionOffset
|
||||
| Fault of ExceptionOffset
|
||||
|
||||
static member OfExceptionRegion (r : System.Reflection.Metadata.ExceptionRegion) : ExceptionRegion =
|
||||
let offset =
|
||||
{
|
||||
HandlerLength = r.HandlerLength
|
||||
HandlerOffset = r.HandlerOffset
|
||||
TryLength = r.TryLength
|
||||
TryOffset = r.TryOffset
|
||||
}
|
||||
|
||||
match r.Kind with
|
||||
| ExceptionRegionKind.Catch -> ExceptionRegion.Catch (MetadataToken.ofEntityHandle r.CatchType, offset)
|
||||
| ExceptionRegionKind.Filter -> ExceptionRegion.Filter (r.FilterOffset, offset)
|
||||
| ExceptionRegionKind.Finally -> ExceptionRegion.Finally offset
|
||||
| ExceptionRegionKind.Fault -> ExceptionRegion.Fault offset
|
||||
| _ -> raise (ArgumentOutOfRangeException ())
|
||||
|
||||
type MethodInstructions =
|
||||
{
|
||||
/// <summary>
|
||||
/// The IL instructions that compose the method body, along with their offset positions.
|
||||
/// Each tuple contains the instruction and its offset in the method body.
|
||||
/// </summary>
|
||||
Instructions : (IlOp * int) list
|
||||
|
||||
/// <summary>
|
||||
/// A map from instruction offset (program counter) to the corresponding IL operation.
|
||||
/// This is the inverse of Instructions for efficient lookup.
|
||||
/// </summary>
|
||||
Locations : Map<int, IlOp>
|
||||
|
||||
/// <summary>
|
||||
/// Whether local variables in this method should be initialized to their default values.
|
||||
/// This corresponds to the localsinit flag in the method header.
|
||||
/// </summary>
|
||||
LocalsInit : bool
|
||||
|
||||
LocalVars : ImmutableArray<TypeDefn> option
|
||||
|
||||
ExceptionRegions : ImmutableArray<ExceptionRegion>
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents detailed information about a method in a .NET assembly.
|
||||
/// This is a strongly-typed representation of MethodDefinition from System.Reflection.Metadata.
|
||||
/// </summary>
|
||||
type MethodInfo<'typeGenerics when 'typeGenerics :> IComparable<'typeGenerics> and 'typeGenerics : comparison> =
|
||||
{
|
||||
/// <summary>
|
||||
/// The type that declares this method, along with its assembly information.
|
||||
/// </summary>
|
||||
DeclaringType : ConcreteType<'typeGenerics>
|
||||
|
||||
/// <summary>
|
||||
/// The metadata token handle that uniquely identifies this method in the assembly.
|
||||
/// </summary>
|
||||
Handle : MethodDefinitionHandle
|
||||
|
||||
/// <summary>The name of the method.</summary>
|
||||
Name : string
|
||||
|
||||
/// <summary>
|
||||
/// The IL instructions that compose the method body, along with their offset positions.
|
||||
///
|
||||
/// There may be no instructions for this method, e.g. if it's an `InternalCall`.
|
||||
/// </summary>
|
||||
Instructions : MethodInstructions option
|
||||
|
||||
/// <summary>
|
||||
/// The parameters of this method.
|
||||
/// </summary>
|
||||
Parameters : Parameter ImmutableArray
|
||||
|
||||
/// <summary>
|
||||
/// The generic type parameters defined by this method, if any.
|
||||
/// </summary>
|
||||
Generics : GenericParameter ImmutableArray
|
||||
|
||||
/// <summary>
|
||||
/// The signature of the method, including return type and parameter types.
|
||||
/// </summary>
|
||||
Signature : TypeMethodSignature<TypeDefn>
|
||||
|
||||
/// <summary>
|
||||
/// Custom attributes defined on the method. I've never yet seen one of these in practice.
|
||||
/// </summary>
|
||||
CustomAttributes : WoofWare.PawPrint.CustomAttribute ImmutableArray
|
||||
|
||||
MethodAttributes : MethodAttributes
|
||||
|
||||
ImplAttributes : MethodImplAttributes
|
||||
|
||||
/// <summary>
|
||||
/// Whether this method is static (true) or an instance method (false).
|
||||
/// </summary>
|
||||
IsStatic : bool
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this method's implementation is directly supplied by the CLI, rather than being loaded
|
||||
/// from an assembly as IL.
|
||||
/// </summary>
|
||||
member this.IsCliInternal : bool =
|
||||
this.ImplAttributes.HasFlag MethodImplAttributes.InternalCall
|
||||
|
||||
/// <summary>
|
||||
/// Whether this method is implemented as a platform invoke (P/Invoke) to unmanaged code.
|
||||
/// </summary>
|
||||
member this.IsPinvokeImpl : bool =
|
||||
this.MethodAttributes.HasFlag MethodAttributes.PinvokeImpl
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module MethodInfo =
|
||||
let mapTypeGenerics<'a, 'b
|
||||
when 'a :> IComparable<'a> and 'a : comparison and 'b : comparison and 'b :> IComparable<'b>>
|
||||
(f : int -> 'a -> 'b)
|
||||
(m : MethodInfo<'a>)
|
||||
: MethodInfo<'b>
|
||||
=
|
||||
{
|
||||
DeclaringType = m.DeclaringType |> ConcreteType.mapGeneric f
|
||||
Handle = m.Handle
|
||||
Name = m.Name
|
||||
Instructions = m.Instructions
|
||||
Parameters = m.Parameters
|
||||
Generics = m.Generics
|
||||
Signature = m.Signature
|
||||
CustomAttributes = m.CustomAttributes
|
||||
MethodAttributes = m.MethodAttributes
|
||||
ImplAttributes = m.ImplAttributes
|
||||
IsStatic = m.IsStatic
|
||||
}
|
||||
|
||||
type private Dummy = class end
|
||||
|
||||
type private MethodBody =
|
||||
{
|
||||
Instructions : (IlOp * int) list
|
||||
LocalInit : bool
|
||||
LocalSig : ImmutableArray<TypeDefn> option
|
||||
MaxStackSize : int
|
||||
ExceptionRegions : ImmutableArray<ExceptionRegion>
|
||||
}
|
||||
|
||||
let private readMetadataToken (reader : byref<BlobReader>) : MetadataToken =
|
||||
reader.ReadUInt32 () |> int |> MetadataToken.ofInt
|
||||
|
||||
let private readStringToken (reader : byref<BlobReader>) : StringToken =
|
||||
let value = reader.ReadUInt32 () |> int
|
||||
StringToken.ofInt value
|
||||
|
||||
// TODO: each opcode probably ought to store how many bytes it takes, so we can advance the program counter?
|
||||
let private readOpCode (reader : byref<BlobReader>) : ILOpCode =
|
||||
let op = reader.ReadByte ()
|
||||
|
||||
if op = 0xFEuy then
|
||||
let op2 = reader.ReadByte ()
|
||||
LanguagePrimitives.EnumOfValue (0xFE00us ||| (uint16 op2))
|
||||
else
|
||||
LanguagePrimitives.EnumOfValue (uint16 op)
|
||||
|
||||
let private readMethodBody
|
||||
(peReader : PEReader)
|
||||
(metadataReader : MetadataReader)
|
||||
(assembly : AssemblyName)
|
||||
(methodDef : MethodDefinition)
|
||||
: MethodBody option
|
||||
=
|
||||
if methodDef.RelativeVirtualAddress = 0 then
|
||||
None
|
||||
else
|
||||
let methodBody = peReader.GetMethodBody methodDef.RelativeVirtualAddress
|
||||
|
||||
let localSig =
|
||||
if methodBody.LocalSignature.IsNil then
|
||||
None
|
||||
else
|
||||
|
||||
let s = methodBody.LocalSignature |> metadataReader.GetStandaloneSignature
|
||||
s.DecodeLocalSignature (TypeDefn.typeProvider assembly, ()) |> Some
|
||||
|
||||
let ilBytes = methodBody.GetILBytes ()
|
||||
use bytes = fixed ilBytes
|
||||
let mutable reader : BlobReader = BlobReader (bytes, ilBytes.Length)
|
||||
|
||||
let rec readInstructions acc =
|
||||
if reader.Offset >= ilBytes.Length then
|
||||
List.rev acc
|
||||
else
|
||||
let offset = reader.Offset
|
||||
let opCode = readOpCode (&reader)
|
||||
|
||||
let opCode =
|
||||
match opCode with
|
||||
| ILOpCode.Nop -> IlOp.Nullary NullaryIlOp.Nop
|
||||
| ILOpCode.Break -> IlOp.Nullary NullaryIlOp.Break
|
||||
| ILOpCode.Ldarg_0 -> IlOp.Nullary NullaryIlOp.LdArg0
|
||||
| ILOpCode.Ldarg_1 -> IlOp.Nullary NullaryIlOp.LdArg1
|
||||
| ILOpCode.Ldarg_2 -> IlOp.Nullary NullaryIlOp.LdArg2
|
||||
| ILOpCode.Ldarg_3 -> IlOp.Nullary NullaryIlOp.LdArg3
|
||||
| ILOpCode.Ldloc_0 -> IlOp.Nullary NullaryIlOp.Ldloc_0
|
||||
| ILOpCode.Ldloc_1 -> IlOp.Nullary NullaryIlOp.Ldloc_1
|
||||
| ILOpCode.Ldloc_2 -> IlOp.Nullary NullaryIlOp.Ldloc_2
|
||||
| ILOpCode.Ldloc_3 -> IlOp.Nullary NullaryIlOp.Ldloc_3
|
||||
| ILOpCode.Stloc_0 -> IlOp.Nullary NullaryIlOp.Stloc_0
|
||||
| ILOpCode.Stloc_1 -> IlOp.Nullary NullaryIlOp.Stloc_1
|
||||
| ILOpCode.Stloc_2 -> IlOp.Nullary NullaryIlOp.Stloc_2
|
||||
| ILOpCode.Stloc_3 -> IlOp.Nullary NullaryIlOp.Stloc_3
|
||||
| ILOpCode.Ldarg_s -> IlOp.UnaryConst (UnaryConstIlOp.Ldarg_s (reader.ReadByte ()))
|
||||
| ILOpCode.Ldarga_s -> IlOp.UnaryConst (UnaryConstIlOp.Ldarga_s (reader.ReadByte ()))
|
||||
| ILOpCode.Starg_s -> IlOp.UnaryConst (UnaryConstIlOp.Starg_s (reader.ReadByte ()))
|
||||
| ILOpCode.Ldloc_s -> IlOp.UnaryConst (UnaryConstIlOp.Ldloc_s (reader.ReadByte ()))
|
||||
| ILOpCode.Ldloca_s -> IlOp.UnaryConst (UnaryConstIlOp.Ldloca_s (reader.ReadByte ()))
|
||||
| ILOpCode.Stloc_s -> IlOp.UnaryConst (UnaryConstIlOp.Stloc_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Ldnull -> IlOp.Nullary NullaryIlOp.LdNull
|
||||
| ILOpCode.Ldc_i4_m1 -> IlOp.Nullary NullaryIlOp.LdcI4_m1
|
||||
| ILOpCode.Ldc_i4_0 -> IlOp.Nullary NullaryIlOp.LdcI4_0
|
||||
| ILOpCode.Ldc_i4_1 -> IlOp.Nullary NullaryIlOp.LdcI4_1
|
||||
| ILOpCode.Ldc_i4_2 -> IlOp.Nullary NullaryIlOp.LdcI4_2
|
||||
| ILOpCode.Ldc_i4_3 -> IlOp.Nullary NullaryIlOp.LdcI4_3
|
||||
| ILOpCode.Ldc_i4_4 -> IlOp.Nullary NullaryIlOp.LdcI4_4
|
||||
| ILOpCode.Ldc_i4_5 -> IlOp.Nullary NullaryIlOp.LdcI4_5
|
||||
| ILOpCode.Ldc_i4_6 -> IlOp.Nullary NullaryIlOp.LdcI4_6
|
||||
| ILOpCode.Ldc_i4_7 -> IlOp.Nullary NullaryIlOp.LdcI4_7
|
||||
| ILOpCode.Ldc_i4_8 -> IlOp.Nullary NullaryIlOp.LdcI4_8
|
||||
| ILOpCode.Ldc_i4_s -> IlOp.UnaryConst (UnaryConstIlOp.Ldc_I4_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Ldc_i4 -> IlOp.UnaryConst (UnaryConstIlOp.Ldc_I4 (reader.ReadInt32 ()))
|
||||
| ILOpCode.Ldc_i8 -> IlOp.UnaryConst (UnaryConstIlOp.Ldc_I8 (reader.ReadInt64 ()))
|
||||
| ILOpCode.Ldc_r4 -> IlOp.UnaryConst (UnaryConstIlOp.Ldc_R4 (reader.ReadSingle ()))
|
||||
| ILOpCode.Ldc_r8 -> IlOp.UnaryConst (UnaryConstIlOp.Ldc_R8 (reader.ReadDouble ()))
|
||||
| ILOpCode.Dup -> IlOp.Nullary NullaryIlOp.Dup
|
||||
| ILOpCode.Pop -> IlOp.Nullary NullaryIlOp.Pop
|
||||
| ILOpCode.Jmp ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Jmp, readMetadataToken &reader)
|
||||
| ILOpCode.Call ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Call, readMetadataToken &reader)
|
||||
| ILOpCode.Calli ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Calli, readMetadataToken &reader)
|
||||
| ILOpCode.Ret -> IlOp.Nullary NullaryIlOp.Ret
|
||||
| ILOpCode.Br_s -> IlOp.UnaryConst (UnaryConstIlOp.Br_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Brfalse_s -> IlOp.UnaryConst (UnaryConstIlOp.Brfalse_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Brtrue_s -> IlOp.UnaryConst (UnaryConstIlOp.Brtrue_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Beq_s -> IlOp.UnaryConst (UnaryConstIlOp.Beq_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Bge_s -> IlOp.UnaryConst (UnaryConstIlOp.Bge_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Bgt_s -> IlOp.UnaryConst (UnaryConstIlOp.Bgt_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Ble_s -> IlOp.UnaryConst (UnaryConstIlOp.Ble_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Blt_s -> IlOp.UnaryConst (UnaryConstIlOp.Blt_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Bne_un_s -> IlOp.UnaryConst (UnaryConstIlOp.Bne_un_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Bge_un_s -> IlOp.UnaryConst (UnaryConstIlOp.Bge_un_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Bgt_un_s -> IlOp.UnaryConst (UnaryConstIlOp.Bgt_un_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Ble_un_s -> IlOp.UnaryConst (UnaryConstIlOp.Ble_un_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Blt_un_s -> IlOp.UnaryConst (UnaryConstIlOp.Blt_un_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Br -> IlOp.UnaryConst (UnaryConstIlOp.Br (reader.ReadInt32 ()))
|
||||
| ILOpCode.Brfalse -> IlOp.UnaryConst (UnaryConstIlOp.Brfalse (reader.ReadInt32 ()))
|
||||
| ILOpCode.Brtrue -> IlOp.UnaryConst (UnaryConstIlOp.Brtrue (reader.ReadInt32 ()))
|
||||
| ILOpCode.Beq -> IlOp.UnaryConst (UnaryConstIlOp.Beq (reader.ReadInt32 ()))
|
||||
| ILOpCode.Bge -> IlOp.UnaryConst (UnaryConstIlOp.Bge (reader.ReadInt32 ()))
|
||||
| ILOpCode.Bgt -> IlOp.UnaryConst (UnaryConstIlOp.Bgt (reader.ReadInt32 ()))
|
||||
| ILOpCode.Ble -> IlOp.UnaryConst (UnaryConstIlOp.Ble (reader.ReadInt32 ()))
|
||||
| ILOpCode.Blt -> IlOp.UnaryConst (UnaryConstIlOp.Blt (reader.ReadInt32 ()))
|
||||
| ILOpCode.Bne_un -> IlOp.UnaryConst (UnaryConstIlOp.Bne_un (reader.ReadInt32 ()))
|
||||
| ILOpCode.Bge_un -> IlOp.UnaryConst (UnaryConstIlOp.Bge_un (reader.ReadInt32 ()))
|
||||
| ILOpCode.Bgt_un -> IlOp.UnaryConst (UnaryConstIlOp.Bgt_un (reader.ReadInt32 ()))
|
||||
| ILOpCode.Ble_un -> IlOp.UnaryConst (UnaryConstIlOp.Ble_un (reader.ReadInt32 ()))
|
||||
| ILOpCode.Blt_un -> IlOp.UnaryConst (UnaryConstIlOp.Blt_un (reader.ReadInt32 ()))
|
||||
| ILOpCode.Switch ->
|
||||
let count = reader.ReadUInt32 ()
|
||||
|
||||
if count > uint32 System.Int32.MaxValue then
|
||||
failwith "Debugger error: can't create a jump table with more than int32.Max entries"
|
||||
|
||||
let count = int count
|
||||
let result = ImmutableArray.CreateBuilder count
|
||||
|
||||
for i = 0 to count - 1 do
|
||||
result.Add (reader.ReadInt32 ())
|
||||
|
||||
IlOp.Switch (result.ToImmutable ())
|
||||
| ILOpCode.Ldind_i -> IlOp.Nullary NullaryIlOp.Ldind_i
|
||||
| ILOpCode.Ldind_i1 -> IlOp.Nullary NullaryIlOp.Ldind_i1
|
||||
| ILOpCode.Ldind_u1 -> IlOp.Nullary NullaryIlOp.Ldind_u1
|
||||
| ILOpCode.Ldind_i2 -> IlOp.Nullary NullaryIlOp.Ldind_i2
|
||||
| ILOpCode.Ldind_u2 -> IlOp.Nullary NullaryIlOp.Ldind_u2
|
||||
| ILOpCode.Ldind_i4 -> IlOp.Nullary NullaryIlOp.Ldind_i4
|
||||
| ILOpCode.Ldind_u4 -> IlOp.Nullary NullaryIlOp.Ldind_u4
|
||||
| ILOpCode.Ldind_i8 -> IlOp.Nullary NullaryIlOp.Ldind_i8
|
||||
| ILOpCode.Ldind_r4 -> IlOp.Nullary NullaryIlOp.Ldind_r4
|
||||
| ILOpCode.Ldind_r8 -> IlOp.Nullary NullaryIlOp.Ldind_r8
|
||||
| ILOpCode.Ldind_ref -> IlOp.Nullary NullaryIlOp.Ldind_ref
|
||||
| ILOpCode.Stind_ref -> IlOp.Nullary NullaryIlOp.Stind_ref
|
||||
| ILOpCode.Stind_i1 -> IlOp.Nullary NullaryIlOp.Stind_I1
|
||||
| ILOpCode.Stind_i2 -> IlOp.Nullary NullaryIlOp.Stind_I2
|
||||
| ILOpCode.Stind_i4 -> IlOp.Nullary NullaryIlOp.Stind_I4
|
||||
| ILOpCode.Stind_i8 -> IlOp.Nullary NullaryIlOp.Stind_I8
|
||||
| ILOpCode.Stind_r4 -> IlOp.Nullary NullaryIlOp.Stind_R4
|
||||
| ILOpCode.Stind_r8 -> IlOp.Nullary NullaryIlOp.Stind_R8
|
||||
| ILOpCode.Add -> IlOp.Nullary NullaryIlOp.Add
|
||||
| ILOpCode.Sub -> IlOp.Nullary NullaryIlOp.Sub
|
||||
| ILOpCode.Mul -> IlOp.Nullary NullaryIlOp.Mul
|
||||
| ILOpCode.Div -> IlOp.Nullary NullaryIlOp.Div
|
||||
| ILOpCode.Div_un -> IlOp.Nullary NullaryIlOp.Div_un
|
||||
| ILOpCode.Rem -> IlOp.Nullary NullaryIlOp.Rem
|
||||
| ILOpCode.Rem_un -> IlOp.Nullary NullaryIlOp.Rem_un
|
||||
| ILOpCode.And -> IlOp.Nullary NullaryIlOp.And
|
||||
| ILOpCode.Or -> IlOp.Nullary NullaryIlOp.Or
|
||||
| ILOpCode.Xor -> IlOp.Nullary NullaryIlOp.Xor
|
||||
| ILOpCode.Shl -> IlOp.Nullary NullaryIlOp.Shl
|
||||
| ILOpCode.Shr -> IlOp.Nullary NullaryIlOp.Shr
|
||||
| ILOpCode.Shr_un -> IlOp.Nullary NullaryIlOp.Shr_un
|
||||
| ILOpCode.Neg -> IlOp.Nullary NullaryIlOp.Neg
|
||||
| ILOpCode.Not -> IlOp.Nullary NullaryIlOp.Not
|
||||
| ILOpCode.Conv_i1 -> IlOp.Nullary NullaryIlOp.Conv_I1
|
||||
| ILOpCode.Conv_i2 -> IlOp.Nullary NullaryIlOp.Conv_I2
|
||||
| ILOpCode.Conv_i4 -> IlOp.Nullary NullaryIlOp.Conv_I4
|
||||
| ILOpCode.Conv_i8 -> IlOp.Nullary NullaryIlOp.Conv_I8
|
||||
| ILOpCode.Conv_r4 -> IlOp.Nullary NullaryIlOp.Conv_R4
|
||||
| ILOpCode.Conv_r8 -> IlOp.Nullary NullaryIlOp.Conv_R8
|
||||
| ILOpCode.Conv_u4 -> IlOp.Nullary NullaryIlOp.Conv_U4
|
||||
| ILOpCode.Conv_u8 -> IlOp.Nullary NullaryIlOp.Conv_U8
|
||||
| ILOpCode.Callvirt ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Callvirt, readMetadataToken &reader)
|
||||
| ILOpCode.Cpobj ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Cpobj, readMetadataToken &reader)
|
||||
| ILOpCode.Ldobj ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldobj, readMetadataToken &reader)
|
||||
| ILOpCode.Ldstr -> IlOp.UnaryStringToken (UnaryStringTokenIlOp.Ldstr, readStringToken &reader)
|
||||
| ILOpCode.Newobj ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Newobj, readMetadataToken &reader)
|
||||
| ILOpCode.Castclass ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Castclass, readMetadataToken &reader)
|
||||
| ILOpCode.Isinst ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Isinst, readMetadataToken &reader)
|
||||
| ILOpCode.Conv_r_un -> IlOp.Nullary NullaryIlOp.Conv_r_un
|
||||
| ILOpCode.Unbox ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Unbox, readMetadataToken &reader)
|
||||
| ILOpCode.Throw -> IlOp.Nullary NullaryIlOp.Throw
|
||||
| ILOpCode.Ldfld ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldfld, readMetadataToken &reader)
|
||||
| ILOpCode.Ldflda ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldflda, readMetadataToken &reader)
|
||||
| ILOpCode.Stfld ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Stfld, readMetadataToken &reader)
|
||||
| ILOpCode.Ldsfld ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldsfld, readMetadataToken &reader)
|
||||
| ILOpCode.Ldsflda ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldsflda, readMetadataToken &reader)
|
||||
| ILOpCode.Stsfld ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Stsfld, readMetadataToken &reader)
|
||||
| ILOpCode.Stobj ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Stobj, readMetadataToken &reader)
|
||||
| ILOpCode.Conv_ovf_i_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_i_un
|
||||
| ILOpCode.Conv_ovf_i1_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_i1_un
|
||||
| ILOpCode.Conv_ovf_i2_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_i2_un
|
||||
| ILOpCode.Conv_ovf_i4_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_i4_un
|
||||
| ILOpCode.Conv_ovf_i8_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_i8_un
|
||||
| ILOpCode.Conv_ovf_u_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_u_un
|
||||
| ILOpCode.Conv_ovf_u1_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_u1_un
|
||||
| ILOpCode.Conv_ovf_u2_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_u2_un
|
||||
| ILOpCode.Conv_ovf_u4_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_u4_un
|
||||
| ILOpCode.Conv_ovf_u8_un -> IlOp.Nullary NullaryIlOp.Conv_ovf_u8_un
|
||||
| ILOpCode.Box ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Box, readMetadataToken &reader)
|
||||
| ILOpCode.Newarr ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Newarr, readMetadataToken &reader)
|
||||
| ILOpCode.Ldlen -> IlOp.Nullary NullaryIlOp.LdLen
|
||||
| ILOpCode.Ldelema ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldelema, readMetadataToken &reader)
|
||||
| ILOpCode.Ldelem_i1 -> IlOp.Nullary NullaryIlOp.Ldelem_i1
|
||||
| ILOpCode.Ldelem_u1 -> IlOp.Nullary NullaryIlOp.Ldelem_u1
|
||||
| ILOpCode.Ldelem_i2 -> IlOp.Nullary NullaryIlOp.Ldelem_i2
|
||||
| ILOpCode.Ldelem_u2 -> IlOp.Nullary NullaryIlOp.Ldelem_u2
|
||||
| ILOpCode.Ldelem_i4 -> IlOp.Nullary NullaryIlOp.Ldelem_i4
|
||||
| ILOpCode.Ldelem_u4 -> IlOp.Nullary NullaryIlOp.Ldelem_u4
|
||||
| ILOpCode.Ldelem_i8 -> IlOp.Nullary NullaryIlOp.Ldelem_i8
|
||||
| ILOpCode.Ldelem_i -> IlOp.Nullary NullaryIlOp.Ldelem_i
|
||||
| ILOpCode.Ldelem_r4 -> IlOp.Nullary NullaryIlOp.Ldelem_r4
|
||||
| ILOpCode.Ldelem_r8 -> IlOp.Nullary NullaryIlOp.Ldelem_r8
|
||||
| ILOpCode.Ldelem_ref -> IlOp.Nullary NullaryIlOp.Ldelem_ref
|
||||
| ILOpCode.Stelem_i -> IlOp.Nullary NullaryIlOp.Stelem_i
|
||||
| ILOpCode.Stelem_i1 -> IlOp.Nullary NullaryIlOp.Stelem_i1
|
||||
| ILOpCode.Stelem_i2 -> IlOp.Nullary NullaryIlOp.Stelem_i2
|
||||
| ILOpCode.Stelem_i4 -> IlOp.Nullary NullaryIlOp.Stelem_i4
|
||||
| ILOpCode.Stelem_i8 -> IlOp.Nullary NullaryIlOp.Stelem_i8
|
||||
| ILOpCode.Stelem_r4 -> IlOp.Nullary NullaryIlOp.Stelem_r4
|
||||
| ILOpCode.Stelem_r8 -> IlOp.Nullary NullaryIlOp.Stelem_r8
|
||||
| ILOpCode.Stelem_ref -> IlOp.Nullary NullaryIlOp.Stelem_ref
|
||||
| ILOpCode.Ldelem ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldelem, readMetadataToken &reader)
|
||||
| ILOpCode.Stelem ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Stelem, readMetadataToken &reader)
|
||||
| ILOpCode.Unbox_any ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Unbox_Any, readMetadataToken &reader)
|
||||
| ILOpCode.Conv_ovf_i1 -> IlOp.Nullary NullaryIlOp.Conv_ovf_i1
|
||||
| ILOpCode.Conv_ovf_u1 -> IlOp.Nullary NullaryIlOp.Conv_ovf_u1
|
||||
| ILOpCode.Conv_ovf_i2 -> IlOp.Nullary NullaryIlOp.Conv_ovf_i2
|
||||
| ILOpCode.Conv_ovf_u2 -> IlOp.Nullary NullaryIlOp.Conv_ovf_u2
|
||||
| ILOpCode.Conv_ovf_i4 -> IlOp.Nullary NullaryIlOp.Conv_ovf_i4
|
||||
| ILOpCode.Conv_ovf_u4 -> IlOp.Nullary NullaryIlOp.Conv_ovf_u4
|
||||
| ILOpCode.Conv_ovf_i8 -> IlOp.Nullary NullaryIlOp.Conv_ovf_i8
|
||||
| ILOpCode.Conv_ovf_u8 -> IlOp.Nullary NullaryIlOp.Conv_ovf_u8
|
||||
| ILOpCode.Refanyval ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Refanyval, readMetadataToken &reader)
|
||||
| ILOpCode.Ckfinite -> IlOp.Nullary NullaryIlOp.Ckfinite
|
||||
| ILOpCode.Mkrefany ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Mkrefany, readMetadataToken &reader)
|
||||
| ILOpCode.Ldtoken ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldtoken, readMetadataToken &reader)
|
||||
| ILOpCode.Conv_u2 -> IlOp.Nullary NullaryIlOp.Conv_U2
|
||||
| ILOpCode.Conv_u1 -> IlOp.Nullary NullaryIlOp.Conv_U1
|
||||
| ILOpCode.Conv_i -> IlOp.Nullary NullaryIlOp.Conv_I
|
||||
| ILOpCode.Conv_ovf_i -> IlOp.Nullary NullaryIlOp.Conv_ovf_i
|
||||
| ILOpCode.Conv_ovf_u -> IlOp.Nullary NullaryIlOp.Conv_ovf_u
|
||||
| ILOpCode.Add_ovf -> IlOp.Nullary NullaryIlOp.Add_ovf
|
||||
| ILOpCode.Add_ovf_un -> IlOp.Nullary NullaryIlOp.Add_ovf_un
|
||||
| ILOpCode.Mul_ovf -> IlOp.Nullary NullaryIlOp.Mul_ovf
|
||||
| ILOpCode.Mul_ovf_un -> IlOp.Nullary NullaryIlOp.Mul_ovf_un
|
||||
| ILOpCode.Sub_ovf -> IlOp.Nullary NullaryIlOp.Sub_ovf
|
||||
| ILOpCode.Sub_ovf_un -> IlOp.Nullary NullaryIlOp.Sub_ovf_un
|
||||
| ILOpCode.Endfinally -> IlOp.Nullary NullaryIlOp.Endfinally
|
||||
| ILOpCode.Leave -> IlOp.UnaryConst (UnaryConstIlOp.Leave (reader.ReadInt32 ()))
|
||||
| ILOpCode.Leave_s -> IlOp.UnaryConst (UnaryConstIlOp.Leave_s (reader.ReadSByte ()))
|
||||
| ILOpCode.Stind_i -> IlOp.Nullary NullaryIlOp.Stind_I
|
||||
| ILOpCode.Conv_u -> IlOp.Nullary NullaryIlOp.Conv_U
|
||||
| ILOpCode.Arglist -> IlOp.Nullary NullaryIlOp.Arglist
|
||||
| ILOpCode.Ceq -> IlOp.Nullary NullaryIlOp.Ceq
|
||||
| ILOpCode.Cgt -> IlOp.Nullary NullaryIlOp.Cgt
|
||||
| ILOpCode.Cgt_un -> IlOp.Nullary NullaryIlOp.Cgt_un
|
||||
| ILOpCode.Clt -> IlOp.Nullary NullaryIlOp.Clt
|
||||
| ILOpCode.Clt_un -> IlOp.Nullary NullaryIlOp.Clt_un
|
||||
| ILOpCode.Ldftn ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldftn, readMetadataToken &reader)
|
||||
| ILOpCode.Ldvirtftn ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldvirtftn, readMetadataToken &reader)
|
||||
| ILOpCode.Ldarg -> IlOp.UnaryConst (UnaryConstIlOp.Ldarg (reader.ReadUInt16 ()))
|
||||
| ILOpCode.Ldarga -> IlOp.UnaryConst (UnaryConstIlOp.Ldarga (reader.ReadUInt16 ()))
|
||||
| ILOpCode.Starg -> IlOp.UnaryConst (UnaryConstIlOp.Starg (reader.ReadUInt16 ()))
|
||||
| ILOpCode.Ldloc -> IlOp.UnaryConst (UnaryConstIlOp.Ldloc (reader.ReadUInt16 ()))
|
||||
| ILOpCode.Ldloca -> IlOp.UnaryConst (UnaryConstIlOp.Ldloca (reader.ReadUInt16 ()))
|
||||
| ILOpCode.Stloc -> IlOp.UnaryConst (UnaryConstIlOp.Stloc (reader.ReadUInt16 ()))
|
||||
| ILOpCode.Localloc -> IlOp.Nullary NullaryIlOp.Localloc
|
||||
| ILOpCode.Endfilter -> IlOp.Nullary NullaryIlOp.Endfilter
|
||||
| ILOpCode.Unaligned -> IlOp.UnaryConst (UnaryConstIlOp.Unaligned (reader.ReadByte ()))
|
||||
| ILOpCode.Volatile -> IlOp.Nullary NullaryIlOp.Volatile
|
||||
| ILOpCode.Tail -> IlOp.Nullary NullaryIlOp.Tail
|
||||
| ILOpCode.Initobj ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Initobj, readMetadataToken &reader)
|
||||
| ILOpCode.Constrained ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Constrained, readMetadataToken &reader)
|
||||
| ILOpCode.Cpblk -> IlOp.Nullary NullaryIlOp.Cpblk
|
||||
| ILOpCode.Initblk -> IlOp.Nullary NullaryIlOp.Initblk
|
||||
| ILOpCode.Rethrow -> IlOp.Nullary NullaryIlOp.Rethrow
|
||||
| ILOpCode.Sizeof ->
|
||||
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Sizeof, readMetadataToken &reader)
|
||||
| ILOpCode.Refanytype -> IlOp.Nullary NullaryIlOp.Refanytype
|
||||
| ILOpCode.Readonly -> IlOp.Nullary NullaryIlOp.Readonly
|
||||
| i -> failwithf "Unknown opcode: %A" i
|
||||
|
||||
readInstructions ((opCode, offset) :: acc)
|
||||
|
||||
let instructions = readInstructions []
|
||||
|
||||
let er =
|
||||
methodBody.ExceptionRegions
|
||||
|> Seq.map ExceptionRegion.OfExceptionRegion
|
||||
|> ImmutableArray.CreateRange
|
||||
|
||||
{
|
||||
Instructions = instructions
|
||||
LocalInit = methodBody.LocalVariablesInitialized
|
||||
LocalSig = localSig
|
||||
MaxStackSize = methodBody.MaxStack
|
||||
ExceptionRegions = er
|
||||
}
|
||||
|> Some
|
||||
|
||||
let read
|
||||
(loggerFactory : ILoggerFactory)
|
||||
(peReader : PEReader)
|
||||
(metadataReader : MetadataReader)
|
||||
(methodHandle : MethodDefinitionHandle)
|
||||
: MethodInfo<FakeUnit> option
|
||||
=
|
||||
let logger = loggerFactory.CreateLogger "MethodInfo"
|
||||
let assemblyName = metadataReader.GetAssemblyDefinition().GetAssemblyName ()
|
||||
let methodDef = metadataReader.GetMethodDefinition methodHandle
|
||||
let methodName = metadataReader.GetString methodDef.Name
|
||||
let methodSig = methodDef.DecodeSignature (TypeDefn.typeProvider assemblyName, ())
|
||||
let implAttrs = methodDef.ImplAttributes
|
||||
|
||||
let methodBody =
|
||||
if
|
||||
implAttrs.HasFlag MethodImplAttributes.InternalCall
|
||||
|| implAttrs.HasFlag MethodImplAttributes.Runtime
|
||||
then
|
||||
None
|
||||
elif methodDef.Attributes.HasFlag MethodAttributes.PinvokeImpl then
|
||||
None
|
||||
else
|
||||
match readMethodBody peReader metadataReader assemblyName methodDef with
|
||||
| None ->
|
||||
logger.LogTrace $"no method body in {assemblyName.Name} {methodName}"
|
||||
None
|
||||
| Some body ->
|
||||
{
|
||||
MethodInstructions.Instructions = body.Instructions
|
||||
Locations = body.Instructions |> List.map (fun (a, b) -> b, a) |> Map.ofList
|
||||
LocalsInit = body.LocalInit
|
||||
LocalVars = body.LocalSig
|
||||
ExceptionRegions = body.ExceptionRegions
|
||||
}
|
||||
|> Some
|
||||
|
||||
let declaringType = methodDef.GetDeclaringType ()
|
||||
|
||||
let declaringTypeGenericParams =
|
||||
metadataReader.GetTypeDefinition(declaringType).GetGenericParameters().Count
|
||||
|
||||
let attrs =
|
||||
let result = ImmutableArray.CreateBuilder ()
|
||||
let attrs = methodDef.GetCustomAttributes ()
|
||||
|
||||
for attr in attrs do
|
||||
metadataReader.GetCustomAttribute attr
|
||||
|> CustomAttribute.make attr
|
||||
|> result.Add
|
||||
|
||||
result.ToImmutable ()
|
||||
|
||||
let typeSig = TypeMethodSignature.make methodSig
|
||||
|
||||
let methodParams = Parameter.readAll metadataReader (methodDef.GetParameters ())
|
||||
|
||||
let methodGenericParams =
|
||||
GenericParameter.readAll metadataReader (methodDef.GetGenericParameters ())
|
||||
|
||||
{
|
||||
DeclaringType = ConcreteType.make' assemblyName declaringType declaringTypeGenericParams
|
||||
Handle = methodHandle
|
||||
Name = methodName
|
||||
Instructions = methodBody
|
||||
Parameters = methodParams
|
||||
Generics = methodGenericParams
|
||||
Signature = typeSig
|
||||
MethodAttributes = methodDef.Attributes
|
||||
CustomAttributes = attrs
|
||||
IsStatic = not methodSig.Header.IsInstance
|
||||
ImplAttributes = implAttrs
|
||||
}
|
||||
|> Some
|
||||
|
||||
let rec resolveBaseType
|
||||
(methodGenerics : TypeDefn ImmutableArray option)
|
||||
(executingMethod : MethodInfo<TypeDefn>)
|
||||
(td : TypeDefn)
|
||||
: ResolvedBaseType
|
||||
=
|
||||
match td with
|
||||
| TypeDefn.Void -> failwith "Void isn't a type that appears at runtime and has no base type"
|
||||
| TypeDefn.PrimitiveType ty ->
|
||||
match ty with
|
||||
| PrimitiveType.SByte
|
||||
| PrimitiveType.Byte
|
||||
| PrimitiveType.Int16
|
||||
| PrimitiveType.UInt16
|
||||
| PrimitiveType.Int32
|
||||
| PrimitiveType.UInt32
|
||||
| PrimitiveType.Int64
|
||||
| PrimitiveType.UInt64
|
||||
| PrimitiveType.Single
|
||||
| PrimitiveType.Double
|
||||
| PrimitiveType.Char
|
||||
| PrimitiveType.Boolean -> ResolvedBaseType.ValueType
|
||||
| PrimitiveType.String -> ResolvedBaseType.Object
|
||||
| PrimitiveType.TypedReference -> failwith "todo"
|
||||
| PrimitiveType.IntPtr -> failwith "todo"
|
||||
| PrimitiveType.UIntPtr -> failwith "todo"
|
||||
| PrimitiveType.Object -> failwith "todo"
|
||||
| TypeDefn.Array (elt, shape) -> failwith "todo"
|
||||
| TypeDefn.Pinned typeDefn -> failwith "todo"
|
||||
| TypeDefn.Pointer typeDefn -> failwith "todo"
|
||||
| TypeDefn.Byref typeDefn -> failwith "todo"
|
||||
| TypeDefn.OneDimensionalArrayLowerBoundZero elements -> failwith "todo"
|
||||
| TypeDefn.Modified (original, afterMod, modificationRequired) -> failwith "todo"
|
||||
| TypeDefn.FromReference (typeRef, signatureTypeKind) -> failwith "todo"
|
||||
| TypeDefn.FromDefinition (comparableTypeDefinitionHandle, _, signatureTypeKind) -> failwith "todo"
|
||||
| TypeDefn.GenericInstantiation (generic, args) -> failwith "todo"
|
||||
| TypeDefn.FunctionPointer typeMethodSignature -> failwith "todo"
|
||||
| TypeDefn.GenericTypeParameter index ->
|
||||
resolveBaseType methodGenerics executingMethod executingMethod.DeclaringType.Generics.[index]
|
||||
| TypeDefn.GenericMethodParameter index ->
|
||||
match methodGenerics with
|
||||
| None -> failwith "unexpectedly asked for a generic method parameter when we had none"
|
||||
| Some generics -> resolveBaseType methodGenerics executingMethod generics.[index]
|
30
WoofWare.PawPrint.Domain/MethodSpec.fs
Normal file
30
WoofWare.PawPrint.Domain/MethodSpec.fs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System.Collections.Immutable
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
|
||||
/// <summary>
|
||||
/// Represents a method specification, which provides information about a method,
|
||||
/// particularly for generic method instantiations.
|
||||
/// </summary>
|
||||
type MethodSpec =
|
||||
{
|
||||
/// <summary>
|
||||
/// The token that identifies the method being specialized.
|
||||
/// </summary>
|
||||
Method : MetadataToken
|
||||
|
||||
Signature : TypeDefn ImmutableArray
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module MethodSpec =
|
||||
let make (assemblyName : AssemblyName) (p : MethodSpecification) : MethodSpec =
|
||||
let signature = p.DecodeSignature (TypeDefn.typeProvider assemblyName, ())
|
||||
|
||||
{
|
||||
// Horrible abuse to get this as an int
|
||||
Method = MetadataToken.ofInt (p.Method.GetHashCode ())
|
||||
Signature = signature
|
||||
}
|
39
WoofWare.PawPrint.Domain/Namespace.fs
Normal file
39
WoofWare.PawPrint.Domain/Namespace.fs
Normal file
@@ -0,0 +1,39 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System.Collections.Immutable
|
||||
open System.Reflection.Metadata
|
||||
|
||||
type Namespace =
|
||||
{
|
||||
PrettyName : string
|
||||
Parent : NamespaceDefinitionHandle
|
||||
TypeDefinitions : ImmutableArray<TypeDefinitionHandle>
|
||||
ExportedTypes : ImmutableArray<ExportedTypeHandle>
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Namespace =
|
||||
/// Returns also the children.
|
||||
let make
|
||||
(getString : StringHandle -> string)
|
||||
(getNamespace : NamespaceDefinitionHandle -> NamespaceDefinition)
|
||||
(ns : NamespaceDefinition)
|
||||
: Namespace * ImmutableDictionary<string list, Namespace>
|
||||
=
|
||||
let children = ImmutableDictionary.CreateBuilder ()
|
||||
|
||||
let rec inner (path : string list) (ns : NamespaceDefinition) : Namespace =
|
||||
for child in ns.NamespaceDefinitions do
|
||||
let rendered = getNamespace child
|
||||
let location = getString rendered.Name :: path
|
||||
children.Add (List.rev location, inner location rendered)
|
||||
|
||||
{
|
||||
PrettyName = getString ns.Name
|
||||
Parent = ns.Parent
|
||||
TypeDefinitions = ns.TypeDefinitions
|
||||
ExportedTypes = ns.ExportedTypes
|
||||
}
|
||||
|
||||
let result = inner [] ns
|
||||
result, children.ToImmutable ()
|
16
WoofWare.PawPrint.Domain/StringToken.fs
Normal file
16
WoofWare.PawPrint.Domain/StringToken.fs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System.Reflection.Metadata
|
||||
open System.Reflection.Metadata.Ecma335
|
||||
|
||||
type StringToken =
|
||||
| UserString of UserStringHandle
|
||||
| String of StringHandle
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module StringToken =
|
||||
let ofInt (value : int) : StringToken =
|
||||
match LanguagePrimitives.EnumOfValue<byte, HandleKind> (byte (value &&& 0xFF000000 >>> 24)) with
|
||||
| HandleKind.UserString -> StringToken.UserString (MetadataTokens.UserStringHandle value)
|
||||
| HandleKind.String -> StringToken.String (MetadataTokens.StringHandle value)
|
||||
| v -> failwith $"Unrecognised string handle kind: {v}"
|
128
WoofWare.PawPrint.Domain/Tokens.fs
Normal file
128
WoofWare.PawPrint.Domain/Tokens.fs
Normal file
@@ -0,0 +1,128 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System.Reflection.Metadata
|
||||
open System.Reflection.Metadata.Ecma335
|
||||
|
||||
/// <summary>
|
||||
/// Represents a strongly-typed metadata token which can reference various elements in the assembly metadata.
|
||||
/// This discriminated union provides type-safe handling of metadata tokens with specific handle types.
|
||||
/// </summary>
|
||||
type MetadataToken =
|
||||
/// <summary>Method implementation token, specifying how a virtual method is implemented.</summary>
|
||||
| MethodImplementation of MethodImplementationHandle
|
||||
/// <summary>Method definition token, identifying a method defined in this assembly.</summary>
|
||||
| MethodDef of MethodDefinitionHandle
|
||||
/// <summary>Method specification token, typically for generic method instantiations.</summary>
|
||||
| MethodSpecification of MethodSpecificationHandle
|
||||
/// <summary>Member reference token, for references to fields or methods in other modules/assemblies.</summary>
|
||||
| MemberReference of MemberReferenceHandle
|
||||
/// <summary>Type reference token, for references to types in other modules/assemblies.</summary>
|
||||
| TypeReference of TypeReferenceHandle
|
||||
/// <summary>Assembly reference token, identifying an external assembly.</summary>
|
||||
| AssemblyReference of AssemblyReferenceHandle
|
||||
/// <summary>Type specification token, for representing complex types like generic instantiations.</summary>
|
||||
| TypeSpecification of TypeSpecificationHandle
|
||||
/// <summary>Type definition token, identifying a type defined in this assembly.</summary>
|
||||
| TypeDefinition of TypeDefinitionHandle
|
||||
/// <summary>Field definition token, identifying a field defined in this assembly.</summary>
|
||||
| FieldDefinition of FieldDefinitionHandle
|
||||
/// <summary>Parameter token, identifying a parameter of a method.</summary>
|
||||
| Parameter of ParameterHandle
|
||||
/// <summary>Interface implementation token, mapping an implementation to an interface method.</summary>
|
||||
| InterfaceImplementation of InterfaceImplementationHandle
|
||||
/// <summary>Exported type token, identifying a type exported from this assembly.</summary>
|
||||
| ExportedType of ExportedTypeHandle
|
||||
/// <summary>Standalone signature token, for method signatures not attached to any method.</summary>
|
||||
| StandaloneSignature of StandaloneSignatureHandle
|
||||
/// <summary>Event definition token, identifying an event defined in this assembly.</summary>
|
||||
| EventDefinition of EventDefinitionHandle
|
||||
/// <summary>Constant token, representing a constant value stored in metadata.</summary>
|
||||
| Constant of ConstantHandle
|
||||
/// <summary>Custom attribute token, identifying an attribute applied to a metadata element.</summary>
|
||||
| CustomAttribute of CustomAttributeHandle
|
||||
/// <summary>Security attribute token, for declarative security attributes.</summary>
|
||||
| DeclarativeSecurityAttribute of DeclarativeSecurityAttributeHandle
|
||||
/// <summary>Property definition token, identifying a property defined in this assembly.</summary>
|
||||
| PropertyDefinition of PropertyDefinitionHandle
|
||||
/// <summary>Module reference token, for references to other modules in the same assembly.</summary>
|
||||
| ModuleReference of ModuleReferenceHandle
|
||||
/// <summary>Assembly file token, identifying a file that is part of this assembly.</summary>
|
||||
| AssemblyFile of AssemblyFileHandle
|
||||
/// <summary>Manifest resource token, identifying a resource embedded in this assembly.</summary>
|
||||
| ManifestResource of ManifestResourceHandle
|
||||
/// <summary>Generic parameter token, identifying a generic type or method parameter.</summary>
|
||||
| GenericParameter of GenericParameterHandle
|
||||
/// <summary>Generic parameter constraint token, identifying a constraint on a generic parameter.</summary>
|
||||
| GenericParameterConstraint of GenericParameterConstraintHandle
|
||||
/// <summary>Document token, used in debugging information.</summary>
|
||||
| Document of DocumentHandle
|
||||
/// <summary>Method debug information token, for debugging metadata about a method.</summary>
|
||||
| MethodDebugInformation of MethodDebugInformationHandle
|
||||
/// <summary>Local scope token, identifying a scope within a method body.</summary>
|
||||
| LocalScope of LocalScopeHandle
|
||||
/// <summary>Local variable token, identifying a local variable in a method.</summary>
|
||||
| LocalVariable of LocalVariableHandle
|
||||
/// <summary>Local constant token, identifying a local constant in a method.</summary>
|
||||
| LocalConstant of LocalConstantHandle
|
||||
/// <summary>Import scope token, used in debugging information for namespace imports.</summary>
|
||||
| ImportScope of ImportScopeHandle
|
||||
/// <summary>Custom debug information token, for user-defined debugging metadata.</summary>
|
||||
| CustomDebugInformation of CustomDebugInformationHandle
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module MetadataToken =
|
||||
let ofInt (value : int32) : MetadataToken =
|
||||
let asRowNum = value &&& 0x00FFFFFF
|
||||
|
||||
match LanguagePrimitives.EnumOfValue<byte, HandleKind> (byte (value &&& 0xFF000000 >>> 24)) with
|
||||
| HandleKind.ModuleDefinition -> failwith "TODO"
|
||||
| HandleKind.TypeReference -> MetadataToken.TypeReference (MetadataTokens.TypeReferenceHandle asRowNum)
|
||||
| HandleKind.TypeDefinition -> MetadataToken.TypeDefinition (MetadataTokens.TypeDefinitionHandle asRowNum)
|
||||
| HandleKind.FieldDefinition -> MetadataToken.FieldDefinition (MetadataTokens.FieldDefinitionHandle asRowNum)
|
||||
| HandleKind.MethodDefinition -> MetadataToken.MethodDef (MetadataTokens.MethodDefinitionHandle asRowNum)
|
||||
| HandleKind.Parameter -> MetadataToken.Parameter (MetadataTokens.ParameterHandle asRowNum)
|
||||
| HandleKind.InterfaceImplementation ->
|
||||
MetadataToken.InterfaceImplementation (MetadataTokens.InterfaceImplementationHandle asRowNum)
|
||||
| HandleKind.MemberReference -> MetadataToken.MemberReference (MetadataTokens.MemberReferenceHandle asRowNum)
|
||||
| HandleKind.Constant -> MetadataToken.Constant (MetadataTokens.ConstantHandle asRowNum)
|
||||
| HandleKind.CustomAttribute -> MetadataToken.CustomAttribute (MetadataTokens.CustomAttributeHandle asRowNum)
|
||||
| HandleKind.DeclarativeSecurityAttribute ->
|
||||
MetadataToken.DeclarativeSecurityAttribute (MetadataTokens.DeclarativeSecurityAttributeHandle asRowNum)
|
||||
| HandleKind.StandaloneSignature ->
|
||||
MetadataToken.StandaloneSignature (MetadataTokens.StandaloneSignatureHandle asRowNum)
|
||||
| HandleKind.EventDefinition -> MetadataToken.EventDefinition (MetadataTokens.EventDefinitionHandle asRowNum)
|
||||
| HandleKind.PropertyDefinition ->
|
||||
MetadataToken.PropertyDefinition (MetadataTokens.PropertyDefinitionHandle asRowNum)
|
||||
| HandleKind.MethodImplementation ->
|
||||
MetadataToken.MethodImplementation (MetadataTokens.MethodImplementationHandle asRowNum)
|
||||
| HandleKind.ModuleReference -> MetadataToken.ModuleReference (MetadataTokens.ModuleReferenceHandle asRowNum)
|
||||
| HandleKind.TypeSpecification ->
|
||||
MetadataToken.TypeSpecification (MetadataTokens.TypeSpecificationHandle asRowNum)
|
||||
| HandleKind.AssemblyDefinition -> failwith "TODO"
|
||||
| HandleKind.AssemblyReference ->
|
||||
MetadataToken.AssemblyReference (MetadataTokens.AssemblyReferenceHandle asRowNum)
|
||||
| HandleKind.AssemblyFile -> MetadataToken.AssemblyFile (MetadataTokens.AssemblyFileHandle asRowNum)
|
||||
| HandleKind.ExportedType -> MetadataToken.ExportedType (MetadataTokens.ExportedTypeHandle asRowNum)
|
||||
| HandleKind.ManifestResource -> MetadataToken.ManifestResource (MetadataTokens.ManifestResourceHandle asRowNum)
|
||||
| HandleKind.GenericParameter -> MetadataToken.GenericParameter (MetadataTokens.GenericParameterHandle asRowNum)
|
||||
| HandleKind.MethodSpecification ->
|
||||
MetadataToken.MethodSpecification (MetadataTokens.MethodSpecificationHandle asRowNum)
|
||||
| HandleKind.GenericParameterConstraint ->
|
||||
MetadataToken.GenericParameterConstraint (MetadataTokens.GenericParameterConstraintHandle asRowNum)
|
||||
| HandleKind.Document -> MetadataToken.Document (MetadataTokens.DocumentHandle asRowNum)
|
||||
| HandleKind.MethodDebugInformation ->
|
||||
MetadataToken.MethodDebugInformation (MetadataTokens.MethodDebugInformationHandle asRowNum)
|
||||
| HandleKind.LocalScope -> MetadataToken.LocalScope (MetadataTokens.LocalScopeHandle asRowNum)
|
||||
| HandleKind.LocalVariable -> MetadataToken.LocalVariable (MetadataTokens.LocalVariableHandle asRowNum)
|
||||
| HandleKind.LocalConstant -> MetadataToken.LocalConstant (MetadataTokens.LocalConstantHandle asRowNum)
|
||||
| HandleKind.ImportScope -> MetadataToken.ImportScope (MetadataTokens.ImportScopeHandle asRowNum)
|
||||
| HandleKind.CustomDebugInformation ->
|
||||
MetadataToken.CustomDebugInformation (MetadataTokens.CustomDebugInformationHandle asRowNum)
|
||||
| HandleKind.UserString -> failwith "TODO"
|
||||
| HandleKind.Blob -> failwith "TODO"
|
||||
| HandleKind.Guid -> failwith "TODO"
|
||||
| HandleKind.String -> failwith "TODO"
|
||||
| HandleKind.NamespaceDefinition -> failwith "TODO"
|
||||
| h -> failwith $"Unrecognised kind: {h}"
|
||||
|
||||
let ofEntityHandle (eh : EntityHandle) : MetadataToken = ofInt (eh.GetHashCode ())
|
230
WoofWare.PawPrint.Domain/TypeDefn.fs
Normal file
230
WoofWare.PawPrint.Domain/TypeDefn.fs
Normal file
@@ -0,0 +1,230 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System.Collections.Immutable
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
open System.Reflection.Metadata.Ecma335
|
||||
open Microsoft.FSharp.Core
|
||||
|
||||
type ResolvedBaseType =
|
||||
| Enum
|
||||
| ValueType
|
||||
| Object
|
||||
| Delegate
|
||||
|
||||
/// <summary>
|
||||
/// Represents a method signature with type parameters.
|
||||
/// Corresponds to MethodSignature in System.Reflection.Metadata.
|
||||
/// </summary>
|
||||
type TypeMethodSignature<'Types> =
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains calling convention and other method attributes encoded in the metadata.
|
||||
/// </summary>
|
||||
Header : ComparableSignatureHeader
|
||||
|
||||
/// <summary>
|
||||
/// The types of all parameters of the method.
|
||||
/// </summary>
|
||||
ParameterTypes : 'Types list
|
||||
|
||||
/// <summary>
|
||||
/// The number of generic type parameters defined by this method.
|
||||
/// </summary>
|
||||
GenericParameterCount : int
|
||||
|
||||
/// <summary>
|
||||
/// The number of required parameters (non-optional parameters).
|
||||
/// </summary>
|
||||
RequiredParameterCount : int
|
||||
|
||||
/// <summary>
|
||||
/// The return type of the method.
|
||||
/// </summary>
|
||||
ReturnType : 'Types
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module TypeMethodSignature =
|
||||
let make<'T> (p : MethodSignature<'T>) : TypeMethodSignature<'T> =
|
||||
{
|
||||
Header = ComparableSignatureHeader.Make p.Header
|
||||
ReturnType = p.ReturnType
|
||||
ParameterTypes = List.ofSeq p.ParameterTypes
|
||||
GenericParameterCount = p.GenericParameterCount
|
||||
RequiredParameterCount = p.RequiredParameterCount
|
||||
}
|
||||
|
||||
/// See I.8.2.2
|
||||
type PrimitiveType =
|
||||
| Boolean
|
||||
| Char
|
||||
| SByte
|
||||
| Byte
|
||||
| Int16
|
||||
| UInt16
|
||||
| Int32
|
||||
| UInt32
|
||||
| Int64
|
||||
| UInt64
|
||||
| Single
|
||||
| Double
|
||||
| String
|
||||
| TypedReference
|
||||
| IntPtr
|
||||
| UIntPtr
|
||||
| Object
|
||||
|
||||
static member OfEnum (ptc : PrimitiveTypeCode) : PrimitiveType option =
|
||||
match ptc with
|
||||
| PrimitiveTypeCode.Void -> None
|
||||
| PrimitiveTypeCode.Boolean -> PrimitiveType.Boolean |> Some
|
||||
| PrimitiveTypeCode.Char -> PrimitiveType.Char |> Some
|
||||
| PrimitiveTypeCode.SByte -> PrimitiveType.SByte |> Some
|
||||
| PrimitiveTypeCode.Byte -> PrimitiveType.Byte |> Some
|
||||
| PrimitiveTypeCode.Int16 -> PrimitiveType.Int16 |> Some
|
||||
| PrimitiveTypeCode.UInt16 -> PrimitiveType.UInt16 |> Some
|
||||
| PrimitiveTypeCode.Int32 -> PrimitiveType.Int32 |> Some
|
||||
| PrimitiveTypeCode.UInt32 -> PrimitiveType.UInt32 |> Some
|
||||
| PrimitiveTypeCode.Int64 -> PrimitiveType.Int64 |> Some
|
||||
| PrimitiveTypeCode.UInt64 -> PrimitiveType.UInt64 |> Some
|
||||
| PrimitiveTypeCode.Single -> PrimitiveType.Single |> Some
|
||||
| PrimitiveTypeCode.Double -> PrimitiveType.Double |> Some
|
||||
| PrimitiveTypeCode.String -> PrimitiveType.String |> Some
|
||||
| PrimitiveTypeCode.TypedReference -> PrimitiveType.TypedReference |> Some
|
||||
| PrimitiveTypeCode.IntPtr -> PrimitiveType.IntPtr |> Some
|
||||
| PrimitiveTypeCode.UIntPtr -> PrimitiveType.UIntPtr |> Some
|
||||
| PrimitiveTypeCode.Object -> PrimitiveType.Object |> Some
|
||||
| x -> failwithf $"Unrecognised primitive type code: %O{x}"
|
||||
|
||||
type TypeDefn =
|
||||
| PrimitiveType of PrimitiveType
|
||||
// TODO: array shapes
|
||||
| Array of elt : TypeDefn * shape : unit
|
||||
| Pinned of TypeDefn
|
||||
| Pointer of TypeDefn
|
||||
| Byref of TypeDefn
|
||||
| OneDimensionalArrayLowerBoundZero of elements : TypeDefn
|
||||
| Modified of original : TypeDefn * afterMod : TypeDefn * modificationRequired : bool
|
||||
| FromReference of TypeRef * SignatureTypeKind
|
||||
| FromDefinition of ComparableTypeDefinitionHandle * assemblyFullName : string * SignatureTypeKind
|
||||
| GenericInstantiation of generic : TypeDefn * args : ImmutableArray<TypeDefn>
|
||||
| FunctionPointer of TypeMethodSignature<TypeDefn>
|
||||
| GenericTypeParameter of index : int
|
||||
| GenericMethodParameter of index : int
|
||||
/// Not really a type: this indicates the *absence* of a return value.
|
||||
| Void
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module TypeDefn =
|
||||
let isManaged (typeDefn : TypeDefn) : bool =
|
||||
match typeDefn with
|
||||
| TypeDefn.PrimitiveType primitiveType -> failwith "todo"
|
||||
| TypeDefn.Array (elt, shape) -> failwith "todo"
|
||||
| TypeDefn.Pinned typeDefn -> failwith "todo"
|
||||
| TypeDefn.Pointer typeDefn -> failwith "todo"
|
||||
| TypeDefn.Byref typeDefn -> failwith "todo"
|
||||
| TypeDefn.OneDimensionalArrayLowerBoundZero elements -> failwith "todo"
|
||||
| TypeDefn.Modified (original, afterMod, modificationRequired) -> failwith "todo"
|
||||
| TypeDefn.FromReference _ -> true
|
||||
| TypeDefn.FromDefinition (_, _, signatureTypeKind) ->
|
||||
match signatureTypeKind with
|
||||
| SignatureTypeKind.Unknown -> failwith "todo"
|
||||
| SignatureTypeKind.ValueType -> false
|
||||
| SignatureTypeKind.Class -> true
|
||||
| s -> raise (System.ArgumentOutOfRangeException ())
|
||||
| TypeDefn.GenericInstantiation (generic, args) -> failwith "todo"
|
||||
| TypeDefn.FunctionPointer typeMethodSignature -> failwith "todo"
|
||||
| TypeDefn.GenericTypeParameter index -> failwith "todo"
|
||||
| TypeDefn.GenericMethodParameter index -> failwith "todo"
|
||||
| TypeDefn.Void -> false
|
||||
|
||||
let fromTypeCode (s : SignatureTypeCode) : TypeDefn =
|
||||
match s with
|
||||
| SignatureTypeCode.Invalid -> failwith "todo"
|
||||
| SignatureTypeCode.Void -> TypeDefn.Void
|
||||
| SignatureTypeCode.Boolean -> TypeDefn.PrimitiveType PrimitiveType.Boolean
|
||||
| SignatureTypeCode.Char -> TypeDefn.PrimitiveType PrimitiveType.Char
|
||||
| SignatureTypeCode.SByte -> TypeDefn.PrimitiveType PrimitiveType.SByte
|
||||
| SignatureTypeCode.Byte -> TypeDefn.PrimitiveType PrimitiveType.Byte
|
||||
| SignatureTypeCode.Int16 -> TypeDefn.PrimitiveType PrimitiveType.Int16
|
||||
| SignatureTypeCode.UInt16 -> TypeDefn.PrimitiveType PrimitiveType.UInt16
|
||||
| SignatureTypeCode.Int32 -> TypeDefn.PrimitiveType PrimitiveType.Int32
|
||||
| SignatureTypeCode.UInt32 -> TypeDefn.PrimitiveType PrimitiveType.UInt32
|
||||
| SignatureTypeCode.Int64 -> TypeDefn.PrimitiveType PrimitiveType.Int64
|
||||
| SignatureTypeCode.UInt64 -> TypeDefn.PrimitiveType PrimitiveType.UInt64
|
||||
| SignatureTypeCode.Single -> TypeDefn.PrimitiveType PrimitiveType.Single
|
||||
| SignatureTypeCode.Double -> TypeDefn.PrimitiveType PrimitiveType.Double
|
||||
| SignatureTypeCode.String -> TypeDefn.PrimitiveType PrimitiveType.String
|
||||
| SignatureTypeCode.Pointer -> failwith "todo"
|
||||
| SignatureTypeCode.ByReference -> failwith "TODO"
|
||||
| SignatureTypeCode.GenericTypeParameter -> failwith "todo"
|
||||
| SignatureTypeCode.Array -> failwith "todo"
|
||||
| SignatureTypeCode.GenericTypeInstance -> failwith "todo"
|
||||
| SignatureTypeCode.TypedReference -> TypeDefn.PrimitiveType PrimitiveType.TypedReference
|
||||
| SignatureTypeCode.IntPtr -> TypeDefn.PrimitiveType PrimitiveType.IntPtr
|
||||
| SignatureTypeCode.UIntPtr -> failwith "todo"
|
||||
| SignatureTypeCode.FunctionPointer -> failwith "todo"
|
||||
| SignatureTypeCode.Object -> failwith "todo"
|
||||
| SignatureTypeCode.SZArray -> failwith "todo"
|
||||
| SignatureTypeCode.GenericMethodParameter -> failwith "todo"
|
||||
| SignatureTypeCode.RequiredModifier -> failwith "todo"
|
||||
| SignatureTypeCode.OptionalModifier -> failwith "todo"
|
||||
| SignatureTypeCode.TypeHandle -> failwith "todo"
|
||||
| SignatureTypeCode.Sentinel -> failwith "todo"
|
||||
| SignatureTypeCode.Pinned -> failwith "todo"
|
||||
| x -> failwith $"Unrecognised type code: {x}"
|
||||
|
||||
let typeProvider (a : AssemblyName) : ISignatureTypeProvider<TypeDefn, unit> =
|
||||
{ new ISignatureTypeProvider<TypeDefn, unit> with
|
||||
member this.GetArrayType (elementType : TypeDefn, shape : ArrayShape) : TypeDefn =
|
||||
TypeDefn.Array (elementType, ())
|
||||
|
||||
member this.GetByReferenceType (elementType : TypeDefn) : TypeDefn = TypeDefn.Byref elementType
|
||||
|
||||
member this.GetSZArrayType (elementType : TypeDefn) : TypeDefn =
|
||||
TypeDefn.OneDimensionalArrayLowerBoundZero elementType
|
||||
|
||||
member this.GetPrimitiveType (elementType : PrimitiveTypeCode) : TypeDefn =
|
||||
match PrimitiveType.OfEnum elementType with
|
||||
| None -> TypeDefn.Void
|
||||
| Some v -> TypeDefn.PrimitiveType v
|
||||
|
||||
member this.GetGenericInstantiation
|
||||
(generic : TypeDefn, typeArguments : ImmutableArray<TypeDefn>)
|
||||
: TypeDefn
|
||||
=
|
||||
TypeDefn.GenericInstantiation (generic, typeArguments)
|
||||
|
||||
member this.GetTypeFromDefinition
|
||||
(reader : MetadataReader, handle : TypeDefinitionHandle, rawTypeKind : byte)
|
||||
: TypeDefn
|
||||
=
|
||||
let handle' : EntityHandle = TypeDefinitionHandle.op_Implicit handle
|
||||
let typeKind = reader.ResolveSignatureTypeKind (handle', rawTypeKind)
|
||||
|
||||
TypeDefn.FromDefinition (ComparableTypeDefinitionHandle.Make handle, a.FullName, typeKind)
|
||||
|
||||
member this.GetTypeFromReference
|
||||
(reader : MetadataReader, handle : TypeReferenceHandle, rawTypeKind : byte)
|
||||
: TypeDefn
|
||||
=
|
||||
let handle' : EntityHandle = TypeReferenceHandle.op_Implicit handle
|
||||
let ref = handle |> TypeRef.make reader
|
||||
let typeKind = reader.ResolveSignatureTypeKind (handle', rawTypeKind)
|
||||
TypeDefn.FromReference (ref, typeKind)
|
||||
|
||||
member this.GetPointerType (typeCode : TypeDefn) : TypeDefn = TypeDefn.Pointer typeCode
|
||||
|
||||
member this.GetFunctionPointerType signature =
|
||||
TypeDefn.FunctionPointer (TypeMethodSignature.make signature)
|
||||
|
||||
member this.GetGenericMethodParameter (genericContext, index) = TypeDefn.GenericMethodParameter index
|
||||
member this.GetGenericTypeParameter (genericContext, index) = TypeDefn.GenericTypeParameter index
|
||||
|
||||
member this.GetModifiedType (modifier, unmodifiedType, isRequired) =
|
||||
TypeDefn.Modified (unmodifiedType, modifier, isRequired)
|
||||
|
||||
member this.GetPinnedType elementType = TypeDefn.Pinned elementType
|
||||
member this.GetTypeFromSpecification (reader, genericContext, handle, rawTypeKind) = failwith "todo"
|
||||
}
|
278
WoofWare.PawPrint.Domain/TypeInfo.fs
Normal file
278
WoofWare.PawPrint.Domain/TypeInfo.fs
Normal file
@@ -0,0 +1,278 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System.Collections.Generic
|
||||
open System.Collections.Immutable
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
open System.Reflection.PortableExecutable
|
||||
open Microsoft.Extensions.Logging
|
||||
open Microsoft.FSharp.Core
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
type BaseTypeInfo =
|
||||
| TypeDef of TypeDefinitionHandle
|
||||
| TypeRef of TypeReferenceHandle
|
||||
| TypeSpec of TypeSpecificationHandle
|
||||
| ForeignAssemblyType of assemblyName : AssemblyName * TypeDefinitionHandle
|
||||
|
||||
type MethodImplParsed =
|
||||
| MethodImplementation of MethodImplementationHandle
|
||||
| MethodDefinition of MethodDefinitionHandle
|
||||
|
||||
/// <summary>
|
||||
/// Represents detailed information about a type definition in a .NET assembly.
|
||||
/// This is a strongly-typed representation of TypeDefinition from System.Reflection.Metadata.
|
||||
/// </summary>
|
||||
type TypeInfo<'generic> =
|
||||
{
|
||||
/// <summary>The namespace containing the type.</summary>
|
||||
Namespace : string
|
||||
|
||||
/// <summary>The name of the type.</summary>
|
||||
Name : string
|
||||
|
||||
/// <summary>
|
||||
/// All methods defined within this type.
|
||||
/// </summary>
|
||||
Methods : WoofWare.PawPrint.MethodInfo<FakeUnit> list
|
||||
|
||||
/// <summary>
|
||||
/// Method implementation mappings for this type, often used for interface implementations
|
||||
/// or overriding virtual methods from base classes.
|
||||
/// </summary>
|
||||
MethodImpls : ImmutableDictionary<MethodImplementationHandle, MethodImplParsed>
|
||||
|
||||
/// <summary>
|
||||
/// Fields defined in this type.
|
||||
/// </summary>
|
||||
Fields : WoofWare.PawPrint.FieldInfo<FakeUnit> list
|
||||
|
||||
/// <summary>
|
||||
/// The base type that this type inherits from, or None for types that don't have a base type
|
||||
/// (like System.Object).
|
||||
///
|
||||
/// Value types inherit *directly* from System.ValueType; enums directly from System.Enum.
|
||||
/// </summary>
|
||||
BaseType : BaseTypeInfo option
|
||||
|
||||
/// <summary>
|
||||
/// Attributes applied to this type, such as visibility, inheritance characteristics,
|
||||
/// special handling, and other flags.
|
||||
/// </summary>
|
||||
TypeAttributes : TypeAttributes
|
||||
|
||||
/// <summary>
|
||||
/// Custom attributes applied to this type.
|
||||
/// </summary>
|
||||
Attributes : WoofWare.PawPrint.CustomAttribute list
|
||||
|
||||
/// <summary>
|
||||
/// The metadata token handle that uniquely identifies this type in the assembly.
|
||||
/// </summary>
|
||||
TypeDefHandle : TypeDefinitionHandle
|
||||
|
||||
/// <summary>
|
||||
/// The assembly in which this type is defined.
|
||||
/// </summary>
|
||||
Assembly : AssemblyName
|
||||
|
||||
Generics : 'generic ImmutableArray
|
||||
|
||||
Events : EventDefn ImmutableArray
|
||||
}
|
||||
|
||||
type TypeInfoEval<'ret> =
|
||||
abstract Eval<'a> : TypeInfo<'a> -> 'ret
|
||||
|
||||
type TypeInfoCrate =
|
||||
abstract Apply<'ret> : TypeInfoEval<'ret> -> 'ret
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module TypeInfoCrate =
|
||||
let make<'a> (t : TypeInfo<'a>) =
|
||||
{ new TypeInfoCrate with
|
||||
member _.Apply e = e.Eval t
|
||||
}
|
||||
|
||||
type BaseClassTypes<'corelib> =
|
||||
{
|
||||
Corelib : 'corelib
|
||||
String : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
Boolean : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
Char : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
SByte : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
Byte : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
Int16 : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
UInt16 : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
Int32 : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
UInt32 : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
Int64 : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
UInt64 : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
Single : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
Double : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
Array : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
Enum : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
ValueType : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
Object : TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module TypeInfo =
|
||||
let withGenerics<'a, 'b> (gen : 'b ImmutableArray) (t : TypeInfo<'a>) : TypeInfo<'b> =
|
||||
{
|
||||
Namespace = t.Namespace
|
||||
Name = t.Name
|
||||
Methods = t.Methods
|
||||
MethodImpls = t.MethodImpls
|
||||
Fields = t.Fields
|
||||
BaseType = t.BaseType
|
||||
TypeAttributes = t.TypeAttributes
|
||||
Attributes = t.Attributes
|
||||
TypeDefHandle = t.TypeDefHandle
|
||||
Assembly = t.Assembly
|
||||
Generics = gen
|
||||
Events = t.Events
|
||||
}
|
||||
|
||||
let mapGeneric<'a, 'b> (f : int -> 'a -> 'b) (t : TypeInfo<'a>) : TypeInfo<'b> =
|
||||
withGenerics (t.Generics |> Seq.mapi f |> ImmutableArray.CreateRange) t
|
||||
|
||||
let internal read
|
||||
(loggerFactory : ILoggerFactory)
|
||||
(peReader : PEReader)
|
||||
(thisAssembly : AssemblyName)
|
||||
(metadataReader : MetadataReader)
|
||||
(typeHandle : TypeDefinitionHandle)
|
||||
: TypeInfo<WoofWare.PawPrint.GenericParameter>
|
||||
=
|
||||
let typeDef = metadataReader.GetTypeDefinition typeHandle
|
||||
let methods = typeDef.GetMethods ()
|
||||
|
||||
let methodImpls =
|
||||
typeDef.GetMethodImplementations ()
|
||||
|> Seq.map (fun handle ->
|
||||
let m = metadataReader.GetMethodImplementation handle
|
||||
let methodBody = MetadataToken.ofEntityHandle m.MethodBody
|
||||
|
||||
match methodBody with
|
||||
| MetadataToken.MethodImplementation t ->
|
||||
KeyValuePair (handle, MethodImplParsed.MethodImplementation t)
|
||||
| MetadataToken.MethodDef t -> KeyValuePair (handle, MethodImplParsed.MethodDefinition t)
|
||||
| k -> failwith $"unexpected kind: {k}"
|
||||
|
||||
)
|
||||
|> ImmutableDictionary.CreateRange
|
||||
|
||||
let fields =
|
||||
typeDef.GetFields ()
|
||||
|> Seq.map (fun h -> FieldInfo.make metadataReader thisAssembly h (metadataReader.GetFieldDefinition h))
|
||||
|> Seq.toList
|
||||
|
||||
let name = metadataReader.GetString typeDef.Name
|
||||
let ns = metadataReader.GetString typeDef.Namespace
|
||||
let typeAttrs = typeDef.Attributes
|
||||
|
||||
let attrs =
|
||||
typeDef.GetCustomAttributes ()
|
||||
|> Seq.map (fun h -> CustomAttribute.make h (metadataReader.GetCustomAttribute h))
|
||||
|> Seq.toList
|
||||
|
||||
let genericParams =
|
||||
GenericParameter.readAll metadataReader (typeDef.GetGenericParameters ())
|
||||
|
||||
let methods =
|
||||
methods
|
||||
|> Seq.choose (fun m ->
|
||||
let result = MethodInfo.read loggerFactory peReader metadataReader m
|
||||
|
||||
match result with
|
||||
| None -> None
|
||||
| Some x -> Some x
|
||||
)
|
||||
|> Seq.toList
|
||||
|
||||
let baseType =
|
||||
match MetadataToken.ofEntityHandle typeDef.BaseType with
|
||||
| TypeReference typeReferenceHandle -> Some (BaseTypeInfo.TypeRef typeReferenceHandle)
|
||||
| TypeDefinition typeDefinitionHandle ->
|
||||
if typeDefinitionHandle.IsNil then
|
||||
None
|
||||
else
|
||||
Some (BaseTypeInfo.TypeDef typeDefinitionHandle)
|
||||
| TypeSpecification typeSpecHandle -> Some (BaseTypeInfo.TypeSpec typeSpecHandle)
|
||||
| t -> failwith $"Unrecognised base-type entity identifier: %O{t}"
|
||||
|
||||
let events =
|
||||
let result = ImmutableArray.CreateBuilder ()
|
||||
|
||||
for evt in typeDef.GetEvents () do
|
||||
metadataReader.GetEventDefinition evt
|
||||
|> EventDefn.make metadataReader
|
||||
|> result.Add
|
||||
|
||||
result.ToImmutable ()
|
||||
|
||||
{
|
||||
Namespace = ns
|
||||
Name = name
|
||||
Methods = methods
|
||||
MethodImpls = methodImpls
|
||||
Fields = fields
|
||||
BaseType = baseType
|
||||
TypeAttributes = typeAttrs
|
||||
Attributes = attrs
|
||||
TypeDefHandle = typeHandle
|
||||
Assembly = thisAssembly
|
||||
Generics = genericParams
|
||||
Events = events
|
||||
}
|
||||
|
||||
let rec resolveBaseType<'corelib, 'generic>
|
||||
(baseClassTypes : BaseClassTypes<'corelib>)
|
||||
(getName : 'corelib -> AssemblyName)
|
||||
(getType : 'corelib -> TypeDefinitionHandle -> TypeInfo<'generic>)
|
||||
(sourceAssembly : AssemblyName)
|
||||
(value : BaseTypeInfo option)
|
||||
: ResolvedBaseType
|
||||
=
|
||||
match value with
|
||||
| None -> ResolvedBaseType.Object
|
||||
| Some value ->
|
||||
|
||||
match value with
|
||||
| BaseTypeInfo.TypeDef typeDefinitionHandle ->
|
||||
if sourceAssembly = getName baseClassTypes.Corelib then
|
||||
if typeDefinitionHandle = baseClassTypes.Enum.TypeDefHandle then
|
||||
ResolvedBaseType.Enum
|
||||
elif typeDefinitionHandle = baseClassTypes.ValueType.TypeDefHandle then
|
||||
ResolvedBaseType.ValueType
|
||||
else
|
||||
let baseType = getType baseClassTypes.Corelib typeDefinitionHandle
|
||||
resolveBaseType baseClassTypes getName getType sourceAssembly baseType.BaseType
|
||||
else
|
||||
failwith "unexpected base type not in corelib"
|
||||
| BaseTypeInfo.TypeRef typeReferenceHandle -> failwith "todo"
|
||||
| BaseTypeInfo.TypeSpec typeSpecificationHandle -> failwith "todo"
|
||||
| BaseTypeInfo.ForeignAssemblyType (assemblyName, typeDefinitionHandle) ->
|
||||
resolveBaseType
|
||||
baseClassTypes
|
||||
getName
|
||||
getType
|
||||
assemblyName
|
||||
(Some (BaseTypeInfo.TypeDef typeDefinitionHandle))
|
||||
|
||||
let toTypeDefn
|
||||
(corelib : BaseClassTypes<'corelib>)
|
||||
(getName : 'corelib -> AssemblyName)
|
||||
(getType : 'corelib -> TypeDefinitionHandle -> TypeInfo<'generic>)
|
||||
(ty : TypeInfo<'generic>)
|
||||
: TypeDefn
|
||||
=
|
||||
let stk =
|
||||
match resolveBaseType corelib getName getType ty.Assembly ty.BaseType with
|
||||
| ResolvedBaseType.Enum
|
||||
| ResolvedBaseType.ValueType -> SignatureTypeKind.ValueType
|
||||
| ResolvedBaseType.Object -> SignatureTypeKind.Class
|
||||
| ResolvedBaseType.Delegate -> failwith "todo"
|
||||
|
||||
TypeDefn.FromDefinition (ComparableTypeDefinitionHandle.Make ty.TypeDefHandle, ty.Assembly.FullName, stk)
|
94
WoofWare.PawPrint.Domain/TypeRef.fs
Normal file
94
WoofWare.PawPrint.Domain/TypeRef.fs
Normal file
@@ -0,0 +1,94 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System
|
||||
open System.Reflection.Metadata
|
||||
open Microsoft.FSharp.Core
|
||||
|
||||
[<CustomComparison>]
|
||||
[<CustomEquality>]
|
||||
type TypeRefResolutionScope =
|
||||
| Assembly of AssemblyReferenceHandle
|
||||
| ModuleRef of ModuleReferenceHandle
|
||||
| TypeRef of TypeReferenceHandle
|
||||
|
||||
override this.Equals (other : obj) : bool =
|
||||
let other =
|
||||
match other with
|
||||
| :? TypeRefResolutionScope as other -> other
|
||||
| _ -> failwith "should never compare with non-TypeRefResolutionScope"
|
||||
|
||||
match this, other with
|
||||
| TypeRefResolutionScope.Assembly a1, TypeRefResolutionScope.Assembly a2 -> a1 = a2
|
||||
| TypeRefResolutionScope.Assembly _, _ -> false
|
||||
| TypeRefResolutionScope.ModuleRef m1, TypeRefResolutionScope.ModuleRef m2 -> m1 = m2
|
||||
| TypeRefResolutionScope.ModuleRef _, _ -> false
|
||||
| TypeRefResolutionScope.TypeRef t1, TypeRefResolutionScope.TypeRef t2 -> t1 = t2
|
||||
| TypeRefResolutionScope.TypeRef _, _ -> false
|
||||
|
||||
override this.GetHashCode () : int =
|
||||
match this with
|
||||
| TypeRefResolutionScope.Assembly h -> hash (1, h)
|
||||
| TypeRefResolutionScope.ModuleRef h -> hash (2, h)
|
||||
| TypeRefResolutionScope.TypeRef h -> hash (3, h)
|
||||
|
||||
interface IComparable<TypeRefResolutionScope> with
|
||||
member this.CompareTo other =
|
||||
match this, other with
|
||||
| TypeRefResolutionScope.Assembly h1, TypeRefResolutionScope.Assembly h2 ->
|
||||
// this happens to get the underlying int
|
||||
h1.GetHashCode().CompareTo (h2.GetHashCode ())
|
||||
| TypeRefResolutionScope.Assembly _, TypeRefResolutionScope.ModuleRef _ -> -1
|
||||
| TypeRefResolutionScope.Assembly _, TypeRefResolutionScope.TypeRef _ -> -1
|
||||
| TypeRefResolutionScope.ModuleRef _, Assembly _ -> 1
|
||||
| TypeRefResolutionScope.ModuleRef m1, ModuleRef m2 -> m1.GetHashCode().CompareTo (m2.GetHashCode ())
|
||||
| TypeRefResolutionScope.ModuleRef _, TypeRef _ -> -1
|
||||
| TypeRefResolutionScope.TypeRef _, Assembly _ -> 1
|
||||
| TypeRefResolutionScope.TypeRef _, ModuleRef _ -> 1
|
||||
| TypeRefResolutionScope.TypeRef t1, TypeRef t2 -> t1.GetHashCode().CompareTo (t2.GetHashCode ())
|
||||
|
||||
interface IComparable with
|
||||
member this.CompareTo (other : obj) : int =
|
||||
let other =
|
||||
match other with
|
||||
| :? TypeRefResolutionScope as other -> other
|
||||
| _ -> failwith "unexpectedly comparing TypeRefResolutionScope with something else"
|
||||
|
||||
(this :> IComparable<TypeRefResolutionScope>).CompareTo other
|
||||
|
||||
/// <summary>
|
||||
/// Represents a type reference in a .NET assembly metadata.
|
||||
/// This corresponds to a TypeReferenceHandle in System.Reflection.Metadata.
|
||||
/// </summary>
|
||||
type TypeRef =
|
||||
{
|
||||
/// <summary>The simple name of the referenced type (without namespace).</summary>
|
||||
Name : string
|
||||
|
||||
/// <summary>The namespace of the referenced type, or empty string for nested types.</summary>
|
||||
Namespace : string
|
||||
|
||||
/// <summary>
|
||||
/// The scope of the type reference: where to find the type.
|
||||
/// </summary>
|
||||
ResolutionScope : TypeRefResolutionScope
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module TypeRef =
|
||||
let make (metadataReader : MetadataReader) (ty : TypeReferenceHandle) : TypeRef =
|
||||
let typeRef = metadataReader.GetTypeReference ty
|
||||
let prettyName = metadataReader.GetString typeRef.Name
|
||||
let prettyNamespace = metadataReader.GetString typeRef.Namespace
|
||||
|
||||
let resolutionScope =
|
||||
match MetadataToken.ofEntityHandle typeRef.ResolutionScope with
|
||||
| MetadataToken.AssemblyReference ref -> TypeRefResolutionScope.Assembly ref
|
||||
| MetadataToken.ModuleReference ref -> TypeRefResolutionScope.ModuleRef ref
|
||||
| MetadataToken.TypeReference ref -> TypeRefResolutionScope.TypeRef ref
|
||||
| handle -> failwith $"Unexpected TypeRef resolution scope: {handle}"
|
||||
|
||||
{
|
||||
Name = prettyName
|
||||
Namespace = prettyNamespace
|
||||
ResolutionScope = resolutionScope
|
||||
}
|
33
WoofWare.PawPrint.Domain/TypeSpec.fs
Normal file
33
WoofWare.PawPrint.Domain/TypeSpec.fs
Normal file
@@ -0,0 +1,33 @@
|
||||
namespace WoofWare.PawPrint
|
||||
|
||||
open System.Reflection
|
||||
open System.Reflection.Metadata
|
||||
|
||||
/// <summary>
|
||||
/// Represents a type specification in assembly metadata.
|
||||
/// Type specifications describe complex types like generic instantiations,
|
||||
/// arrays, pointers, and other composite types.
|
||||
/// </summary>
|
||||
type TypeSpec =
|
||||
{
|
||||
/// <summary>
|
||||
/// The metadata token handle that uniquely identifies this type specification.
|
||||
/// </summary>
|
||||
Handle : TypeSpecificationHandle
|
||||
|
||||
/// <summary>
|
||||
/// The full type definition/signature of this type specification.
|
||||
/// This contains all the details about the composite type structure.
|
||||
/// </summary>
|
||||
Signature : TypeDefn
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module TypeSpec =
|
||||
let make (assembly : AssemblyName) (handle : TypeSpecificationHandle) (r : TypeSpecification) : TypeSpec =
|
||||
let spec = r.DecodeSignature (TypeDefn.typeProvider assembly, ())
|
||||
|
||||
{
|
||||
Handle = handle
|
||||
Signature = spec
|
||||
}
|
35
WoofWare.PawPrint.Domain/WoofWare.PawPrint.Domain.fsproj
Normal file
35
WoofWare.PawPrint.Domain/WoofWare.PawPrint.Domain.fsproj
Normal file
@@ -0,0 +1,35 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="StringToken.fs" />
|
||||
<Compile Include="Tokens.fs" />
|
||||
<Compile Include="TypeRef.fs" />
|
||||
<Compile Include="IlOp.fs" />
|
||||
<Compile Include="CustomAttribute.fs" />
|
||||
<Compile Include="AssemblyReference.fs" />
|
||||
<Compile Include="EventDefn.fs" />
|
||||
<Compile Include="ComparableTypeDefinitionHandle.fs" />
|
||||
<Compile Include="ComparableSignatureHeader.fs" />
|
||||
<Compile Include="TypeDefn.fs" />
|
||||
<Compile Include="ConcreteType.fs" />
|
||||
<Compile Include="FieldInfo.fs" />
|
||||
<Compile Include="MethodInfo.fs" />
|
||||
<Compile Include="TypeInfo.fs" />
|
||||
<Compile Include="MethodSpec.fs" />
|
||||
<Compile Include="MemberReference.fs" />
|
||||
<Compile Include="Namespace.fs" />
|
||||
<Compile Include="ExportedType.fs" />
|
||||
<Compile Include="TypeSpec.fs" />
|
||||
<Compile Include="Assembly.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Reference in New Issue
Block a user