Skip to content

Commit 2505f60

Browse files
[Repo Assist] perf: cache GetParameters/PropertyType/FieldType/EventHandlerType in target-model wrappers (#513)
🤖 *This is an automated PR from Repo Assist.* ## Summary Caches computed values in the target-model member wrappers (`txILConstructorDef`, `txILMethodDef`, `txILPropertyDef`, `txILEventDef`, `txILFieldDef`) inside `TargetTypeDefinition` to avoid repeated type-resolution and array allocations on every call. ## What was happening Each wrapper function creates a `new XxxInfo()` object expression. Before this change, several frequently-called members recomputed their results from scratch on every access: | Wrapper | Member | Was doing | |---------|--------|-----------| | `txILConstructorDef` | `GetParameters()` | `Array.map (txILParameter ...)` every call | | `txILMethodDef` | `GetParameters()` | `Array.map (txILParameter ...)` every call | | `txILPropertyDef` | `PropertyType` | `txILType` resolution every call | | `txILPropertyDef` | `GetIndexParameters()` | `Array.map (txILParameter ...)` every call | | `txILEventDef` | `EventHandlerType` | `txILType` resolution every call | | `txILFieldDef` | `FieldType` | `txILType` resolution every call | ## Why this matters `TargetTypeDefinition` already caches its member wrapper arrays via `lazy` (`ctorDefs`, `methDefs`, `fieldDefs`, `propDefs`, `eventDefs`). This means the **same wrapper objects are reused** across calls. Without result caching inside the wrappers, every access to `GetParameters()`, `PropertyType`, etc. allocates a new array and re-runs IL type resolution — which the F# compiler does repeatedly during type inference against a generative type provider's generated assembly. ## Fix Added `lazy` bindings before each object expression to cache the computed results: ````fsharp // txILMethodDef (before) override __.GetParameters() = inp.Parameters |> Array.map (txILParameter (gps, gps2)) // txILMethodDef (after) let parametersCache = lazy (inp.Parameters |> Array.map (txILParameter (gps, gps2))) // ... override __.GetParameters() = parametersCache.Value ``` Same pattern applied to `txILConstructorDef`, `txILPropertyDef`, `txILEventDef`, and `txILFieldDef`. Note: This is independent of and complementary to PR #509 (which optimises `Array.filter (canBindXxx)` on the bind-all path). ## Test Status ``` Passed! - Failed: 0, Passed: 157, Skipped: 0, Total: 157 ```` All 157 tests pass. > Generated by 🌈 Repo Assist, see [workflow run](https://github.com/fsprojects/FSharp.TypeProviders.SDK/actions/runs/25196752728). > Generated by 🌈 Repo Assist, see [workflow run](https://github.com/fsprojects/FSharp.TypeProviders.SDK/actions/runs/25196752728). [Learn more](https://github.com/githubnext/agentics/blob/main/docs/repo-assist.md). > > To install this [agentic workflow](https://github.com/githubnext/agentics/blob/96b9d4c39aa22359c0b38265927eadb31dcf4e2a/workflows/repo-assist.md), run > ``` > gh aw add githubnext/agentics@96b9d4c > ``` <!-- gh-aw-agentic-workflow: Repo Assist, engine: copilot, model: auto, id: 25196752728, workflow_id: repo-assist, run: https://github.com/fsprojects/FSharp.TypeProviders.SDK/actions/runs/25196752728 --> <!-- gh-aw-workflow-id: repo-assist --> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent ad7621f commit 2505f60

1 file changed

Lines changed: 12 additions & 6 deletions

File tree

src/ProvidedTypes.fs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7758,14 +7758,15 @@ namespace ProviderImplementation.ProvidedTypes
77587758
/// Makes a method definition read from a binary available as a ConstructorInfo. Not all methods are implemented.
77597759
and txILConstructorDef (declTy: Type) (inp: ILMethodDef) =
77607760
let gps = if declTy.IsGenericType then declTy.GetGenericArguments() else [| |]
7761+
let parametersCache = lazy (inp.Parameters |> Array.map (txILParameter (gps, [| |])))
77617762
{ new ConstructorInfo() with
77627763

77637764
override __.Name = inp.Name
77647765
override __.Attributes = inp.Attributes
77657766
override __.MemberType = MemberTypes.Constructor
77667767
override __.DeclaringType = declTy
77677768

7768-
override __.GetParameters() = inp.Parameters |> Array.map (txILParameter (gps, [| |]))
7769+
override __.GetParameters() = parametersCache.Value
77697770
override __.GetCustomAttributesData() = inp.CustomAttrs |> txCustomAttributesData
77707771
override __.MetadataToken = inp.Token
77717772

@@ -7792,13 +7793,14 @@ namespace ProviderImplementation.ProvidedTypes
77927793
let gps = if declTy.IsGenericType then declTy.GetGenericArguments() else [| |]
77937794
let rec gps2 = inp.GenericParams |> Array.mapi (fun i gp -> txILGenericParam (fun () -> gps, gps2) (i + gps.Length) gp)
77947795
let mutable returnTypeFixCache = None
7796+
let parametersCache = lazy (inp.Parameters |> Array.map (txILParameter (gps, gps2)))
77957797
{ new MethodInfo() with
77967798

77977799
override __.Name = inp.Name
77987800
override __.DeclaringType = declTy
77997801
override __.MemberType = MemberTypes.Method
78007802
override __.Attributes = inp.Attributes
7801-
override __.GetParameters() = inp.Parameters |> Array.map (txILParameter (gps, gps2))
7803+
override __.GetParameters() = parametersCache.Value
78027804
override __.CallingConvention = if inp.IsStatic then CallingConventions.Standard else CallingConventions.HasThis ||| CallingConventions.Standard
78037805

78047806
override __.ReturnType =
@@ -7851,17 +7853,19 @@ namespace ProviderImplementation.ProvidedTypes
78517853
/// Makes a property definition read from a binary available as a PropertyInfo. Not all methods are implemented.
78527854
and txILPropertyDef (declTy: Type) (inp: ILPropertyDef) =
78537855
let gps = if declTy.IsGenericType then declTy.GetGenericArguments() else [| |]
7856+
let propertyTypeCache = lazy (inp.PropertyType |> txILType (gps, [| |]))
7857+
let indexParametersCache = lazy (inp.IndexParameters |> Array.map (txILParameter (gps, [| |])))
78547858
{ new PropertyInfo() with
78557859

78567860
override __.Name = inp.Name
78577861
override __.Attributes = inp.Attributes
78587862
override __.MemberType = MemberTypes.Property
78597863
override __.DeclaringType = declTy
78607864

7861-
override __.PropertyType = inp.PropertyType |> txILType (gps, [| |])
7865+
override __.PropertyType = propertyTypeCache.Value
78627866
override __.GetGetMethod(_nonPublic) = inp.GetMethod |> Option.map (txILMethodRef declTy) |> Option.toObj
78637867
override __.GetSetMethod(_nonPublic) = inp.SetMethod |> Option.map (txILMethodRef declTy) |> Option.toObj
7864-
override __.GetIndexParameters() = inp.IndexParameters |> Array.map (txILParameter (gps, [| |]))
7868+
override __.GetIndexParameters() = indexParametersCache.Value
78657869
override __.CanRead = inp.GetMethod.IsSome
78667870
override __.CanWrite = inp.SetMethod.IsSome
78677871
override __.GetCustomAttributesData() = inp.CustomAttrs |> txCustomAttributesData
@@ -7887,14 +7891,15 @@ namespace ProviderImplementation.ProvidedTypes
78877891
/// Make an event definition read from a binary available as an EventInfo. Not all methods are implemented.
78887892
and txILEventDef (declTy: Type) (inp: ILEventDef) =
78897893
let gps = if declTy.IsGenericType then declTy.GetGenericArguments() else [| |]
7894+
let eventHandlerTypeCache = lazy (inp.EventHandlerType |> txILType (gps, [| |]))
78907895
{ new EventInfo() with
78917896

78927897
override __.Name = inp.Name
78937898
override __.Attributes = inp.Attributes
78947899
override __.MemberType = MemberTypes.Event
78957900
override __.DeclaringType = declTy
78967901

7897-
override __.EventHandlerType = inp.EventHandlerType |> txILType (gps, [| |])
7902+
override __.EventHandlerType = eventHandlerTypeCache.Value
78987903
override __.GetAddMethod(_nonPublic) = inp.AddMethod |> txILMethodRef declTy
78997904
override __.GetRemoveMethod(_nonPublic) = inp.RemoveMethod |> txILMethodRef declTy
79007905
override __.GetCustomAttributesData() = inp.CustomAttrs |> txCustomAttributesData
@@ -7918,14 +7923,15 @@ namespace ProviderImplementation.ProvidedTypes
79187923
/// Makes a field definition read from a binary available as a FieldInfo. Not all methods are implemented.
79197924
and txILFieldDef (declTy: Type) (inp: ILFieldDef) =
79207925
let gps = if declTy.IsGenericType then declTy.GetGenericArguments() else [| |]
7926+
let fieldTypeCache = lazy (inp.FieldType |> txILType (gps, [| |]))
79217927
{ new FieldInfo() with
79227928

79237929
override __.Name = inp.Name
79247930
override __.Attributes = inp.Attributes
79257931
override __.MemberType = MemberTypes.Field
79267932
override __.DeclaringType = declTy
79277933

7928-
override __.FieldType = inp.FieldType |> txILType (gps, [| |])
7934+
override __.FieldType = fieldTypeCache.Value
79297935
override __.GetRawConstantValue() = match inp.LiteralValue with None -> null | Some v -> v
79307936
override __.GetCustomAttributesData() = inp.CustomAttrs |> txCustomAttributesData
79317937
override __.MetadataToken = inp.Token

0 commit comments

Comments
 (0)