core
core copied to clipboard
Type provider assembly load issues
TPs can load assemblies and lock them in main context of booster process. Test using AssemblyLoadContext for F# compilations
Example error:
The type initializer for 'WebSharper.Moment.Tests.Site' threw an exception. ---> System.TypeInitializationException: The type initializer for '<StartupCode$WebSharper-Moment-Tests>.$Samples' threw an exception. ---> System.BadImageFormatException: Could not load file or assembly 'Microsoft.AspNetCore.Mvc.Abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. Reference assemblies should not be loaded for execution. They can only be loaded in the Reflection-only loader context. (0x80131058)File name: 'Microsoft.AspNetCore.Mvc.Abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60' ---> System.BadImageFormatException: Could not load file or assembly 'Microsoft.AspNetCore.Mvc.Abstractions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. Reference assemblies should not be loaded for execution. They can only be loaded in the Reflection-only loader context. (0x80131058)File name: 'Microsoft.AspNetCore.Mvc.Abstractions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60' ---> System.BadImageFormatException: Cannot load a reference assembly for execution. at System.Runtime.Loader.AssemblyLoadContext.LoadFromPath(IntPtr ptrNativeAssemblyLoadContext, String ilPath, String niPath, ObjectHandleOnStack retAssembly) at System.Runtime.Loader.AssemblyLoadContext.LoadFromAssemblyPath(String assemblyPath) at System.Reflection.Assembly.LoadFrom(String assemblyFile) at WebSharper.UI.Templating.TemplatingProvider.ResolveAssembly(ResolveEventArgs args) at System.Runtime.Loader.AssemblyLoadContext.InvokeResolveEvent(ResolveEventHandler eventHandler, RuntimeAssembly assembly, String name) at System.Runtime.Loader.AssemblyLoadContext.OnAssemblyResolve(RuntimeAssembly assembly, String assemblyFullName) at <StartupCode$WebSharper-Moment-Tests>.$Samples..cctor() --- End of inner exception stack trace --- at WebSharper.Moment.Tests.Site..cctor() --- End of inner exception stack trace --- at WebSharper.Moment.Tests.Site.get_Main() at WebSharper.Moment.Tests.Website.WebSharper.Sitelets.IWebsite<WebSharper.Moment.Tests.Action>.get_Sitelet() at WebSharper.Sitelets.Utils.GetSitelet@60.WebSharper.Sitelets.Specialization.IGeneric<System.Object, System.Tuple<WebSharper.Sitelets.Sitelet<System.Object>, Microsoft.FSharp.Collections.List<System.Object>>>.Run[T](Object website) at WebSharper.Sitelets.Offline.HtmlCommand.WebSharper.Compiler.HtmlCommand.IHtmlCommand.Execute(Environment env, Config options) at WebSharper.Compiler.FSharp.Compile.Compile$cont@199-2(WsConfig config, WarnSettings warnSettings, LoggerBase logger, Boolean isBundleOnly, AssemblyResolver aR, Loader loader, FSharpList`1 refs, Task`1 wsRefsMeta, Compilation comp, Unit unitVar) at WebSharper.Compiler.FSharp.Compile.Compile$cont@129-1(WsConfig config, WarnSettings warnSettings, LoggerBase logger, FSharpFunc`2 tryGetMetadata, String thisName, FSharpChecker checker, Boolean isBundleOnly, Unit unitVar) at WebSharper.Compiler.FSharp.Compile.Compile$cont@94(WsConfig config, WarnSettings warnSettings, LoggerBase logger, FSharpFunc`2 checkerFactory, FSharpFunc`2 tryGetMetadata, String thisName, Unit unitVar) at WebSharper.Compiler.FSharp.Compile.Compile(WsConfig config, WarnSettings warnSettings, LoggerBase logger, FSharpFunc`2 checkerFactory, FSharpFunc`2 tryGetMetadata) at Program.compilationResultForDebugOrRelease@133(FSharpFunc`2 checkerFactory, FSharpFunc`2 tryGetMetadata, ArgsType deserializedMessage, LoggerBase logger, Unit unitVar0)
TP is called twice per WS compilation, F# compilation and then for inspecting type checking results. The latter is never disposed and the assembly resolver event handler added in ProvidedTypes.fs causes the issue. The first compile step IsInvalidationSupported=false, for latter (used for code service or in case of WS, AST inspection) has IsInvalidationSupported=true.
Idk how I can make FCS to dispose of TP instances in the second case checker or TC results objects are non-disposable. But I can look for IsInvalidationSupported=true and not add the problematic assembly resolve handler. WS can still inspect AST, because project cache is not invalidated, so the new TP instance is not even used... (I can verify this bc test projects still pass). But also I need to check somehow that we are inside a WS compilation, otherwise it breaks code service.
Probably related: bumping the WebSharper.UI dependency to a newer version (from 5.0.0.102-preview1 to 5.0.0.105-preview1) in my test project, I get:
FSC : parse error FS3031: The type provider 'C:\Users\adam.granicz\.nuget\packages\websharper.ui\5.0.0.105-
preview1\lib\netstandard2.0\WebSharper.UI.Templating.dll' reported an error: Assembly attribute
'TypeProviderAssemblyAttribute' refers to a designer assembly 'C:\Users\adam.granicz\.nuget\packages\websharper.ui
\5.0.0.105-preview1\lib\netstandard2.0\WebSharper.UI.Templating.dll' which cannot be loaded from path
'C:\Users\adam.granicz\.nuget\packages\websharper.ui\5.0.0.105-preview1\lib\netstandard2.0\WebSharper.UI.Templating.dll'.
The exception reported was: System.IO.FileLoadException - Assembly with same name is already loaded
[C:\granicz\MyMin01\MyMin01.fsproj]
Once I shut the Booster down with dotnet ws stop and recompile, things work as expected.
@granicz Yes, sadly F# compiler locks in TP assemblies, this is an annoyance when developing TPs in Visual Studio too... I see no other workaround for now than wsfscservice somehow recognizing that a new TP version is needed and starting another instance of itself... but then interprocess communication scenario becomes more complicated.