Skip to content

Commit dda5426

Browse files
brettfoKevinRansom
authored andcommitted
fire events when script host resolves assembly reference (#7571)
* add event every time `#r` is invoked * pass additional args to script host
1 parent cbf0e98 commit dda5426

File tree

6 files changed

+51
-6
lines changed

6 files changed

+51
-6
lines changed

src/fsharp/CompileOps.fs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4775,7 +4775,7 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
47754775

47764776
/// Process #r in F# Interactive.
47774777
/// Adds the reference to the tcImports and add the ccu to the type checking environment.
4778-
let RequireDLL (ctok, tcImports: TcImports, tcEnv, thisAssemblyName, m, file) =
4778+
let RequireDLL (ctok, tcImports: TcImports, tcEnv, thisAssemblyName, m, file, assemblyReferenceAdded: string -> unit) =
47794779
let resolutions = CommitOperationResult(tcImports.TryResolveAssemblyReference(ctok, AssemblyReference(m, file, None), ResolveAssemblyReferenceMode.ReportErrors))
47804780
let dllinfos, ccuinfos = tcImports.RegisterAndImportReferencedAssemblies(ctok, resolutions) |> Cancellable.runWithoutCancellation
47814781

@@ -4786,7 +4786,13 @@ let RequireDLL (ctok, tcImports: TcImports, tcEnv, thisAssemblyName, m, file) =
47864786

47874787
let g = tcImports.GetTcGlobals()
47884788
let amap = tcImports.GetImportMap()
4789-
let tcEnv = (tcEnv, asms) ||> List.fold (fun tcEnv asm -> AddCcuToTcEnv(g, amap, m, tcEnv, thisAssemblyName, asm.FSharpViewOfMetadata, asm.AssemblyAutoOpenAttributes, asm.AssemblyInternalsVisibleToAttributes))
4789+
let buildTcEnv tcEnv asm =
4790+
let tcEnv = AddCcuToTcEnv(g, amap, m, tcEnv, thisAssemblyName, asm.FSharpViewOfMetadata, asm.AssemblyAutoOpenAttributes, asm.AssemblyInternalsVisibleToAttributes)
4791+
match asm.FSharpViewOfMetadata.FileName with
4792+
| Some asmPath -> assemblyReferenceAdded asmPath
4793+
| None -> ()
4794+
tcEnv
4795+
let tcEnv = (tcEnv, asms) ||> List.fold buildTcEnv
47904796
tcEnv, (dllinfos, asms)
47914797

47924798

src/fsharp/CompileOps.fsi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,7 @@ val WriteOptimizationData: TcGlobals * filename: string * inMem: bool * CcuThunk
672672

673673
/// Process #r in F# Interactive.
674674
/// Adds the reference to the tcImports and add the ccu to the type checking environment.
675-
val RequireDLL: CompilationThreadToken * TcImports * TcEnv * thisAssemblyName: string * referenceRange: range * file: string -> TcEnv * (ImportedBinary list * ImportedAssembly list)
675+
val RequireDLL: CompilationThreadToken * TcImports * TcEnv * thisAssemblyName: string * referenceRange: range * file: string * assemblyReferenceAdded: (string -> unit) -> TcEnv * (ImportedBinary list * ImportedAssembly list)
676676

677677
/// Processing # commands
678678
val ProcessMetaCommandsFromInput:

src/fsharp/FSharp.Compiler.Private.Scripting/FSharpScript.fs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace FSharp.Compiler.Scripting
55
open System
66
open FSharp.Compiler.Interactive.Shell
77

8-
type FSharpScript(?captureInput: bool, ?captureOutput: bool) as this =
8+
type FSharpScript(?captureInput: bool, ?captureOutput: bool, ?additionalArgs: string[]) as this =
99
let outputProduced = Event<string>()
1010
let errorProduced = Event<string>()
1111

@@ -17,6 +17,7 @@ type FSharpScript(?captureInput: bool, ?captureOutput: bool) as this =
1717
do stderr.LineWritten.Add errorProduced.Trigger
1818
let captureInput = defaultArg captureInput false
1919
let captureOutput = defaultArg captureOutput false
20+
let additionalArgs = defaultArg additionalArgs [||]
2021
let savedInput = Console.In
2122
let savedOutput = Console.Out
2223
let savedError = Console.Error
@@ -29,9 +30,12 @@ type FSharpScript(?captureInput: bool, ?captureOutput: bool) as this =
2930
())()
3031

3132
let config = FsiEvaluationSession.GetDefaultConfiguration()
32-
let argv = [| this.GetType().Assembly.Location; "--noninteractive"; "--targetprofile:netcore"; "--quiet" |]
33+
let baseArgs = [| this.GetType().Assembly.Location; "--noninteractive"; "--targetprofile:netcore"; "--quiet" |]
34+
let argv = Array.append baseArgs additionalArgs
3335
let fsi = FsiEvaluationSession.Create (config, argv, stdin, stdout, stderr, collectible=true)
3436

37+
member __.AssemblyReferenceAdded = fsi.AssemblyReferenceAdded
38+
3539
member __.ProvideInput = stdin.ProvideInput
3640

3741
member __.OutputProduced = outputProduced.Publish

src/fsharp/fsi/fsi.fs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,8 @@ type internal FsiDynamicCompiler
962962
let outfile = "TMPFSCI.exe"
963963
let assemblyName = "FSI-ASSEMBLY"
964964

965+
let assemblyReferenceAddedEvent = Control.Event<string>()
966+
965967
let mutable fragmentId = 0
966968
let mutable prevIt : ValRef option = None
967969

@@ -1245,7 +1247,7 @@ type internal FsiDynamicCompiler
12451247
let tcState = istate.tcState
12461248
let tcEnv,(_dllinfos,ccuinfos) =
12471249
try
1248-
RequireDLL (ctok, tcImports, tcState.TcEnvFromImpls, assemblyName, m, path)
1250+
RequireDLL (ctok, tcImports, tcState.TcEnvFromImpls, assemblyName, m, path, assemblyReferenceAddedEvent.Trigger)
12491251
with e ->
12501252
tcConfigB.RemoveReferencedAssemblyByPath(m,path)
12511253
reraise()
@@ -1334,6 +1336,8 @@ type internal FsiDynamicCompiler
13341336
member __.FormatValue(obj:obj, objTy) =
13351337
valuePrinter.FormatValue(obj, objTy)
13361338

1339+
member __.AssemblyReferenceAdded = assemblyReferenceAddedEvent.Publish
1340+
13371341
//----------------------------------------------------------------------------
13381342
// ctrl-c handling
13391343
//----------------------------------------------------------------------------
@@ -2640,6 +2644,9 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i
26402644
fsiInteractionProcessor.EvalScript(ctok, scriptPath, errorLogger)
26412645
|> commitResultNonThrowing errorOptions scriptPath errorLogger
26422646
|> function Choice1Of2 (_), errs -> Choice1Of2 (), errs | Choice2Of2 exn, errs -> Choice2Of2 exn, errs
2647+
2648+
/// Event fires every time an assembly reference is added to the execution environment, e.g., via `#r`.
2649+
member __.AssemblyReferenceAdded = fsiDynamicCompiler.AssemblyReferenceAdded
26432650

26442651
/// Performs these steps:
26452652
/// - Load the dummy interaction, if any

src/fsharp/fsi/fsi.fsi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,9 @@ type FsiEvaluationSession =
227227
/// A host calls this to report an unhandled exception in a standard way, e.g. an exception on the GUI thread gets printed to stderr
228228
member ReportUnhandledException : exn: exn -> unit
229229

230+
/// Event fires every time an assembly reference is added to the execution environment, e.g., via `#r`.
231+
member AssemblyReferenceAdded : IEvent<string>
232+
230233
/// Load the dummy interaction, load the initial files, and,
231234
/// if interacting, start the background thread to read the standard input.
232235
///

tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharpScriptTests.fs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace FSharp.Compiler.Scripting.UnitTests
44

55
open System
6+
open System.IO
67
open System.Threading
78
open FSharp.Compiler.Interactive.Shell
89
open FSharp.Compiler.Scripting
@@ -57,3 +58,27 @@ type InteractiveTests() =
5758
let value = opt.Value
5859
Assert.AreEqual(typeof<int>, value.ReflectionType)
5960
Assert.AreEqual(5, value.ReflectionValue :?> int)
61+
62+
[<Test>]
63+
member __.``Assembly reference event successful``() =
64+
use script = new FSharpScript()
65+
let testAssembly = "System.dll"
66+
let mutable assemblyResolveEventCount = 0
67+
let mutable foundAssemblyReference = false
68+
Event.add (fun (assembly: string) ->
69+
assemblyResolveEventCount <- assemblyResolveEventCount + 1
70+
foundAssemblyReference <- String.Compare(testAssembly, Path.GetFileName(assembly), StringComparison.OrdinalIgnoreCase) = 0)
71+
script.AssemblyReferenceAdded
72+
script.Eval(sprintf "#r \"%s\"" testAssembly) |> ignoreValue
73+
Assert.AreEqual(1, assemblyResolveEventCount)
74+
Assert.True(foundAssemblyReference)
75+
76+
[<Test>]
77+
member __.``Assembly reference event unsuccessful``() =
78+
use script = new FSharpScript()
79+
let testAssembly = "not-an-assembly-that-can-be-found.dll"
80+
let mutable foundAssemblyReference = false
81+
Event.add (fun _ -> foundAssemblyReference <- true) script.AssemblyReferenceAdded
82+
let _result, errors = script.Eval(sprintf "#r \"%s\"" testAssembly)
83+
Assert.AreEqual(1, errors.Length)
84+
Assert.False(foundAssemblyReference)

0 commit comments

Comments
 (0)