Merge pull request #1328 from PowerShell/dongbo/type-completion
Fix issue #682 - [system.manage<Tab>] doesn't work
This commit is contained in:
commit
5e1cbe0e5d
9 changed files with 79 additions and 56 deletions
|
@ -11,6 +11,7 @@ namespace Microsoft.PowerShell.Commands.Diagnostics.Common
|
||||||
|
|
||||||
internal static class CommonUtilities
|
internal static class CommonUtilities
|
||||||
{
|
{
|
||||||
|
#if !CORECLR
|
||||||
//
|
//
|
||||||
// StringArrayToString helper converts a string array into a comma-separated string.
|
// StringArrayToString helper converts a string array into a comma-separated string.
|
||||||
// Note this has only limited use, individual strings cannot have commas.
|
// Note this has only limited use, individual strings cannot have commas.
|
||||||
|
@ -111,13 +112,13 @@ namespace Microsoft.PowerShell.Commands.Diagnostics.Common
|
||||||
}
|
}
|
||||||
return formatError;
|
return formatError;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public static ResourceManager GetResourceManager()
|
public static ResourceManager GetResourceManager()
|
||||||
{
|
{
|
||||||
// this naming pattern is dictated by the dotnet cli
|
// this naming pattern is dictated by the dotnet cli
|
||||||
return new ResourceManager("Microsoft.PowerShell.Commands.Diagnostics.resources.GetEventResources", typeof(CommonUtilities).GetTypeInfo().Assembly);
|
return new ResourceManager("Microsoft.PowerShell.Commands.Diagnostics.resources.GetEventResources", typeof(CommonUtilities).GetTypeInfo().Assembly);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -175,7 +175,7 @@ namespace System.Management.Automation
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Singleton instance of PowerShellAssemblyLoadContext
|
/// Singleton instance of PowerShellAssemblyLoadContext
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static PowerShellAssemblyLoadContext Instance
|
internal static PowerShellAssemblyLoadContext Instance
|
||||||
{
|
{
|
||||||
get; private set;
|
get; private set;
|
||||||
}
|
}
|
||||||
|
@ -375,9 +375,21 @@ namespace System.Management.Automation
|
||||||
{
|
{
|
||||||
string tpaStrongName;
|
string tpaStrongName;
|
||||||
if (coreClrTypeCatalog.TryGetValue(namespaceQualifiedTypeName, out tpaStrongName))
|
if (coreClrTypeCatalog.TryGetValue(namespaceQualifiedTypeName, out tpaStrongName))
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
return new Assembly[] { GetTrustedPlatformAssembly(tpaStrongName) };
|
return new Assembly[] { GetTrustedPlatformAssembly(tpaStrongName) };
|
||||||
}
|
}
|
||||||
|
catch (FileNotFoundException)
|
||||||
|
{
|
||||||
|
// It's possible that the type catalog generated in OPS contains more entries than
|
||||||
|
// the one generated in windows build. This is because in OPS we have more freedom
|
||||||
|
// to control what packages to depend on, such as Json.NET.
|
||||||
|
// If we deploy the PSALC.dll generated from OPS to NanoServer, then it's possible
|
||||||
|
// that 'GetTrustedPlatformAssembly(tpaStrongName)' may fail for such entries. In
|
||||||
|
// this case, we ignore the exception and return our cached assemblies.
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we return all assemblies from the AssemblyCache
|
// Otherwise, we return all assemblies from the AssemblyCache
|
||||||
|
@ -459,6 +471,15 @@ namespace System.Management.Automation
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set the profile optimization root on the appropriate load context
|
/// Set the profile optimization root on the appropriate load context
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// When using PS ALC as a full fledged ALC in OPS, we don't enable profile optimization.
|
||||||
|
/// This is because PS assemblies will be recorded in the profile, and the next time OPS
|
||||||
|
/// starts up, the default context will load the PS assemblies pretty early to ngen them
|
||||||
|
/// in another CPU core, so our Load override won't track the loading of them, and thus
|
||||||
|
/// OPS will fail to work.
|
||||||
|
/// The root cause is that dotnet.exe put all PS assemblies in TPA list. If PS assemblies
|
||||||
|
/// are not in TPA list, then we can enable profile optimization without a problem.
|
||||||
|
/// </remarks>
|
||||||
internal void SetProfileOptimizationRootImpl(string directoryPath)
|
internal void SetProfileOptimizationRootImpl(string directoryPath)
|
||||||
{
|
{
|
||||||
if (this.useResolvingHandlerOnly)
|
if (this.useResolvingHandlerOnly)
|
||||||
|
@ -468,6 +489,15 @@ namespace System.Management.Automation
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start the profile optimization on the appropriate load context
|
/// Start the profile optimization on the appropriate load context
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// When using PS ALC as a full fledged ALC in OPS, we don't enable profile optimization.
|
||||||
|
/// This is because PS assemblies will be recorded in the profile, and the next time OPS
|
||||||
|
/// starts up, the default context will load the PS assemblies pretty early to ngen them
|
||||||
|
/// in another CPU core, so our Load override won't track the loading of them, and thus
|
||||||
|
/// OPS will fail to work.
|
||||||
|
/// The root cause is that dotnet.exe put all PS assemblies in TPA list. If PS assemblies
|
||||||
|
/// are not in TPA list, then we can enable profile optimization without a problem.
|
||||||
|
/// </remarks>
|
||||||
internal void StartProfileOptimizationImpl(string profile)
|
internal void StartProfileOptimizationImpl(string profile)
|
||||||
{
|
{
|
||||||
if (this.useResolvingHandlerOnly)
|
if (this.useResolvingHandlerOnly)
|
||||||
|
@ -620,38 +650,15 @@ namespace System.Management.Automation
|
||||||
/// </param>
|
/// </param>
|
||||||
private Assembly GetTrustedPlatformAssembly(string tpaStrongName)
|
private Assembly GetTrustedPlatformAssembly(string tpaStrongName)
|
||||||
{
|
{
|
||||||
Assembly asmLoaded;
|
// We always depend on the default context to load the TPAs that are recorded in
|
||||||
|
// the type catalog.
|
||||||
|
// - If the requested TPA is already loaded, then 'Assembly.Load' will just get
|
||||||
|
// it back from the cache of default context.
|
||||||
|
// - If the requested TPA is not loaded yet, then 'Assembly.Load' will make the
|
||||||
|
// default context to load it
|
||||||
AssemblyName assemblyName = new AssemblyName(tpaStrongName);
|
AssemblyName assemblyName = new AssemblyName(tpaStrongName);
|
||||||
|
Assembly asmLoaded = Assembly.Load(assemblyName);
|
||||||
// With the current standalone-app model of OPS, .NET Core libraries and PS assemblies are mixed together in one folder.
|
|
||||||
// So when using PSALC as a full fledged ALC in OPS, some TPAs might be loaded by our Load override. In that case, if we
|
|
||||||
// alwasy call Assembly.Load here to get a TPA, we might end up with a different Assembly instance of the the same TPA
|
|
||||||
// loaded in the default load context. We want to use the same assembly instance for type resolution in PS to avoid creating
|
|
||||||
// types and running .NET code from different assembly instances of the same DLL. Therefore, we try our cache first to see
|
|
||||||
// if the requested TPA is already loaded. If so, we use that one. If not, we load it in default context using Assembly.Load.
|
|
||||||
// Once a TPA is loaded in the default context, the same Assembly instance will always be used by custom ALC's when they attempt
|
|
||||||
// to resolve an "Assembly.Load" request for the same TPA.
|
|
||||||
//
|
|
||||||
// For in-box PS of NanoServer/IoT and the share-framework host model of OPS, we don't have the mixed libraries/assemblies
|
|
||||||
// problem, and TPAs are always resolved/loaded by the default context. In those cases, checking our cache would be unnecessary,
|
|
||||||
// but it won't cause any problems.
|
|
||||||
|
|
||||||
// Probe the assembly cache
|
|
||||||
if (TryGetAssemblyFromCache(assemblyName, out asmLoaded))
|
|
||||||
return asmLoaded;
|
return asmLoaded;
|
||||||
|
|
||||||
// Prepare to load the assembly
|
|
||||||
lock (syncObj)
|
|
||||||
{
|
|
||||||
// Probe the cache again in case it's already loaded
|
|
||||||
if (TryGetAssemblyFromCache(assemblyName, out asmLoaded))
|
|
||||||
return asmLoaded;
|
|
||||||
|
|
||||||
// The requested TPA is not loaded by PS ALC, so load it in the default load context using Assembly.Load.
|
|
||||||
// There is no need to add it to our cache. It's cached in the default context.
|
|
||||||
asmLoaded = Assembly.Load(assemblyName);
|
|
||||||
return asmLoaded;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -5907,21 +5907,32 @@ namespace System.Management.Automation
|
||||||
/// <param name="namespace">The namespace</param>
|
/// <param name="namespace">The namespace</param>
|
||||||
private static void HandleNamespace(Dictionary<string, TypeCompletionMapping> entryCache, string @namespace)
|
private static void HandleNamespace(Dictionary<string, TypeCompletionMapping> entryCache, string @namespace)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(@namespace))
|
if (string.IsNullOrEmpty(@namespace))
|
||||||
{
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dotIndex = 0;
|
||||||
|
while (dotIndex != -1)
|
||||||
|
{
|
||||||
|
dotIndex = @namespace.IndexOf('.', dotIndex + 1);
|
||||||
|
string subNamespace = dotIndex != -1
|
||||||
|
? @namespace.Substring(0, dotIndex)
|
||||||
|
: @namespace;
|
||||||
|
|
||||||
TypeCompletionMapping entry;
|
TypeCompletionMapping entry;
|
||||||
if (!entryCache.TryGetValue(@namespace, out entry))
|
if (!entryCache.TryGetValue(subNamespace, out entry))
|
||||||
{
|
{
|
||||||
entry = new TypeCompletionMapping
|
entry = new TypeCompletionMapping
|
||||||
{
|
{
|
||||||
Key = @namespace,
|
Key = subNamespace,
|
||||||
Completions = { new NamespaceCompletion { Namespace = @namespace } }
|
Completions = { new NamespaceCompletion { Namespace = subNamespace } }
|
||||||
};
|
};
|
||||||
entryCache.Add(@namespace, entry);
|
entryCache.Add(subNamespace, entry);
|
||||||
}
|
}
|
||||||
else if (!entry.Completions.OfType<NamespaceCompletion>().Any())
|
else if (!entry.Completions.OfType<NamespaceCompletion>().Any())
|
||||||
{
|
{
|
||||||
entry.Completions.Add(new NamespaceCompletion { Namespace = @namespace });
|
entry.Completions.Add(new NamespaceCompletion { Namespace = subNamespace });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1818,7 +1818,7 @@ namespace Microsoft.PowerShell.Commands
|
||||||
DirectoryInfo parent = null;
|
DirectoryInfo parent = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
parent = ClrFacade.GetParent(moduleManifestPath);
|
parent = Directory.GetParent(moduleManifestPath);
|
||||||
}
|
}
|
||||||
catch (IOException)
|
catch (IOException)
|
||||||
{
|
{
|
||||||
|
|
|
@ -241,7 +241,7 @@ namespace Microsoft.PowerShell.Commands
|
||||||
DirectoryInfo parent = null;
|
DirectoryInfo parent = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
parent = ClrFacade.GetParent(filePath);
|
parent = Directory.GetParent(filePath);
|
||||||
}
|
}
|
||||||
catch (IOException) { }
|
catch (IOException) { }
|
||||||
catch (UnauthorizedAccessException) { }
|
catch (UnauthorizedAccessException) { }
|
||||||
|
|
|
@ -1351,8 +1351,8 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent
|
||||||
<data name="CantActivateDocumentInPowerShellCore" xml:space="preserve">
|
<data name="CantActivateDocumentInPowerShellCore" xml:space="preserve">
|
||||||
<value>Cannot run a document in PowerShell Core: {0}.</value>
|
<value>Cannot run a document in PowerShell Core: {0}.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InvalidAssemblyLoadContextInUse" xml:space="preserve">
|
<data name="LoadContextNotInitialized" xml:space="preserve">
|
||||||
<value>The default AssemblyLoadContext in use is invalid. The default AssemblyLoadContext for PowerShell Core should be of type 'PowerShellAssemblyLoader'.</value>
|
<value>'PowerShellAssemblyLoadContext' is not initialized.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MultipleTypeConstraintsOnMethodParam" xml:space="preserve">
|
<data name="MultipleTypeConstraintsOnMethodParam" xml:space="preserve">
|
||||||
<value>Multiple type constraints are not allowed on a method parameter.</value>
|
<value>Multiple type constraints are not allowed on a method parameter.</value>
|
||||||
|
|
|
@ -344,6 +344,10 @@ namespace System.Management.Automation
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
if (PowerShellAssemblyLoadContext.Instance == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(ParserStrings.LoadContextNotInitialized);
|
||||||
|
}
|
||||||
return PowerShellAssemblyLoadContext.Instance;
|
return PowerShellAssemblyLoadContext.Instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -512,15 +516,6 @@ namespace System.Management.Automation
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Facade for Directory.GetParent(string)
|
|
||||||
/// </summary>
|
|
||||||
internal static DirectoryInfo GetParent(string path)
|
|
||||||
{
|
|
||||||
// Porting note: this is in recent CoreCLR
|
|
||||||
return Directory.GetParent(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Facade for ManagementDateTimeConverter.ToDmtfDateTime(DateTime)
|
/// Facade for ManagementDateTimeConverter.ToDmtfDateTime(DateTime)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -1457,11 +1457,11 @@ namespace NativeMsh
|
||||||
nProps++;
|
nProps++;
|
||||||
|
|
||||||
props[nProps] = L"APP_PATHS";
|
props[nProps] = L"APP_PATHS";
|
||||||
vals[nProps] = hostEnvironment.GetHostDirectoryPath();
|
vals[nProps] = L"";
|
||||||
nProps++;
|
nProps++;
|
||||||
|
|
||||||
props[nProps] = L"APP_NI_PATHS";
|
props[nProps] = L"APP_NI_PATHS";
|
||||||
vals[nProps] = hostEnvironment.GetHostDirectoryPath();
|
vals[nProps] = L"";
|
||||||
nProps++;
|
nProps++;
|
||||||
|
|
||||||
// Create the customized AppDomainManager out of the SandboxHelper class
|
// Create the customized AppDomainManager out of the SandboxHelper class
|
||||||
|
|
9
test/powershell/Host/TabCompletion/BugFix.Tests.ps1
Normal file
9
test/powershell/Host/TabCompletion/BugFix.Tests.ps1
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Describe "Issue#682 - [system.manage<tab>] should work" {
|
||||||
|
|
||||||
|
It "[system.manage<tab>" {
|
||||||
|
$result = TabExpansion2 -inputScript "[system.manage" -cursorColumn "[system.manage".Length
|
||||||
|
$result | Should Not BeNullOrEmpty
|
||||||
|
$result.CompletionMatches.Count | Should Be 1
|
||||||
|
$result.CompletionMatches[0].CompletionText | Should Be "System.Management"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue