@@ -175,7 +175,7 @@ private PowerShellAssemblyLoadContext(string basePaths, bool useResolvingHandler
175175 /// <summary>
176176 /// Singleton instance of PowerShellAssemblyLoadContext
177177 /// </summary>
178- public static PowerShellAssemblyLoadContext Instance
178+ internal static PowerShellAssemblyLoadContext Instance
179179 {
180180 get ; private set ;
181181 }
@@ -376,7 +376,19 @@ internal IEnumerable<Assembly> GetAssemblies(string namespaceQualifiedTypeName)
376376 string tpaStrongName ;
377377 if ( coreClrTypeCatalog . TryGetValue ( namespaceQualifiedTypeName , out tpaStrongName ) )
378378 {
379- return new Assembly [ ] { GetTrustedPlatformAssembly ( tpaStrongName ) } ;
379+ try
380+ {
381+ return new Assembly [ ] { GetTrustedPlatformAssembly ( tpaStrongName ) } ;
382+ }
383+ catch ( FileNotFoundException )
384+ {
385+ // It's possible that the type catalog generated in OPS contains more entries than
386+ // the one generated in windows build. This is because in OPS we have more freedom
387+ // to control what packages to depend on, such as Json.NET.
388+ // If we deploy the PSALC.dll generated from OPS to NanoServer, then it's possible
389+ // that 'GetTrustedPlatformAssembly(tpaStrongName)' may fail for such entries. In
390+ // this case, we ignore the exception and return our cached assemblies.
391+ }
380392 }
381393 }
382394
@@ -459,6 +471,15 @@ internal IEnumerable<string> GetAvailableDotNetTypes()
459471 /// <summary>
460472 /// Set the profile optimization root on the appropriate load context
461473 /// </summary>
474+ /// <remarks>
475+ /// When using PS ALC as a full fledged ALC in OPS, we don't enable profile optimization.
476+ /// This is because PS assemblies will be recorded in the profile, and the next time OPS
477+ /// starts up, the default context will load the PS assemblies pretty early to ngen them
478+ /// in another CPU core, so our Load override won't track the loading of them, and thus
479+ /// OPS will fail to work.
480+ /// The root cause is that dotnet.exe put all PS assemblies in TPA list. If PS assemblies
481+ /// are not in TPA list, then we can enable profile optimization without a problem.
482+ /// </remarks>
462483 internal void SetProfileOptimizationRootImpl ( string directoryPath )
463484 {
464485 if ( this . useResolvingHandlerOnly )
@@ -468,6 +489,15 @@ internal void SetProfileOptimizationRootImpl(string directoryPath)
468489 /// <summary>
469490 /// Start the profile optimization on the appropriate load context
470491 /// </summary>
492+ /// <remarks>
493+ /// When using PS ALC as a full fledged ALC in OPS, we don't enable profile optimization.
494+ /// This is because PS assemblies will be recorded in the profile, and the next time OPS
495+ /// starts up, the default context will load the PS assemblies pretty early to ngen them
496+ /// in another CPU core, so our Load override won't track the loading of them, and thus
497+ /// OPS will fail to work.
498+ /// The root cause is that dotnet.exe put all PS assemblies in TPA list. If PS assemblies
499+ /// are not in TPA list, then we can enable profile optimization without a problem.
500+ /// </remarks>
471501 internal void StartProfileOptimizationImpl ( string profile )
472502 {
473503 if ( this . useResolvingHandlerOnly )
@@ -620,38 +650,15 @@ private bool IsAssemblyMatching(AssemblyName requestedAssembly, AssemblyName loa
620650 /// </param>
621651 private Assembly GetTrustedPlatformAssembly ( string tpaStrongName )
622652 {
623- Assembly asmLoaded ;
653+ // We always depend on the default context to load the TPAs that are recorded in
654+ // the type catalog.
655+ // - If the requested TPA is already loaded, then 'Assembly.Load' will just get
656+ // it back from the cache of default context.
657+ // - If the requested TPA is not loaded yet, then 'Assembly.Load' will make the
658+ // default context to load it
624659 AssemblyName assemblyName = new AssemblyName ( tpaStrongName ) ;
625-
626- // With the current standalone-app model of OPS, .NET Core libraries and PS assemblies are mixed together in one folder.
627- // So when using PSALC as a full fledged ALC in OPS, some TPAs might be loaded by our Load override. In that case, if we
628- // alwasy call Assembly.Load here to get a TPA, we might end up with a different Assembly instance of the the same TPA
629- // loaded in the default load context. We want to use the same assembly instance for type resolution in PS to avoid creating
630- // types and running .NET code from different assembly instances of the same DLL. Therefore, we try our cache first to see
631- // if the requested TPA is already loaded. If so, we use that one. If not, we load it in default context using Assembly.Load.
632- // Once a TPA is loaded in the default context, the same Assembly instance will always be used by custom ALC's when they attempt
633- // to resolve an "Assembly.Load" request for the same TPA.
634- //
635- // For in-box PS of NanoServer/IoT and the share-framework host model of OPS, we don't have the mixed libraries/assemblies
636- // problem, and TPAs are always resolved/loaded by the default context. In those cases, checking our cache would be unnecessary,
637- // but it won't cause any problems.
638-
639- // Probe the assembly cache
640- if ( TryGetAssemblyFromCache ( assemblyName , out asmLoaded ) )
641- return asmLoaded ;
642-
643- // Prepare to load the assembly
644- lock ( syncObj )
645- {
646- // Probe the cache again in case it's already loaded
647- if ( TryGetAssemblyFromCache ( assemblyName , out asmLoaded ) )
648- return asmLoaded ;
649-
650- // The requested TPA is not loaded by PS ALC, so load it in the default load context using Assembly.Load.
651- // There is no need to add it to our cache. It's cached in the default context.
652- asmLoaded = Assembly . Load ( assemblyName ) ;
653- return asmLoaded ;
654- }
660+ Assembly asmLoaded = Assembly . Load ( assemblyName ) ;
661+ return asmLoaded ;
655662 }
656663
657664 /// <summary>
0 commit comments