-
Notifications
You must be signed in to change notification settings - Fork 234
/
Copy pathEditorExtensionServiceProvider.cs
203 lines (171 loc) · 9.1 KB
/
EditorExtensionServiceProvider.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.PowerShell.EditorServices.Services.Extension;
using Microsoft.PowerShell.EditorServices.Utility;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
using InternalServices = Microsoft.PowerShell.EditorServices.Services;
namespace Microsoft.PowerShell.EditorServices.Extensions.Services
{
/// <summary>
/// Object to provide extension service APIs to extensions to PSES.
/// </summary>
public class EditorExtensionServiceProvider
{
private static readonly Assembly s_psesAsm = typeof(EditorExtensionServiceProvider).Assembly;
private static readonly Lazy<object> s_psesAsmLoadContextLazy = new(GetPsesAsmLoadContext);
private static readonly Lazy<Type> s_asmLoadContextType = new(() => Type.GetType("System.Runtime.Loader.AssemblyLoadContext"));
private static readonly Lazy<Func<IDisposable>> s_enterPsesReflectionContextLazy = new(GetPsesAlcReflectionContextEntryFunc);
private static readonly Lazy<Func<string, Assembly>> s_loadAssemblyInPsesAlc = new(GetPsesAlcLoadAsmFunc);
private static Type AsmLoadContextType => s_asmLoadContextType.Value;
private static object PsesAssemblyLoadContext => s_psesAsmLoadContextLazy.Value;
private static Func<IDisposable> EnterPsesAlcReflectionContext => s_enterPsesReflectionContextLazy.Value;
private static Func<string, Assembly> LoadAssemblyInPsesAlc => s_loadAssemblyInPsesAlc.Value;
private readonly IServiceProvider _serviceProvider;
internal EditorExtensionServiceProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
LanguageServer = new LanguageServerService(_serviceProvider.GetService<ILanguageServerFacade>());
ExtensionCommands = new ExtensionCommandService(_serviceProvider.GetService<ExtensionService>());
Workspace = new WorkspaceService(_serviceProvider.GetService<InternalServices.WorkspaceService>());
EditorContext = new EditorContextService(_serviceProvider.GetService<ILanguageServerFacade>());
EditorUI = new EditorUIService(_serviceProvider.GetService<ILanguageServerFacade>());
}
/// <summary>
/// A service wrapper around the language server allowing sending notifications and requests to the LSP client.
/// </summary>
public ILanguageServerService LanguageServer { get; }
/// <summary>
/// Service providing extension command registration and functionality.
/// </summary>
public IExtensionCommandService ExtensionCommands { get; }
/// <summary>
/// Service providing editor workspace functionality.
/// </summary>
public IWorkspaceService Workspace { get; }
/// <summary>
/// Service providing current editor context functionality.
/// </summary>
public IEditorContextService EditorContext { get; }
/// <summary>
/// Service providing editor UI functionality.
/// </summary>
public IEditorUIService EditorUI { get; }
/// <summary>
/// Get an underlying service object from PSES by type name.
/// </summary>
/// <param name="psesServiceFullTypeName">The full type name of the service to get.</param>
/// <returns>The service object requested, or null if no service of that type name exists.</returns>
/// <remarks>
/// This method is intended as a trapdoor and should not be used in the first instance.
/// Consider using the public extension services if possible.
/// </remarks>
public object GetService(string psesServiceFullTypeName) => GetService(psesServiceFullTypeName, "Microsoft.PowerShell.EditorServices");
/// <summary>
/// Get an underlying service object from PSES by type name.
/// </summary>
/// <param name="fullTypeName">The full type name of the service to get.</param>
/// <param name="assemblyName">The assembly name from which the service comes.</param>
/// <returns>The service object requested, or null if no service of that type name exists.</returns>
/// <remarks>
/// This method is intended as a trapdoor and should not be used in the first instance.
/// Consider using the public extension services if possible.
/// </remarks>
public object GetService(string fullTypeName, string assemblyName)
{
string asmQualifiedName = $"{fullTypeName}, {assemblyName}";
return GetServiceByAssemblyQualifiedName(asmQualifiedName);
}
/// <summary>
/// Get a PSES service by its fully assembly qualified name.
/// </summary>
/// <param name="asmQualifiedTypeName">The fully assembly qualified name of the service type to load.</param>
/// <returns>The service corresponding to the given type, or null if none was found.</returns>
/// <remarks>
/// It's not recommended to run this method in parallel with anything,
/// since the global reflection context change may have side effects in other threads.
/// </remarks>
public object GetServiceByAssemblyQualifiedName(string asmQualifiedTypeName)
{
Type serviceType;
if (VersionUtils.IsNetCore)
{
using (EnterPsesAlcReflectionContext())
{
serviceType = s_psesAsm.GetType(asmQualifiedTypeName);
}
}
else
{
serviceType = Type.GetType(asmQualifiedTypeName);
}
return GetService(serviceType);
}
/// <summary>
/// Get an underlying service object from PSES by type name.
/// </summary>
/// <param name="serviceType">The type of the service to fetch.</param>
/// <returns>The service object requested, or null if no service of that type name exists.</returns>
/// <remarks>
/// This method is intended as a trapdoor and should not be used in the first instance.
/// Consider using the public extension services if possible.
/// Also note that services in PSES may live in a separate assembly load context,
/// meaning that a type of the seemingly correct name may fail to fetch to a service
/// that is known under a type of the same name but loaded in a different context.
/// </remarks>
public object GetService(Type serviceType) => _serviceProvider.GetService(serviceType);
/// <summary>
/// Get the assembly load context the PSES loads its dependencies into.
/// In .NET Framework, this returns null.
/// </summary>
/// <returns>The assembly load context used for loading PSES, or null in .NET Framework.</returns>
public static object GetPsesAssemblyLoadContext()
{
if (!VersionUtils.IsNetCore)
{
return null;
}
return PsesAssemblyLoadContext;
}
/// <summary>
/// Load the given assembly in the PSES assembly load context.
/// In .NET Framework, this simple loads the assembly in the LoadFrom context.
/// </summary>
/// <param name="assemblyPath">The absolute path of the assembly to load.</param>
/// <returns>The loaded assembly object.</returns>
public static Assembly LoadAssemblyInPsesLoadContext(string assemblyPath)
{
if (!VersionUtils.IsNetCore)
{
return Assembly.LoadFrom(assemblyPath);
}
return LoadAssemblyInPsesAlc(assemblyPath);
}
private static Func<IDisposable> GetPsesAlcReflectionContextEntryFunc()
{
MethodInfo enterReflectionContextMethod = AsmLoadContextType.GetMethod("EnterContextualReflection", BindingFlags.Public | BindingFlags.Instance);
return Expression.Lambda<Func<IDisposable>>(
Expression.Convert(
Expression.Call(Expression.Constant(PsesAssemblyLoadContext), enterReflectionContextMethod),
typeof(IDisposable))).Compile();
}
private static Func<string, Assembly> GetPsesAlcLoadAsmFunc()
{
MethodInfo loadFromAssemblyPathMethod = AsmLoadContextType.GetMethod("LoadFromAssemblyPath", BindingFlags.Public | BindingFlags.Instance);
return (Func<string, Assembly>)loadFromAssemblyPathMethod.CreateDelegate(typeof(Func<string, Assembly>), PsesAssemblyLoadContext);
}
private static object GetPsesAsmLoadContext()
{
if (!VersionUtils.IsNetCore)
{
return null;
}
Type alcType = Type.GetType("System.Runtime.Loader.AssemblyLoadContext");
MethodInfo getAlcMethod = alcType.GetMethod("GetLoadContext", BindingFlags.Public | BindingFlags.Static);
return getAlcMethod.Invoke(obj: null, new object[] { s_psesAsm });
}
}
}