|
1 | 1 | // Copyright (c) Microsoft Corporation. All rights reserved. |
2 | 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. |
3 | 3 |
|
| 4 | +#if NET |
| 5 | +using System.Reflection; |
| 6 | +using System.Runtime.Loader; |
| 7 | +#endif |
4 | 8 | using Microsoft.VisualStudio.Threading; |
5 | 9 | using Nerdbank; |
| 10 | +using StreamJsonRpc.Tests; |
6 | 11 | using ExAssembly = StreamJsonRpc.Tests.ExternalAssembly; |
7 | 12 |
|
8 | 13 | public class JsonRpcProxyGenerationTests : TestBase |
@@ -135,6 +140,11 @@ public interface IServerWithGenericMethod |
135 | 140 | Task AddAsync<T>(T a, T b); |
136 | 141 | } |
137 | 142 |
|
| 143 | + public interface IReferenceAnUnreachableAssembly |
| 144 | + { |
| 145 | + Task TakeAsync(UnreachableAssembly.SomeUnreachableClass obj); |
| 146 | + } |
| 147 | + |
138 | 148 | internal interface IServerInternal : |
139 | 149 | ExAssembly.ISomeInternalProxyInterface, |
140 | 150 | IServerInternalWithInternalTypesFromOtherAssemblies, |
@@ -810,6 +820,51 @@ public void ReuseDynamicAssembliesTest() |
810 | 820 | Assert.Same(proxy1.GetType().Assembly, proxy2.GetType().Assembly); |
811 | 821 | } |
812 | 822 |
|
| 823 | +#if NET |
| 824 | + [Fact] |
| 825 | + public void DynamicAssembliesKeyedByAssemblyLoadContext() |
| 826 | + { |
| 827 | + UnreachableAssemblyTools.VerifyUnreachableAssembly(); |
| 828 | + |
| 829 | + // Set up a new ALC that can find the hidden assembly, and ask for the proxy type. |
| 830 | + AssemblyLoadContext alc = UnreachableAssemblyTools.CreateContextForReachingTheUnreachable(); |
| 831 | + |
| 832 | + JsonRpc clientRpc = new(Stream.Null); |
| 833 | + |
| 834 | + // Ensure we first generate a proxy in our own default ALC. |
| 835 | + // The goal being to emit a DynamicAssembly that we *might* reuse |
| 836 | + // for the later proxy for which the first DynamicAssembly is not appropriate. |
| 837 | + clientRpc.Attach<IServer>(); |
| 838 | + |
| 839 | + // Now take very specific steps to invoke the rest of the test in the other AssemblyLoadContext. |
| 840 | + // This is important so that our IReferenceAnUnreachableAssembly type will be able to resolve its |
| 841 | + // own type references to UnreachableAssembly.dll, which our own default ALC cannot do. |
| 842 | + MethodInfo helperMethodInfo = typeof(JsonRpcProxyGenerationTests).GetMethod(nameof(DynamicAssembliesKeyedByAssemblyLoadContext_Helper), BindingFlags.NonPublic | BindingFlags.Static)!; |
| 843 | + MethodInfo helperWithinAlc = UnreachableAssemblyTools.LoadHelperInAlc(alc, helperMethodInfo); |
| 844 | + helperWithinAlc.Invoke(null, null); |
| 845 | + } |
| 846 | + |
| 847 | + private static void DynamicAssembliesKeyedByAssemblyLoadContext_Helper() |
| 848 | + { |
| 849 | + // Although this method executes within the special ALC, |
| 850 | + // StreamJsonRpc is loaded in the default ALC. |
| 851 | + // Therefore unless StreamJsonRpc is taking care to use a DynamicAssembly |
| 852 | + // that belongs to *this* ALC, it won't be able to resolve the same type references |
| 853 | + // that we can here (the ones from UnreachableAssembly). |
| 854 | + // That's what makes this test effective: it'll fail if the DynamicAssembly is shared across ALCs, |
| 855 | + // thereby verifying that StreamJsonRpc has a dedicated set of DynamicAssemblies for each ALC. |
| 856 | + // We have to manually set the contextual reflection context to this special ALC |
| 857 | + // so that StreamJsonRpc knows to get the DynamicAssembly for this ALC. |
| 858 | + // Otherwise it will default to its own. |
| 859 | + using (AssemblyLoadContext.EnterContextualReflection(MethodBase.GetCurrentMethod()!.DeclaringType!.Assembly)) |
| 860 | + { |
| 861 | + JsonRpc clientRpc = new(Stream.Null); |
| 862 | + clientRpc.Attach<IReferenceAnUnreachableAssembly>(); |
| 863 | + } |
| 864 | + } |
| 865 | + |
| 866 | +#endif |
| 867 | + |
813 | 868 | public class EmptyClass |
814 | 869 | { |
815 | 870 | } |
|
0 commit comments