15
15
using Microsoft . Extensions . DependencyModel ;
16
16
using Newtonsoft . Json . Linq ;
17
17
using static Microsoft . DotNet . PlatformAbstractions . RuntimeEnvironment ;
18
+ using ResolutionPolicyEvaluator = System . Func < System . Reflection . AssemblyName , System . Reflection . Assembly , bool > ;
18
19
19
20
namespace Microsoft . Azure . WebJobs . Script . Description
20
21
{
@@ -23,11 +24,12 @@ namespace Microsoft.Azure.WebJobs.Script.Description
23
24
/// </summary>
24
25
public partial class FunctionAssemblyLoadContext : AssemblyLoadContext
25
26
{
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 ) ;
29
29
private static Lazy < FunctionAssemblyLoadContext > _defaultContext = new Lazy < FunctionAssemblyLoadContext > ( ( ) => new FunctionAssemblyLoadContext ( ResolveFunctionBaseProbingPath ( ) ) , true ) ;
30
30
31
+ private readonly List < string > _probingPaths = new List < string > ( ) ;
32
+
31
33
public FunctionAssemblyLoadContext ( string basePath )
32
34
{
33
35
if ( basePath == null )
@@ -40,13 +42,55 @@ public FunctionAssemblyLoadContext(string basePath)
40
42
41
43
public static FunctionAssemblyLoadContext Shared => _defaultContext . Value ;
42
44
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
+
43
77
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 )
44
84
{
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") ;
47
91
}
48
92
49
- private Assembly LoadInternal ( AssemblyName assemblyName )
93
+ private Assembly LoadCore ( AssemblyName assemblyName )
50
94
{
51
95
foreach ( var probingPath in _probingPaths )
52
96
{
@@ -70,16 +114,15 @@ protected override Assembly Load(AssemblyName assemblyName)
70
114
return assembly ;
71
115
}
72
116
73
- if ( IsRuntimeAssembly ( assemblyName ) )
117
+ if ( TryGetRuntimeAssembly ( assemblyName , out ScriptRuntimeAssembly scriptRuntimeAssembly ) )
74
118
{
75
119
// If there was a failure loading a runtime assembly, ensure we gracefuly unify to
76
120
// the runtime version of the assembly if the version falls within a safe range.
77
121
if ( TryLoadRuntimeAssembly ( new AssemblyName ( assemblyName . Name ) , out assembly ) )
78
122
{
79
- AssemblyName runtimeAssemblyName = AssemblyNameCache . GetName ( assembly ) ;
123
+ var policyEvaluator = GetResolutionPolicyEvaluator ( scriptRuntimeAssembly . ResolutionPolicy ) ;
80
124
81
- if ( runtimeAssemblyName . Version . Major == assemblyName . Version . Major &&
82
- runtimeAssemblyName . Version . Minor == assemblyName . Version . Minor )
125
+ if ( policyEvaluator . Invoke ( assemblyName , assembly ) )
83
126
{
84
127
return assembly ;
85
128
}
@@ -88,7 +131,7 @@ protected override Assembly Load(AssemblyName assemblyName)
88
131
return null ;
89
132
}
90
133
91
- return LoadInternal ( assemblyName ) ;
134
+ return LoadCore ( assemblyName ) ;
92
135
}
93
136
94
137
private bool TryLoadRuntimeAssembly ( AssemblyName assemblyName , out Assembly assembly )
@@ -221,12 +264,14 @@ protected static string ResolveFunctionBaseProbingPath()
221
264
return Path . Combine ( basePath , "bin" ) ;
222
265
}
223
266
224
- private static string [ ] GetRuntimeAssemblies ( )
267
+ private static Dictionary < string , ScriptRuntimeAssembly > GetRuntimeAssemblies ( )
225
268
{
226
269
string assembliesJson = GetRuntimeAssembliesJson ( ) ;
227
270
JObject assemblies = JObject . Parse ( assembliesJson ) ;
228
271
229
- return assemblies [ "runtimeAssemblies" ] . ToObject < string [ ] > ( ) ;
272
+ return assemblies [ "runtimeAssemblies" ]
273
+ . ToObject < ScriptRuntimeAssembly [ ] > ( )
274
+ . ToDictionary ( a => a . Name , StringComparer . OrdinalIgnoreCase ) ;
230
275
}
231
276
232
277
private static string GetRuntimeAssembliesJson ( )
0 commit comments