Merge pull request #1328 from PowerShell/dongbo/type-completion

Fix issue #682 - [system.manage<Tab>] doesn't work
This commit is contained in:
Jason Shirk 2016-07-08 16:16:32 -07:00 committed by GitHub
commit 5e1cbe0e5d
9 changed files with 79 additions and 56 deletions

View file

@ -11,6 +11,7 @@ namespace Microsoft.PowerShell.Commands.Diagnostics.Common
internal static class CommonUtilities
{
#if !CORECLR
//
// StringArrayToString helper converts a string array into a comma-separated string.
// Note this has only limited use, individual strings cannot have commas.
@ -111,13 +112,13 @@ namespace Microsoft.PowerShell.Commands.Diagnostics.Common
}
return formatError;
}
#endif
public static ResourceManager GetResourceManager()
{
// this naming pattern is dictated by the dotnet cli
return new ResourceManager("Microsoft.PowerShell.Commands.Diagnostics.resources.GetEventResources", typeof(CommonUtilities).GetTypeInfo().Assembly);
}
}
}

View file

@ -175,7 +175,7 @@ namespace System.Management.Automation
/// <summary>
/// Singleton instance of PowerShellAssemblyLoadContext
/// </summary>
public static PowerShellAssemblyLoadContext Instance
internal static PowerShellAssemblyLoadContext Instance
{
get; private set;
}
@ -376,7 +376,19 @@ namespace System.Management.Automation
string tpaStrongName;
if (coreClrTypeCatalog.TryGetValue(namespaceQualifiedTypeName, out tpaStrongName))
{
return new Assembly[] { GetTrustedPlatformAssembly(tpaStrongName) };
try
{
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.
}
}
}
@ -459,6 +471,15 @@ namespace System.Management.Automation
/// <summary>
/// Set the profile optimization root on the appropriate load context
/// </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)
{
if (this.useResolvingHandlerOnly)
@ -468,6 +489,15 @@ namespace System.Management.Automation
/// <summary>
/// Start the profile optimization on the appropriate load context
/// </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)
{
if (this.useResolvingHandlerOnly)
@ -620,38 +650,15 @@ namespace System.Management.Automation
/// </param>
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);
// 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;
// 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;
}
Assembly asmLoaded = Assembly.Load(assemblyName);
return asmLoaded;
}
/// <summary>

View file

@ -5907,21 +5907,32 @@ namespace System.Management.Automation
/// <param name="namespace">The namespace</param>
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;
if (!entryCache.TryGetValue(@namespace, out entry))
if (!entryCache.TryGetValue(subNamespace, out entry))
{
entry = new TypeCompletionMapping
{
Key = @namespace,
Completions = { new NamespaceCompletion { Namespace = @namespace } }
Key = subNamespace,
Completions = { new NamespaceCompletion { Namespace = subNamespace } }
};
entryCache.Add(@namespace, entry);
entryCache.Add(subNamespace, entry);
}
else if (!entry.Completions.OfType<NamespaceCompletion>().Any())
{
entry.Completions.Add(new NamespaceCompletion { Namespace = @namespace });
entry.Completions.Add(new NamespaceCompletion { Namespace = subNamespace });
}
}
}

View file

@ -1818,7 +1818,7 @@ namespace Microsoft.PowerShell.Commands
DirectoryInfo parent = null;
try
{
parent = ClrFacade.GetParent(moduleManifestPath);
parent = Directory.GetParent(moduleManifestPath);
}
catch (IOException)
{

View file

@ -241,7 +241,7 @@ namespace Microsoft.PowerShell.Commands
DirectoryInfo parent = null;
try
{
parent = ClrFacade.GetParent(filePath);
parent = Directory.GetParent(filePath);
}
catch (IOException) { }
catch (UnauthorizedAccessException) { }

View file

@ -1351,8 +1351,8 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent
<data name="CantActivateDocumentInPowerShellCore" xml:space="preserve">
<value>Cannot run a document in PowerShell Core: {0}.</value>
</data>
<data name="InvalidAssemblyLoadContextInUse" xml:space="preserve">
<value>The default AssemblyLoadContext in use is invalid. The default AssemblyLoadContext for PowerShell Core should be of type 'PowerShellAssemblyLoader'.</value>
<data name="LoadContextNotInitialized" xml:space="preserve">
<value>'PowerShellAssemblyLoadContext' is not initialized.</value>
</data>
<data name="MultipleTypeConstraintsOnMethodParam" xml:space="preserve">
<value>Multiple type constraints are not allowed on a method parameter.</value>

View file

@ -344,6 +344,10 @@ namespace System.Management.Automation
{
get
{
if (PowerShellAssemblyLoadContext.Instance == null)
{
throw new InvalidOperationException(ParserStrings.LoadContextNotInitialized);
}
return PowerShellAssemblyLoadContext.Instance;
}
}
@ -512,15 +516,6 @@ namespace System.Management.Automation
#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>
/// Facade for ManagementDateTimeConverter.ToDmtfDateTime(DateTime)
/// </summary>

View file

@ -1454,14 +1454,14 @@ namespace NativeMsh
props[nProps] = L"TRUSTED_PLATFORM_ASSEMBLIES";
std::wstring tempStr = assemblyList.str();
vals[nProps] = tempStr.c_str();
nProps++;
nProps++;
props[nProps] = L"APP_PATHS";
vals[nProps] = hostEnvironment.GetHostDirectoryPath();
vals[nProps] = L"";
nProps++;
props[nProps] = L"APP_NI_PATHS";
vals[nProps] = hostEnvironment.GetHostDirectoryPath();
vals[nProps] = L"";
nProps++;
// Create the customized AppDomainManager out of the SandboxHelper class

View 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"
}
}