Skip to content

Commit 35a5b43

Browse files
committed
Initial round of assembly resolution and loading
improvements. Isolating extension and function assemblies into distinct load contexts.
1 parent ad8a2d9 commit 35a5b43

37 files changed

+826
-511
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Publish
1313
*.Cache
1414
msbuild.log
1515
package-lock.json
16+
**/Properties/launchSettings.json
1617

1718
/packages
1819
/TestResults

build/common.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<Project>
22
<PropertyGroup>
33
<TargetFramework>netstandard2.0</TargetFramework>
4+
<LangVersion>7.2</LangVersion>
45
<BuildNumber Condition=" '$(BuildNumber)' == '' ">1</BuildNumber>
56
<MajorMinorProductVersion>2.0</MajorMinorProductVersion>
67
<Version>$(MajorMinorProductVersion).0-beta2$(VersionSuffix)</Version>

src/WebJobs.Script.WebHost/Properties/launchSettings.json

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/WebJobs.Script/Binding/BuiltinExtensionBindingProvider.cs

Lines changed: 0 additions & 60 deletions
This file was deleted.

src/WebJobs.Script/Binding/Extensibility/ScriptBindingProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System.Reflection;
5+
using System.Runtime.Loader;
56
using Microsoft.Extensions.Logging;
67
using Newtonsoft.Json.Linq;
78

@@ -64,7 +65,7 @@ public virtual void Initialize()
6465
/// <param name="assemblyName">The name of the assembly to resolve.</param>
6566
/// <param name="assembly">The assembly if we were able to resolve.</param>
6667
/// <returns>True if the assembly could be resolved, false otherwise.</returns>
67-
public virtual bool TryResolveAssembly(string assemblyName, out Assembly assembly)
68+
public virtual bool TryResolveAssembly(string assemblyName, AssemblyLoadContext targetContext, out Assembly assembly)
6869
{
6970
assembly = null;
7071
return false;

src/WebJobs.Script/Binding/ExtensionLoader.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ public void LoadCustomExtensions()
7272
{
7373
string extensionName = item.Name ?? item.TypeName;
7474
_logger.LogInformation($"Loading custom extension '{extensionName}'");
75-
7675
Type extensionType = Type.GetType(item.TypeName,
7776
assemblyName =>
7877
{

src/WebJobs.Script/Binding/GeneralScriptBindingProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.ObjectModel;
66
using System.IO;
77
using System.Reflection;
8+
using System.Runtime.Loader;
89
using Microsoft.Azure.WebJobs.Host;
910
using Microsoft.Azure.WebJobs.Script.Description;
1011
using Microsoft.Azure.WebJobs.Script.Extensibility;
@@ -60,7 +61,7 @@ public override bool TryCreate(ScriptBindingContext context, out ScriptBinding b
6061
return true;
6162
}
6263

63-
public override bool TryResolveAssembly(string assemblyName, out Assembly assembly)
64+
public override bool TryResolveAssembly(string assemblyName, AssemblyLoadContext targetContext, out Assembly assembly)
6465
{
6566
return _metadataProvider.TryResolveAssembly(assemblyName, out assembly);
6667
}

src/WebJobs.Script/Description/DotNet/Compilation/CSharp/CSharpCompilation.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public ImmutableArray<Diagnostic> GetDiagnostics()
3838
return _compilation.WithAnalyzers(GetAnalyzers()).GetAllDiagnosticsAsync().Result;
3939
}
4040

41-
public FunctionSignature GetEntryPointSignature(IFunctionEntryPointResolver entryPointResolver)
41+
public FunctionSignature GetEntryPointSignature(IFunctionEntryPointResolver entryPointResolver, Assembly functionAssembly)
4242
{
4343
if (!_compilation.SyntaxTrees.Any())
4444
{
@@ -90,7 +90,7 @@ private static bool IsOrUsesAssemblyType(ITypeSymbol typeSymbol, IAssemblySymbol
9090

9191
async Task<object> ICompilation.EmitAsync(CancellationToken cancellationToken) => await EmitAsync(cancellationToken);
9292

93-
public async Task<Assembly> EmitAsync(CancellationToken cancellationToken)
93+
public async Task<DotNetCompilationResult> EmitAsync(CancellationToken cancellationToken)
9494
{
9595
try
9696
{
@@ -113,7 +113,7 @@ public async Task<Assembly> EmitAsync(CancellationToken cancellationToken)
113113
// and if so quit here.
114114
cancellationToken.ThrowIfCancellationRequested();
115115

116-
return Assembly.Load(assemblyStream.GetBuffer(), pdbStream.GetBuffer());
116+
return DotNetCompilationResult.FromBytes(assemblyStream.GetBuffer(), pdbStream.GetBuffer());
117117
}
118118
}
119119
catch (Exception exc) when (!(exc is CompilationErrorException))

src/WebJobs.Script/Description/DotNet/Compilation/CSharp/CSharpCompilationService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ private Compilation GetScriptCompilation(Script<object> script, FunctionMetadata
8282
.AddSyntaxTrees(scriptTree);
8383

8484
return compilation.WithOptions(compilation.Options.WithOptimizationLevel(_optimizationLevel))
85-
.WithAssemblyName(FunctionAssemblyLoader.GetAssemblyNameFromMetadata(functionMetadata, compilation.AssemblyName));
85+
.WithAssemblyName(Utility.GetAssemblyNameFromMetadata(functionMetadata, compilation.AssemblyName));
8686
}
8787
}
8888
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.IO;
6+
using System.Reflection;
7+
using Microsoft.Extensions.Logging;
8+
9+
namespace Microsoft.Azure.WebJobs.Script.Description
10+
{
11+
public abstract class DotNetCompilationResult
12+
{
13+
public static DotNetCompilationResult FromBytes(byte[] assemblyBytes, byte[] pdbBytes = null)
14+
=> new BinaryCompilationResult(assemblyBytes, pdbBytes);
15+
16+
public static DotNetCompilationResult FromStream(Stream assemblyStream, Stream pdbStream = null)
17+
=> new BinaryCompilationResult(assemblyStream, pdbStream);
18+
19+
public static DotNetCompilationResult FromPath(string assemblyPath)
20+
=> new PathCompilationResult(assemblyPath);
21+
22+
public abstract Assembly Load(FunctionMetadata metadata, IFunctionMetadataResolver metadataResolver, ILogger logger);
23+
24+
private sealed class BinaryCompilationResult : DotNetCompilationResult
25+
{
26+
public BinaryCompilationResult(Stream assemblyBytes)
27+
: this(assemblyBytes, null)
28+
{
29+
}
30+
31+
public BinaryCompilationResult(Stream assemblyStream, Stream pdbStream)
32+
{
33+
AssemblyStream = assemblyStream ?? throw new ArgumentNullException(nameof(assemblyStream));
34+
PdbStream = pdbStream;
35+
}
36+
37+
public BinaryCompilationResult(byte[] assemblyBytes, byte[] pdbBytes)
38+
{
39+
if (assemblyBytes == null)
40+
{
41+
throw new ArgumentNullException(nameof(assemblyBytes));
42+
}
43+
44+
AssemblyStream = new MemoryStream(assemblyBytes);
45+
46+
if (pdbBytes != null)
47+
{
48+
PdbStream = new MemoryStream(pdbBytes);
49+
}
50+
}
51+
52+
public Stream AssemblyStream { get; }
53+
54+
public Stream PdbStream { get; }
55+
56+
public override Assembly Load(FunctionMetadata metadata, IFunctionMetadataResolver metadataResolver, ILogger logger)
57+
{
58+
var context = new DynamicFunctionAssemblyLoadContext(metadata, metadataResolver, logger);
59+
60+
AssemblyStream.Position = 0;
61+
62+
if (PdbStream != null)
63+
{
64+
PdbStream.Position = 0;
65+
}
66+
67+
return context.LoadFromStream(AssemblyStream, PdbStream);
68+
}
69+
}
70+
71+
private sealed class PathCompilationResult : DotNetCompilationResult
72+
{
73+
public PathCompilationResult(string assemblyPath)
74+
{
75+
if (string.IsNullOrWhiteSpace(assemblyPath))
76+
{
77+
throw new ArgumentException("Invalid path string", nameof(assemblyPath));
78+
}
79+
80+
AssemblyPath = assemblyPath;
81+
}
82+
83+
public string AssemblyPath { get; }
84+
85+
public override Assembly Load(FunctionMetadata metadata, IFunctionMetadataResolver metadataResolver, ILogger logger)
86+
=> FunctionAssemblyLoadContext.Shared.LoadFromAssemblyPath(AssemblyPath);
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)