diff --git a/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj b/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj
index a77ab1e..1955467 100644
--- a/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj
+++ b/WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj
@@ -26,6 +26,7 @@
+
diff --git a/WoofWare.PawPrint.Test/sources/ResizeArray.cs b/WoofWare.PawPrint.Test/sources/ResizeArray.cs
new file mode 100644
index 0000000..c677256
--- /dev/null
+++ b/WoofWare.PawPrint.Test/sources/ResizeArray.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace HelloWorldApp
+{
+ class Program
+ {
+ static int Main(string[] args)
+ {
+ var l = new List();
+ l.Add(3);
+ l.Add(100);
+ var m = l.Select(x => x.ToString()).ToList();
+ // 2 + 103 + (1 + 3) = 109
+ return m.Count + l.Sum() + m.Select(x => x.Length).Sum();
+ }
+ }
+}
diff --git a/WoofWare.PawPrint/Assembly.fs b/WoofWare.PawPrint/Assembly.fs
index a9032d4..4ea6ed1 100644
--- a/WoofWare.PawPrint/Assembly.fs
+++ b/WoofWare.PawPrint/Assembly.fs
@@ -428,6 +428,7 @@ module Assembly =
(assemblies : ImmutableDictionary)
(referencedInAssembly : DumpedAssembly)
(target : TypeRef)
+ (genericArgs : ImmutableArray option)
: TypeResolutionResult
=
match target.ResolutionScope with
@@ -457,16 +458,20 @@ module Assembly =
match targetType with
| [ t ] ->
- // If resolved from TypeDef (above), it won't have generic parameters, I hope?
let t =
- t |> TypeInfo.mapGeneric (fun _ -> failwith "no generic parameters")
+ 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
+ | Some ty -> resolveTypeFromExport assy assemblies ty genericArgs
| k -> failwith $"Unexpected: {k}"
and internal resolveTypeFromName
@@ -474,6 +479,7 @@ module Assembly =
(assemblies : ImmutableDictionary)
(ns : string option)
(name : string)
+ (genericArgs : ImmutableArray option)
: TypeResolutionResult
=
match ns with
@@ -482,26 +488,30 @@ module Assembly =
match assy.TypeDef ns name with
| Some typeDef ->
- // If resolved from TypeDef, it won't have generic parameters, I hope?
let typeDef =
typeDef
- |> TypeInfo.mapGeneric (fun _ -> failwith "no generic parameters")
+ |> TypeInfo.mapGeneric (fun param ->
+ match genericArgs with
+ | None -> failwith $"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
+ | Some typeRef -> resolveTypeRef assemblies assy typeRef genericArgs
| None ->
match assy.ExportedType (Some ns) name with
- | Some export -> resolveTypeFromExport assy assemblies export
+ | Some export -> resolveTypeFromExport assy assemblies export genericArgs
| None -> failwith $"TODO: type resolution unimplemented for {ns} {name}"
and resolveTypeFromExport
(fromAssembly : DumpedAssembly)
(assemblies : ImmutableDictionary)
(ty : WoofWare.PawPrint.ExportedType)
+ (genericArgs : ImmutableArray option)
: TypeResolutionResult
=
match ty.Data with
@@ -511,4 +521,4 @@ module Assembly =
match assemblies.TryGetValue assy.Name.FullName with
| false, _ -> TypeResolutionResult.FirstLoadAssy assy
- | true, toAssy -> resolveTypeFromName toAssy assemblies ty.Namespace ty.Name
+ | true, toAssy -> resolveTypeFromName toAssy assemblies ty.Namespace ty.Name genericArgs
diff --git a/WoofWare.PawPrint/BasicCliType.fs b/WoofWare.PawPrint/BasicCliType.fs
index 02046f3..d428e44 100644
--- a/WoofWare.PawPrint/BasicCliType.fs
+++ b/WoofWare.PawPrint/BasicCliType.fs
@@ -100,7 +100,8 @@ module CliType =
let rec zeroOf
(assemblies : ImmutableDictionary)
(assy : DumpedAssembly)
- (generics : TypeDefn ImmutableArray)
+ (typeGenerics : TypeDefn ImmutableArray option)
+ (methodGenerics : TypeDefn ImmutableArray option)
(ty : TypeDefn)
: CliTypeResolutionResult
=
@@ -135,7 +136,7 @@ module CliType =
match signatureTypeKind with
| SignatureTypeKind.Unknown -> failwith "todo"
| SignatureTypeKind.ValueType ->
- match Assembly.resolveTypeRef assemblies assy typeRef with
+ match Assembly.resolveTypeRef assemblies assy typeRef typeGenerics with
| TypeResolutionResult.Resolved (_, ty) -> failwith $"TODO: {ty}"
| TypeResolutionResult.FirstLoadAssy assy -> CliTypeResolutionResult.FirstLoad assy
| SignatureTypeKind.Class -> CliType.ObjectRef None |> CliTypeResolutionResult.Resolved
@@ -148,17 +149,20 @@ module CliType =
let fields =
typeDef.Fields
- |> List.map (fun fi -> zeroOf assemblies assy generics fi.Signature)
+ |> List.map (fun fi -> zeroOf assemblies assy typeGenerics methodGenerics fi.Signature)
CliType.ObjectRef None |> CliTypeResolutionResult.Resolved
| SignatureTypeKind.Class -> CliType.ObjectRef None |> CliTypeResolutionResult.Resolved
| _ -> raise (ArgumentOutOfRangeException ())
- | TypeDefn.GenericInstantiation (generic, args) ->
- // TODO: this is rather concerning and probably incorrect
- zeroOf assemblies assy args generic
+ | TypeDefn.GenericInstantiation (generic, args) -> zeroOf assemblies assy (Some args) methodGenerics generic
| TypeDefn.FunctionPointer typeMethodSignature -> failwith "todo"
| TypeDefn.GenericTypeParameter index ->
// TODO: can generics depend on other generics? presumably, so we pass the array down again
- zeroOf assemblies assy generics generics.[index]
- | TypeDefn.GenericMethodParameter index -> zeroOf assemblies assy generics generics.[index]
+ match typeGenerics with
+ | None -> failwith "asked for a type parameter of generic type, but no generics in scope"
+ | Some generics -> zeroOf assemblies assy (Some generics) methodGenerics generics.[index]
+ | TypeDefn.GenericMethodParameter index ->
+ match methodGenerics with
+ | None -> failwith "asked for a method parameter of generic type, but no generics in scope"
+ | Some generics -> zeroOf assemblies assy typeGenerics (Some generics) generics.[index]
| TypeDefn.Void -> failwith "should never construct an element of type Void"
diff --git a/WoofWare.PawPrint/IlMachineState.fs b/WoofWare.PawPrint/IlMachineState.fs
index c303575..2838edd 100644
--- a/WoofWare.PawPrint/IlMachineState.fs
+++ b/WoofWare.PawPrint/IlMachineState.fs
@@ -205,11 +205,12 @@ module IlMachineState =
(loggerFactory : ILoggerFactory)
(ns : string option)
(name : string)
+ (genericArgs : ImmutableArray option)
(assy : DumpedAssembly)
(state : IlMachineState)
: IlMachineState * DumpedAssembly * WoofWare.PawPrint.TypeInfo
=
- match Assembly.resolveTypeFromName assy state._LoadedAssemblies ns name with
+ match Assembly.resolveTypeFromName assy state._LoadedAssemblies ns name genericArgs with
| TypeResolutionResult.Resolved (assy, typeDef) -> state, assy, typeDef
| TypeResolutionResult.FirstLoadAssy loadFirst ->
let state, _, _ =
@@ -219,16 +220,17 @@ module IlMachineState =
(fst loadFirst.Handle)
state
- resolveTypeFromName loggerFactory ns name assy state
+ resolveTypeFromName loggerFactory ns name genericArgs assy state
and resolveTypeFromExport
(loggerFactory : ILoggerFactory)
(fromAssembly : DumpedAssembly)
(ty : WoofWare.PawPrint.ExportedType)
+ (genericArgs : ImmutableArray option)
(state : IlMachineState)
: IlMachineState * DumpedAssembly * WoofWare.PawPrint.TypeInfo
=
- match Assembly.resolveTypeFromExport fromAssembly state._LoadedAssemblies ty with
+ match Assembly.resolveTypeFromExport fromAssembly state._LoadedAssemblies ty genericArgs with
| TypeResolutionResult.Resolved (assy, typeDef) -> state, assy, typeDef
| TypeResolutionResult.FirstLoadAssy loadFirst ->
let state, targetAssy, _ =
@@ -238,59 +240,64 @@ module IlMachineState =
(fst loadFirst.Handle)
state
- resolveTypeFromName loggerFactory ty.Namespace ty.Name targetAssy state
+ resolveTypeFromName loggerFactory ty.Namespace ty.Name genericArgs targetAssy state
and resolveTypeFromRef
(loggerFactory : ILoggerFactory)
(referencedInAssembly : DumpedAssembly)
(target : TypeRef)
+ (genericArgs : ImmutableArray option)
(state : IlMachineState)
: IlMachineState * DumpedAssembly * WoofWare.PawPrint.TypeInfo
=
- match Assembly.resolveTypeRef state._LoadedAssemblies referencedInAssembly target with
+ match Assembly.resolveTypeRef state._LoadedAssemblies referencedInAssembly target genericArgs with
| TypeResolutionResult.Resolved (assy, typeDef) -> state, assy, typeDef
| TypeResolutionResult.FirstLoadAssy loadFirst ->
let state, _, _ =
loadAssembly
loggerFactory
- (state._LoadedAssemblies.[snd(loadFirst.Handle).FullName])
+ state._LoadedAssemblies.[snd(loadFirst.Handle).FullName]
(fst loadFirst.Handle)
state
- resolveTypeFromRef loggerFactory referencedInAssembly target state
+ resolveTypeFromRef loggerFactory referencedInAssembly target genericArgs state
and resolveType
(loggerFactory : ILoggerFactory)
(ty : TypeReferenceHandle)
+ (genericArgs : ImmutableArray option)
(assy : DumpedAssembly)
(state : IlMachineState)
: IlMachineState * DumpedAssembly * WoofWare.PawPrint.TypeInfo
=
let target = assy.TypeRefs.[ty]
- resolveTypeFromRef loggerFactory assy target state
+ resolveTypeFromRef loggerFactory assy target genericArgs state
let rec resolveTypeFromDefn
(loggerFactory : ILoggerFactory)
(ty : TypeDefn)
+ (genericArgs : ImmutableArray option)
(assy : DumpedAssembly)
(state : IlMachineState)
- : IlMachineState *
- DumpedAssembly *
- WoofWare.PawPrint.TypeInfo *
- TypeDefn ImmutableArray option
+ : IlMachineState * DumpedAssembly * WoofWare.PawPrint.TypeInfo
=
match ty with
| TypeDefn.GenericInstantiation (generic, args) ->
- let state, _, generic, subArgs =
- resolveTypeFromDefn loggerFactory generic assy state
+ resolveTypeFromDefn loggerFactory generic (Some args) assy state
+ | TypeDefn.FromDefinition (defn, _typeKind) ->
+ let defn =
+ assy.TypeDefs.[defn.Get]
+ |> TypeInfo.mapGeneric (fun param ->
+ match genericArgs with
+ | None -> failwith "somehow got a generic TypeDefn.FromDefinition without any generic args"
+ | Some genericArgs -> genericArgs.[param.SequenceNumber]
+ )
- match subArgs with
- | Some _ -> failwith "unexpectedly had multiple generic instantiations for the same type"
- | None ->
-
- state, assy, generic, Some args
- | TypeDefn.FromDefinition (defn, _typeKind) -> state, assy, assy.TypeDefs.[defn.Get], None
+ state, assy, defn
+ | TypeDefn.FromReference (ref, _typeKind) ->
+ let state, assy, ty = resolveTypeFromRef loggerFactory assy ref genericArgs state
+ state, assy, ty
| s -> failwith $"TODO: resolveTypeFromDefn unimplemented for {s}"
let resolveTypeFromSpec
@@ -300,35 +307,25 @@ module IlMachineState =
(state : IlMachineState)
: IlMachineState * DumpedAssembly * WoofWare.PawPrint.TypeInfo
=
- let state, assy, generic, args =
- resolveTypeFromDefn loggerFactory assy.TypeSpecs.[ty].Signature assy state
-
- match args with
- | None ->
- let generic =
- generic
- |> TypeInfo.mapGeneric (fun _ -> failwith "no generic parameters")
-
- state, assy, generic
- | Some args ->
- let generic = TypeInfo.withGenerics args generic
- state, assy, generic
+ // Any necessary generics will be baked into the TypeDefn e.g. as a `GenericInstantiation`.
+ resolveTypeFromDefn loggerFactory assy.TypeSpecs.[ty].Signature None assy state
let rec cliTypeZeroOf
(loggerFactory : ILoggerFactory)
(assy : DumpedAssembly)
(ty : TypeDefn)
- (generics : TypeDefn ImmutableArray)
+ (typeGenerics : TypeDefn ImmutableArray option)
+ (methodGenerics : TypeDefn ImmutableArray option)
(state : IlMachineState)
: IlMachineState * CliType
=
- match CliType.zeroOf state._LoadedAssemblies assy generics ty with
+ match CliType.zeroOf state._LoadedAssemblies assy typeGenerics methodGenerics ty with
| CliTypeResolutionResult.Resolved result -> state, result
| CliTypeResolutionResult.FirstLoad ref ->
let state, _, _ =
loadAssembly loggerFactory state._LoadedAssemblies.[snd(ref.Handle).FullName] (fst ref.Handle) state
- cliTypeZeroOf loggerFactory assy ty generics state
+ cliTypeZeroOf loggerFactory assy ty typeGenerics methodGenerics state
let callMethod
(loggerFactory : ILoggerFactory)
@@ -342,16 +339,16 @@ module IlMachineState =
(state : IlMachineState)
: IlMachineState
=
+ let typeGenerics =
+ match methodToCall.DeclaringType.Generics with
+ | [] -> None
+ | x -> Some (ImmutableArray.CreateRange x)
+
let state, argZeroObjects =
((state, []), methodToCall.Signature.ParameterTypes)
||> List.fold (fun (state, zeros) ty ->
let state, zero =
- cliTypeZeroOf
- loggerFactory
- (state.ActiveAssembly thread)
- ty
- (methodGenerics |> Option.defaultValue ImmutableArray.Empty)
- state
+ cliTypeZeroOf loggerFactory (state.ActiveAssembly thread) ty typeGenerics methodGenerics state
state, zero :: zeros
)
@@ -553,7 +550,13 @@ module IlMachineState =
| NothingToDo state -> Ok state
| TypeRef typeReferenceHandle ->
let state, assy, targetType =
- resolveType loggerFactory typeReferenceHandle (state.ActiveAssembly currentThread) state
+ // TypeRef won't have any generics; it would be a TypeSpec if it did
+ resolveType
+ loggerFactory
+ typeReferenceHandle
+ None
+ (state.ActiveAssembly currentThread)
+ state
logger.LogDebug (
"Resolved base type of {TypeDefNamespace}.{TypeDefName} to a typeref in assembly {ResolvedAssemblyName}, {BaseTypeNamespace}.{BaseTypeName}",
@@ -564,7 +567,6 @@ module IlMachineState =
targetType.Name
)
- // TypeRef won't have any generics; it would be a TypeSpec if it did
let ty = ConcreteType.make assy.Name targetType.TypeDefHandle []
match loadClass loggerFactory ty currentThread state with
@@ -900,7 +902,7 @@ module IlMachineState =
let state, assy, targetType =
match mem.Parent with
- | MetadataToken.TypeReference parent -> resolveType loggerFactory parent assy state
+ | MetadataToken.TypeReference parent -> resolveType loggerFactory parent None assy state
| MetadataToken.TypeSpecification parent -> resolveTypeFromSpec loggerFactory parent assy state
| parent -> failwith $"Unexpected: {parent}"
@@ -992,12 +994,7 @@ module IlMachineState =
| retType ->
// TODO: generics
let state, zero =
- cliTypeZeroOf
- loggerFactory
- (state.ActiveAssembly currentThread)
- retType
- ImmutableArray.Empty
- state
+ cliTypeZeroOf loggerFactory (state.ActiveAssembly currentThread) retType None None state
let toPush = EvalStackValue.toCliTypeCoerced zero retVal
diff --git a/WoofWare.PawPrint/MethodState.fs b/WoofWare.PawPrint/MethodState.fs
index 63081d4..f9bc9e4 100644
--- a/WoofWare.PawPrint/MethodState.fs
+++ b/WoofWare.PawPrint/MethodState.fs
@@ -164,12 +164,16 @@ and MethodState =
let requiredAssemblies = ResizeArray ()
+ let typeGenerics =
+ match method.DeclaringType.Generics with
+ | [] -> None
+ | x -> ImmutableArray.CreateRange x |> Some
+
let localVars =
- // TODO: generics?
let result = ImmutableArray.CreateBuilder ()
for var in localVariableSig do
- match CliType.zeroOf loadedAssemblies containingAssembly ImmutableArray.Empty var with
+ match CliType.zeroOf loadedAssemblies containingAssembly typeGenerics methodGenerics var with
| CliTypeResolutionResult.Resolved t -> result.Add t
| CliTypeResolutionResult.FirstLoad (assy : WoofWare.PawPrint.AssemblyReference) ->
requiredAssemblies.Add assy
diff --git a/WoofWare.PawPrint/UnaryMetadataIlOp.fs b/WoofWare.PawPrint/UnaryMetadataIlOp.fs
index 7ab26fa..997001e 100644
--- a/WoofWare.PawPrint/UnaryMetadataIlOp.fs
+++ b/WoofWare.PawPrint/UnaryMetadataIlOp.fs
@@ -165,17 +165,17 @@ module internal UnaryMetadataIlOp =
ctorType.Name
)
+ let typeGenerics =
+ match ctor.DeclaringType.Generics with
+ | [] -> None
+ | l -> Some (ImmutableArray.CreateRange l)
+
let state, fieldZeros =
((state, []), ctorType.Fields)
||> List.fold (fun (state, zeros) field ->
// TODO: generics
let state, zero =
- IlMachineState.cliTypeZeroOf
- loggerFactory
- ctorAssembly
- field.Signature
- ImmutableArray.Empty
- state
+ IlMachineState.cliTypeZeroOf loggerFactory ctorAssembly field.Signature typeGenerics None state
state, (field.Name, zero) :: zeros
)
@@ -307,12 +307,18 @@ module internal UnaryMetadataIlOp =
let valueToStore, state = IlMachineState.popEvalStack thread state
+ let typeGenerics =
+ match declaringType.Generics with
+ | [] -> None
+ | l -> Some (ImmutableArray.CreateRange l)
+
let state, zero =
IlMachineState.cliTypeZeroOf
loggerFactory
(state.ActiveAssembly thread)
field.Signature
- (ImmutableArray.CreateRange declaringType.Generics)
+ typeGenerics
+ None // field can't have its own generics
state
let valueToStore = EvalStackValue.toCliTypeCoerced zero valueToStore
@@ -403,12 +409,18 @@ module internal UnaryMetadataIlOp =
let popped, state = IlMachineState.popEvalStack thread state
+ let typeGenerics =
+ match field.DeclaringType.Generics with
+ | [] -> None
+ | l -> Some (ImmutableArray.CreateRange l)
+
let state, zero =
IlMachineState.cliTypeZeroOf
loggerFactory
activeAssy
field.Signature
- (ImmutableArray.CreateRange field.DeclaringType.Generics)
+ typeGenerics
+ None // field can't have its own generics
state
let toStore = EvalStackValue.toCliTypeCoerced zero popped
@@ -453,6 +465,11 @@ module internal UnaryMetadataIlOp =
let currentObj, state = IlMachineState.popEvalStack thread state
+ let typeGenerics =
+ match field.DeclaringType.Generics with
+ | [] -> None
+ | l -> Some (ImmutableArray.CreateRange l)
+
if field.Attributes.HasFlag FieldAttributes.Static then
let state, staticField =
match state.GetStatic field.DeclaringType field.Name with
@@ -463,7 +480,8 @@ module internal UnaryMetadataIlOp =
loggerFactory
(state.LoadedAssembly(field.DeclaringType.Assembly).Value)
field.Signature
- (ImmutableArray.CreateRange field.DeclaringType.Generics)
+ typeGenerics
+ None // field can't have its own generics
state
let state = state.SetStatic field.DeclaringType field.Name zero
@@ -533,6 +551,11 @@ module internal UnaryMetadataIlOp =
| FirstLoadThis state -> state, WhatWeDid.SuspendedForClassInit
| NothingToDo state ->
+ let typeGenerics =
+ match field.DeclaringType.Generics with
+ | [] -> None
+ | l -> Some (ImmutableArray.CreateRange l)
+
let fieldValue, state =
match state.GetStatic field.DeclaringType field.Name with
| None ->
@@ -541,7 +564,8 @@ module internal UnaryMetadataIlOp =
loggerFactory
activeAssy
field.Signature
- (field.DeclaringType.Generics |> ImmutableArray.CreateRange)
+ typeGenerics
+ None // field can't have its own generics
state
newVal, state.SetStatic field.DeclaringType field.Name newVal
@@ -600,13 +624,19 @@ module internal UnaryMetadataIlOp =
|> IlMachineState.advanceProgramCounter thread
|> Tuple.withRight WhatWeDid.Executed
| None ->
+ let typeGenerics =
+ match field.DeclaringType.Generics with
+ | [] -> None
+ | l -> Some (ImmutableArray.CreateRange l)
+
// Field is not yet initialised
let state, zero =
IlMachineState.cliTypeZeroOf
loggerFactory
activeAssy
field.Signature
- (field.DeclaringType.Generics |> ImmutableArray.CreateRange)
+ typeGenerics
+ None // field can't have its own generics
state
state.SetStatic field.DeclaringType field.Name zero