1515using Microsoft . Extensions . DependencyModel ;
1616using Newtonsoft . Json . Linq ;
1717using static Microsoft . DotNet . PlatformAbstractions . RuntimeEnvironment ;
18+ using ResolutionPolicyEvaluator = System . Func < System . Reflection . AssemblyName , System . Reflection . Assembly , bool > ;
1819
1920namespace Microsoft . Azure . WebJobs . Script . Description
2021{
@@ -23,11 +24,12 @@ namespace Microsoft.Azure.WebJobs.Script.Description
2324 /// </summary>
2425 public partial class FunctionAssemblyLoadContext : AssemblyLoadContext
2526 {
26- private readonly List < string > _probingPaths = new List < string > ( ) ;
27- private static readonly Lazy < string [ ] > _runtimeAssemblies = new Lazy < string [ ] > ( GetRuntimeAssemblies ) ;
28-
27+ private static readonly Lazy < Dictionary < string , ScriptRuntimeAssembly > > _runtimeAssemblies = new Lazy < Dictionary < string , ScriptRuntimeAssembly > > ( GetRuntimeAssemblies ) ;
28+ private static readonly Lazy < Dictionary < string , ResolutionPolicyEvaluator > > _resolutionPolicyEvaluators = new Lazy < Dictionary < string , ResolutionPolicyEvaluator > > ( InitializeLoadPolicyEvaluators ) ;
2929 private static Lazy < FunctionAssemblyLoadContext > _defaultContext = new Lazy < FunctionAssemblyLoadContext > ( ( ) => new FunctionAssemblyLoadContext ( ResolveFunctionBaseProbingPath ( ) ) , true ) ;
3030
31+ private readonly List < string > _probingPaths = new List < string > ( ) ;
32+
3133 public FunctionAssemblyLoadContext ( string basePath )
3234 {
3335 if ( basePath == null )
@@ -40,13 +42,55 @@ public FunctionAssemblyLoadContext(string basePath)
4042
4143 public static FunctionAssemblyLoadContext Shared => _defaultContext . Value ;
4244
45+ private static Dictionary < string , ResolutionPolicyEvaluator > InitializeLoadPolicyEvaluators ( )
46+ {
47+ return new Dictionary < string , ResolutionPolicyEvaluator >
48+ {
49+ { "minorMatchOrLower" , IsMinorMatchOrLowerPolicyEvaluator } ,
50+ { "runtimeVersion" , RuntimeVersionPolicyEvaluator }
51+ } ;
52+ }
53+
54+ /// <summary>
55+ /// A load policy evaluator that accepts the runtime assembly, regardless of version.
56+ /// </summary>
57+ /// <param name="requestedAssembly">The name of the requested assembly.</param>
58+ /// <param name="runtimeAssembly">The runtime assembly.</param>
59+ /// <returns>True if the evaluation succeeds.</returns>
60+ private static bool RuntimeVersionPolicyEvaluator ( AssemblyName requestedAssembly , Assembly runtimeAssembly ) => true ;
61+
62+ /// <summary>
63+ /// A load policy evaluator that verifies if the runtime package is the same major and
64+ /// newer minor version.
65+ /// </summary>
66+ /// <param name="requestedAssembly">The name of the requested assembly.</param>
67+ /// <param name="runtimeAssembly">The runtime assembly.</param>
68+ /// <returns>True if the evaluation succeeds.</returns>
69+ private static bool IsMinorMatchOrLowerPolicyEvaluator ( AssemblyName requestedAssembly , Assembly runtimeAssembly )
70+ {
71+ AssemblyName runtimeAssemblyName = AssemblyNameCache . GetName ( runtimeAssembly ) ;
72+
73+ return requestedAssembly . Version . Major == runtimeAssemblyName . Version . Major &&
74+ requestedAssembly . Version . Minor <= runtimeAssemblyName . Version . Minor ;
75+ }
76+
4377 private bool IsRuntimeAssembly ( AssemblyName assemblyName )
78+ => _runtimeAssemblies . Value . ContainsKey ( assemblyName . Name ) ;
79+
80+ private bool TryGetRuntimeAssembly ( AssemblyName assemblyName , out ScriptRuntimeAssembly assembly )
81+ => _runtimeAssemblies . Value . TryGetValue ( assemblyName . Name , out assembly ) ;
82+
83+ private ResolutionPolicyEvaluator GetResolutionPolicyEvaluator ( string policyName )
4484 {
45- return _runtimeAssemblies . Value . Contains ( assemblyName . Name ) ||
46- assemblyName . Name . StartsWith ( "system." , StringComparison . OrdinalIgnoreCase ) ;
85+ if ( _resolutionPolicyEvaluators . Value . TryGetValue ( policyName , out ResolutionPolicyEvaluator policy ) )
86+ {
87+ return policy ;
88+ }
89+
90+ throw new InvalidOperationException ( $ "'{ policyName } ' is not a valid assembly resolution policy") ;
4791 }
4892
49- private Assembly LoadInternal ( AssemblyName assemblyName )
93+ private Assembly LoadCore ( AssemblyName assemblyName )
5094 {
5195 foreach ( var probingPath in _probingPaths )
5296 {
@@ -70,16 +114,15 @@ protected override Assembly Load(AssemblyName assemblyName)
70114 return assembly ;
71115 }
72116
73- if ( IsRuntimeAssembly ( assemblyName ) )
117+ if ( TryGetRuntimeAssembly ( assemblyName , out ScriptRuntimeAssembly scriptRuntimeAssembly ) )
74118 {
75119 // If there was a failure loading a runtime assembly, ensure we gracefuly unify to
76120 // the runtime version of the assembly if the version falls within a safe range.
77121 if ( TryLoadRuntimeAssembly ( new AssemblyName ( assemblyName . Name ) , out assembly ) )
78122 {
79- AssemblyName runtimeAssemblyName = AssemblyNameCache . GetName ( assembly ) ;
123+ var policyEvaluator = GetResolutionPolicyEvaluator ( scriptRuntimeAssembly . ResolutionPolicy ) ;
80124
81- if ( runtimeAssemblyName . Version . Major == assemblyName . Version . Major &&
82- runtimeAssemblyName . Version . Minor == assemblyName . Version . Minor )
125+ if ( policyEvaluator . Invoke ( assemblyName , assembly ) )
83126 {
84127 return assembly ;
85128 }
@@ -88,7 +131,7 @@ protected override Assembly Load(AssemblyName assemblyName)
88131 return null ;
89132 }
90133
91- return LoadInternal ( assemblyName ) ;
134+ return LoadCore ( assemblyName ) ;
92135 }
93136
94137 private bool TryLoadRuntimeAssembly ( AssemblyName assemblyName , out Assembly assembly )
@@ -221,12 +264,14 @@ protected static string ResolveFunctionBaseProbingPath()
221264 return Path . Combine ( basePath , "bin" ) ;
222265 }
223266
224- private static string [ ] GetRuntimeAssemblies ( )
267+ private static Dictionary < string , ScriptRuntimeAssembly > GetRuntimeAssemblies ( )
225268 {
226269 string assembliesJson = GetRuntimeAssembliesJson ( ) ;
227270 JObject assemblies = JObject . Parse ( assembliesJson ) ;
228271
229- return assemblies [ "runtimeAssemblies" ] . ToObject < string [ ] > ( ) ;
272+ return assemblies [ "runtimeAssemblies" ]
273+ . ToObject < ScriptRuntimeAssembly [ ] > ( )
274+ . ToDictionary ( a => a . Name , StringComparer . OrdinalIgnoreCase ) ;
230275 }
231276
232277 private static string GetRuntimeAssembliesJson ( )
0 commit comments